gplugin/gplugin

Release prepping

2020-09-05, Gary Kramlich
39fda4aa8bb2
Release prepping
  • +165 -0
    .clang-format
  • +5 -0
    .reviewboardrc
  • +50 -2
    ChangeLog
  • +0 -19
    HACKING
  • +7 -26
    HACKING.OSX
  • +100 -0
    HACKING.md
  • +4 -4
    HISTORY.md
  • +6 -4
    INSTALL.md
  • +8 -12
    README.md
  • +47 -44
    convey.yml
  • +16 -14
    gplugin-gtk-viewer/gplugin-gtk-viewer-window.c
  • +7 -3
    gplugin-gtk-viewer/gplugin-gtk-viewer-window.h
  • +51 -33
    gplugin-gtk-viewer/gplugin-gtk-viewer.c
  • +1 -1
    gplugin-gtk-viewer/meson.build
  • +47 -5
    gplugin-gtk/data/plugin-info.ui
  • +230 -75
    gplugin-gtk/gplugin-gtk-plugin-info.c
  • +12 -5
    gplugin-gtk/gplugin-gtk-plugin-info.h
  • +134 -63
    gplugin-gtk/gplugin-gtk-store.c
  • +11 -5
    gplugin-gtk/gplugin-gtk-store.h
  • +78 -46
    gplugin-gtk/gplugin-gtk-view.c
  • +12 -5
    gplugin-gtk/gplugin-gtk-view.h
  • +10 -11
    gplugin-gtk/gplugin-gtk.h.in
  • +1 -1
    gplugin-gtk/gplugin-gtk.xml.in
  • +8 -8
    gplugin-gtk/meson.build
  • +11 -14
    gplugin-gtk/reference/gplugin-gtk-docs.xml
  • +2 -1
    gplugin-gtk/reference/meson.build
  • +368 -0
    gplugin-query/gplugin-query.c
  • +17 -0
    gplugin-query/meson.build
  • +0 -1
    gplugin-version.sh.in
  • +10 -10
    gplugin/gplugin-core.c
  • +2 -3
    gplugin/gplugin-core.h
  • +34 -20
    gplugin/gplugin-file-tree.c
  • +1 -2
    gplugin/gplugin-file-tree.h
  • +82 -37
    gplugin/gplugin-loader-tests.c
  • +5 -3
    gplugin/gplugin-loader-tests.h
  • +73 -54
    gplugin/gplugin-loader.c
  • +29 -13
    gplugin/gplugin-loader.h
  • +1012 -504
    gplugin/gplugin-manager.c
  • +19 -6
    gplugin/gplugin-manager.h
  • +113 -85
    gplugin/gplugin-native-loader.c
  • +7 -3
    gplugin/gplugin-native-loader.h
  • +62 -45
    gplugin/gplugin-native-plugin.c
  • +7 -3
    gplugin/gplugin-native-plugin.h
  • +6 -4
    gplugin/gplugin-native-private.h
  • +10 -10
    gplugin/gplugin-native.h.in
  • +26 -19
    gplugin/gplugin-options.c
  • +1 -3
    gplugin/gplugin-options.h
  • +290 -322
    gplugin/gplugin-plugin-info.c
  • +19 -15
    gplugin/gplugin-plugin-info.h
  • +104 -60
    gplugin/gplugin-plugin.c
  • +11 -4
    gplugin/gplugin-plugin.h
  • +6 -6
    gplugin/gplugin-private.c
  • +12 -9
    gplugin/gplugin-private.h
  • +0 -350
    gplugin/gplugin-query.c
  • +126 -65
    gplugin/gplugin-version.c
  • +12 -13
    gplugin/gplugin-version.h.in
  • +10 -12
    gplugin/gplugin.h.in
  • +9 -29
    gplugin/meson.build
  • +4 -5
    gplugin/reference/embedding.xml
  • +70 -0
    gplugin/reference/genie.xml
  • +17 -22
    gplugin/reference/gplugin-docs.xml
  • +26 -25
    gplugin/reference/lua.xml
  • +6 -0
    gplugin/reference/meson.build
  • +9 -10
    gplugin/reference/native-plugins.xml
  • +60 -0
    gplugin/reference/perl5.xml
  • +24 -24
    gplugin/reference/python.xml
  • +36 -35
    gplugin/reference/vala.xml
  • +2 -0
    gplugin/share/meson.build
  • +40 -0
    gplugin/share/valgrind/gplugin.supp
  • +2 -0
    gplugin/share/valgrind/meson.build
  • +9 -7
    gplugin/tests/bad-plugins/query-error.c
  • +12 -9
    gplugin/tests/bind-local/bind-local.c
  • +14 -8
    gplugin/tests/dynamic-type/dynamic-test.h
  • +15 -12
    gplugin/tests/dynamic-type/dynamic-type-provider.c
  • +17 -15
    gplugin/tests/dynamic-type/dynamic-type-user.c
  • +12 -9
    gplugin/tests/id-collision/id-collision1.c
  • +12 -9
    gplugin/tests/id-collision/id-collision2.c
  • +11 -8
    gplugin/tests/load-on-query-fail/load-on-query-fail.c
  • +12 -9
    gplugin/tests/load-on-query-pass/load-on-query-pass.c
  • +19 -4
    gplugin/tests/meson.build
  • +35 -0
    gplugin/tests/newest-version/meson.build
  • +47 -0
    gplugin/tests/newest-version/multiple-semantic-1.0.0.c
  • +47 -0
    gplugin/tests/newest-version/multiple-semantic-1.1.0.c
  • +46 -0
    gplugin/tests/newest-version/no-version-and-semantic-no-version.c
  • +47 -0
    gplugin/tests/newest-version/no-version-and-semantic-semantic.c
  • +47 -0
    gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c
  • +47 -0
    gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c
  • +46 -0
    gplugin/tests/newest-version/solo-no-version.c
  • +47 -0
    gplugin/tests/newest-version/solo-non-semantic.c
  • +13 -13
    gplugin/tests/plugins/basic-plugin.c
  • +13 -13
    gplugin/tests/plugins/broken-dependent-plugin.c
  • +13 -14
    gplugin/tests/plugins/dependent-plugin.c
  • +10 -9
    gplugin/tests/plugins/load-exception.c
  • +9 -8
    gplugin/tests/plugins/load-failed.c
  • +9 -8
    gplugin/tests/plugins/unload-failed.c
  • +8 -8
    gplugin/tests/test-bind-local.c
  • +60 -40
    gplugin/tests/test-core.c
  • +7 -8
    gplugin/tests/test-dynamic-type.c
  • +142 -0
    gplugin/tests/test-find-plugins.c
  • +8 -9
    gplugin/tests/test-id-collision.c
  • +25 -16
    gplugin/tests/test-load-on-query.c
  • +188 -0
    gplugin/tests/test-loader-registration.c
  • +19 -14
    gplugin/tests/test-native-loader.c
  • +117 -0
    gplugin/tests/test-newest-version.c
  • +70 -46
    gplugin/tests/test-option-group.c
  • +225 -225
    gplugin/tests/test-plugin-info.c
  • +98 -30
    gplugin/tests/test-plugin-manager-paths.c
  • +221 -107
    gplugin/tests/test-signals.c
  • +10 -9
    gplugin/tests/test-unresolved-symbol.c
  • +116 -144
    gplugin/tests/test-version-compare.c
  • +38 -47
    gplugin/tests/test-versioned-dependencies.c
  • +9 -7
    gplugin/tests/unresolved-symbol/unresolved-symbol.c
  • +12 -9
    gplugin/tests/versioned-dependencies/bar.c
  • +12 -9
    gplugin/tests/versioned-dependencies/baz.c
  • +12 -9
    gplugin/tests/versioned-dependencies/exact1.c
  • +12 -9
    gplugin/tests/versioned-dependencies/exact2.c
  • +12 -9
    gplugin/tests/versioned-dependencies/fez.c
  • +12 -9
    gplugin/tests/versioned-dependencies/greater-equal.c
  • +12 -9
    gplugin/tests/versioned-dependencies/greater.c
  • +12 -9
    gplugin/tests/versioned-dependencies/less-equal.c
  • +12 -9
    gplugin/tests/versioned-dependencies/less.c
  • +10 -12
    gplugin/tests/versioned-dependencies/no-version.c
  • +17 -12
    gplugin/tests/versioned-dependencies/super-dependent.c
  • +20 -23
    lua/gplugin-lua-core.c
  • +67 -73
    lua/gplugin-lua-loader.c
  • +7 -3
    lua/gplugin-lua-loader.h
  • +43 -19
    lua/gplugin-lua-plugin.c
  • +7 -3
    lua/gplugin-lua-plugin.h
  • +15 -10
    lua/gplugin-lua-test-lgi.c
  • +24 -18
    lua/meson.build
  • +0 -44
    lua/tests/lua-plugins/basic.lua
  • +0 -35
    lua/tests/lua-plugins/dependent.lua
  • +0 -34
    lua/tests/lua-plugins/load-exception.lua
  • +0 -34
    lua/tests/lua-plugins/load-failed.lua
  • +0 -34
    lua/tests/lua-plugins/unload-failed.lua
  • +2 -18
    lua/tests/meson.build
  • +0 -39
    lua/tests/moonscript-plugins/basic.moon
  • +0 -30
    lua/tests/moonscript-plugins/dependent.moon
  • +0 -30
    lua/tests/moonscript-plugins/load-exception.moon
  • +0 -30
    lua/tests/moonscript-plugins/load-failed.moon
  • +0 -30
    lua/tests/moonscript-plugins/unload-failed.moon
  • +42 -0
    lua/tests/plugins/basic.lua
  • +34 -0
    lua/tests/plugins/dependent.lua
  • +33 -0
    lua/tests/plugins/load-exception.lua
  • +33 -0
    lua/tests/plugins/load-failed.lua
  • +33 -0
    lua/tests/plugins/unload-failed.lua
  • +4 -4
    lua/tests/test-lua-loader.c
  • +0 -31
    lua/tests/test-lua-moon-loader.c
  • +8 -20
    meson.build
  • +16 -25
    meson_options.txt
  • +1 -1
    packaging/debian/compat
  • +29 -4
    packaging/debian/control
  • +0 -2
    packaging/debian/gir1.2-gplugin-0.0.install
  • +1 -0
    packaging/debian/gir1.2-gplugin-1.0.install
  • +1 -0
    packaging/debian/gir1.2-gplugin-gtk-1.0.install
  • +2 -1
    packaging/debian/libgplugin-dev.install
  • +2 -1
    packaging/debian/libgplugin-gtk-dev.install
  • +1 -0
    packaging/debian/libgplugin-perl5.install
  • +0 -1
    packaging/debian/libgplugin-python.install
  • +1 -0
    packaging/debian/libgplugin-python3.install
  • +53 -61
    packaging/gplugin.spec.in
  • +3 -2
    packaging/mingw-cross/PKGBUILD
  • +0 -63
    perl/gplugin-perl-core.c
  • +0 -138
    perl/gplugin-perl-loader.c
  • +0 -34
    perl/gplugin-perl-loader.h
  • +0 -205
    perl/gplugin-perl-plugin.c
  • +0 -39
    perl/gplugin-perl-plugin.h
  • +0 -37
    perl/meson.build
  • +0 -49
    perl/tests/plugins/basic.pl
  • +73 -0
    perl5/gplugin-perl5-core.c
  • +332 -0
    perl5/gplugin-perl5-loader.c
  • +38 -0
    perl5/gplugin-perl5-loader.h
  • +217 -0
    perl5/gplugin-perl5-plugin.c
  • +57 -0
    perl5/gplugin-perl5-plugin.h
  • +66 -0
    perl5/meson.build
  • +12 -0
    perl5/tests/meson.build
  • +43 -0
    perl5/tests/plugins/basic.pl
  • +35 -0
    perl5/tests/plugins/dependent.pl
  • +36 -0
    perl5/tests/plugins/load-exception.pl
  • +34 -0
    perl5/tests/plugins/load-failed.pl
  • +34 -0
    perl5/tests/plugins/unload-failed.pl
  • +31 -0
    perl5/tests/test-perl5-loader.c
  • +0 -53
    plugins/gplugin-license-check.c
  • +0 -6
    plugins/meson.build
  • +9 -11
    po/POTFILES
  • +0 -76
    python/gplugin-python-core.c
  • +0 -394
    python/gplugin-python-loader.c
  • +0 -34
    python/gplugin-python-loader.h
  • +0 -277
    python/gplugin-python-plugin.c
  • +0 -34
    python/gplugin-python-plugin.h
  • +0 -66
    python/gplugin-python-test-pygobject.c
  • +0 -112
    python/gplugin-python-utils.c
  • +0 -36
    python/gplugin-python-utils.h
  • +74 -0
    python/gplugin-python3-core.c
  • +401 -0
    python/gplugin-python3-loader.c
  • +38 -0
    python/gplugin-python3-loader.h
  • +310 -0
    python/gplugin-python3-plugin.c
  • +38 -0
    python/gplugin-python3-plugin.h
  • +64 -0
    python/gplugin-python3-test-pygobject.c
  • +117 -0
    python/gplugin-python3-utils.c
  • +33 -0
    python/gplugin-python3-utils.h
  • +33 -33
    python/meson.build
  • +9 -13
    python/tests/meson.build
  • +8 -4
    python/tests/plugins/basic.py
  • +8 -4
    python/tests/plugins/dependent.py
  • +7 -5
    python/tests/plugins/load-exception.py
  • +7 -5
    python/tests/plugins/load-failed.py
  • +7 -5
    python/tests/plugins/unload-failed.py
  • +0 -34
    python/tests/test-python-loader.c
  • +0 -76
    python/tests/test-python-utils.c
  • +37 -0
    python/tests/test-python3-loader.c
  • +87 -0
    python/tests/test-python3-utils.c
  • +26 -33
    tcc/gplugin-tcc-core.c
  • +85 -51
    tcc/gplugin-tcc-loader.c
  • +16 -12
    tcc/gplugin-tcc-loader.h
  • +43 -32
    tcc/gplugin-tcc-plugin.c
  • +20 -14
    tcc/gplugin-tcc-plugin.h
  • +1 -1
    tcc/meson.build
  • +1 -1
    tcc/tests/meson.build
  • +22 -22
    tcc/tests/plugins/basic-plugin.c
  • +22 -23
    tcc/tests/plugins/dependent.c
  • +19 -18
    tcc/tests/plugins/load-exception.c
  • +18 -17
    tcc/tests/plugins/load-failed.c
  • +18 -17
    tcc/tests/plugins/unload-failed.c
  • +13 -13
    tcc/tests/test-tcc-loader.c
  • +3 -3
    vala/meson.build
  • +49 -0
    vala/tests/genie-plugins/basic.gs
  • +41 -0
    vala/tests/genie-plugins/dependent.gs
  • +38 -0
    vala/tests/genie-plugins/load-exception.gs
  • +38 -0
    vala/tests/genie-plugins/load-failed.gs
  • +17 -0
    vala/tests/genie-plugins/meson.build
  • +38 -0
    vala/tests/genie-plugins/unload-failed.gs
  • +10 -0
    vala/tests/meson.build
  • +1 -1
    vala/tests/plugins/basic.vala
  • +1 -1
    vala/tests/plugins/dependent.vala
  • +1 -1
    vala/tests/plugins/load-exception.vala
  • +1 -1
    vala/tests/plugins/load-failed.vala
  • +1 -1
    vala/tests/plugins/unload-failed.vala
  • +34 -0
    vala/tests/test-genie-loading.c
  • +4 -4
    vala/tests/test-vala-loading.c
  • --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/.clang-format Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,165 @@
    +---
    +Language: Cpp
    +AccessModifierOffset: -2
    +AlignAfterOpenBracket: AlwaysBreak
    +AlignConsecutiveMacros: false
    +AlignConsecutiveAssignments: false
    +AlignConsecutiveDeclarations: false
    +AlignEscapedNewlines: DontAlign
    +AlignOperands: true
    +AlignTrailingComments: true
    +AllowAllArgumentsOnNextLine: false
    +AllowAllConstructorInitializersOnNextLine: true
    +AllowAllParametersOfDeclarationOnNextLine: false
    +AllowShortBlocksOnASingleLine: false
    +AllowShortCaseLabelsOnASingleLine: false
    +AllowShortFunctionsOnASingleLine: None
    +AllowShortLambdasOnASingleLine: All
    +AllowShortIfStatementsOnASingleLine: Never
    +AllowShortLoopsOnASingleLine: false
    +AlwaysBreakAfterDefinitionReturnType: TopLevel
    +AlwaysBreakAfterReturnType: TopLevelDefinitions
    +AlwaysBreakBeforeMultilineStrings: false
    +AlwaysBreakTemplateDeclarations: Yes
    +BinPackArguments: false
    +BinPackParameters: false
    +BraceWrapping:
    + AfterCaseLabel: false
    + AfterClass: false
    + AfterControlStatement: false
    + AfterEnum: false
    + AfterFunction: true
    + AfterNamespace: false
    + AfterObjCDeclaration: false
    + AfterStruct: false
    + AfterUnion: false
    + AfterExternBlock: false
    + BeforeCatch: false
    + BeforeElse: false
    + IndentBraces: false
    + SplitEmptyFunction: true
    + SplitEmptyRecord: true
    + SplitEmptyNamespace: true
    +BreakBeforeBinaryOperators: None
    +BreakBeforeBraces: Custom
    +BreakBeforeInheritanceComma: false
    +BreakInheritanceList: BeforeColon
    +BreakBeforeTernaryOperators: true
    +BreakConstructorInitializersBeforeComma: false
    +BreakConstructorInitializers: BeforeColon
    +BreakAfterJavaFieldAnnotations: false
    +BreakStringLiterals: true
    +ColumnLimit: 80
    +CommentPragmas: '^ IWYU pragma:'
    +CompactNamespaces: false
    +ConstructorInitializerAllOnOneLineOrOnePerLine: false
    +ConstructorInitializerIndentWidth: 2
    +ContinuationIndentWidth: 4
    +Cpp11BracedListStyle: true
    +DerivePointerAlignment: false
    +DisableFormat: false
    +ExperimentalAutoDetectBinPacking: false
    +FixNamespaceComments: false
    +ForEachMacros:
    + - foreach
    + - Q_FOREACH
    + - BOOST_FOREACH
    +# With clang-format-11, change:
    +IncludeBlocks: Preserve
    +#IncludeBlocks: Regroup
    +IncludeCategories:
    + # C system headers.
    + - Regex: '^[<"](aio|arpa/inet|assert|complex|cpio|ctype|curses|dirent|dlfcn|errno|fcntl|fenv|float|fmtmsg|fnmatch|ftw|glob|grp|iconv|inttypes|iso646|langinfo|libgen|limits|locale|math|monetary|mqueue|ndbm|netdb|net/if|netinet/in|netinet/tcp|nl_types|poll|pthread|pwd|regex|sched|search|semaphore|setjmp|signal|spawn|stdalign|stdarg|stdatomic|stdbool|stddef|stdint|stdio|stdlib|stdnoreturn|string|strings|stropts|sys/ipc|syslog|sys/mman|sys/msg|sys/resource|sys/select|sys/sem|sys/shm|sys/socket|sys/stat|sys/statvfs|sys/time|sys/times|sys/types|sys/uio|sys/un|sys/utsname|sys/wait|tar|term|termios|tgmath|threads|time|trace|uchar|ulimit|uncntrl|unistd|utime|utmpx|wchar|wctype|wordexp)\.h[">]$'
    + Priority: 20
    + # glib headers
    + - Regex: '^<glib\.h>'
    + Priority: 30
    + - Regex: '^<(glib-|gmodule)'
    + # With clang-format-11, change:
    + Priority: 31
    + #Priority: 30
    + #SortPriority: 31
    + - Regex: '^<(gio/|glib/|gobject/)'
    + # With clang-format-11, change:
    + Priority: 32
    + #Priority: 30
    + #SortPriority: 32
    + # GTK headers
    + - Regex: '^<gtk/'
    + Priority: 40
    + # gplugin headers
    + - Regex: '^<gplugin\.h>'
    + Priority: 50
    + - Regex: '^<gplugin-[^/]+\.h>'
    + # With clang-format-11, change:
    + Priority: 51
    + #Priority: 50
    + #SortPriority: 51
    + - Regex: '^<gplugin/'
    + # With clang-format-11, change:
    + Priority: 52
    + #Priority: 50
    + #SortPriority: 52
    + - Regex: '^<gplugin-gtk/'
    + # With clang-format-11, change:
    + Priority: 53
    + #Priority: 50
    + #SortPriority: 53
    + # Other libraries' headers
    + - Regex: '^<'
    + Priority: 60
    + # Our (internal) headers
    + - Regex: '^"'
    + Priority: 70
    +IncludeIsMainRegex: '(Test)?$'
    +IndentCaseLabels: true
    +IndentPPDirectives: None
    +IndentWidth: 4
    +IndentWrappedFunctionNames: false
    +JavaScriptQuotes: Leave
    +JavaScriptWrapImports: true
    +KeepEmptyLinesAtTheStartOfBlocks: true
    +MacroBlockBegin: '^G_STMT_START$'
    +MacroBlockEnd: '^G_STMT_END$'
    +MaxEmptyLinesToKeep: 1
    +NamespaceIndentation: None
    +ObjCBinPackProtocolList: Auto
    +ObjCBlockIndentWidth: 2
    +ObjCSpaceAfterProperty: false
    +ObjCSpaceBeforeProtocolList: true
    +PenaltyBreakAssignment: 2
    +PenaltyBreakBeforeFirstCallParameter: 19
    +PenaltyBreakComment: 300
    +PenaltyBreakFirstLessLess: 120
    +PenaltyBreakString: 1000
    +PenaltyBreakTemplateDeclaration: 10
    +PenaltyExcessCharacter: 1000000
    +PenaltyReturnTypeOnItsOwnLine: 200
    +PointerAlignment: Right
    +ReflowComments: true
    +SortIncludes: true
    +SortUsingDeclarations: true
    +SpaceAfterCStyleCast: false
    +SpaceAfterLogicalNot: false
    +SpaceAfterTemplateKeyword: false
    +SpaceBeforeAssignmentOperators: true
    +SpaceBeforeCpp11BracedList: false
    +SpaceBeforeCtorInitializerColon: true
    +SpaceBeforeInheritanceColon: true
    +SpaceBeforeParens: Never
    +SpaceBeforeRangeBasedForLoopColon: true
    +SpaceInEmptyParentheses: false
    +SpacesBeforeTrailingComments: 1
    +SpacesInAngles: false
    +SpacesInContainerLiterals: false
    +SpacesInCStyleCastParentheses: false
    +SpacesInParentheses: false
    +SpacesInSquareBrackets: false
    +Standard: Cpp11
    +StatementMacros:
    + - Q_UNUSED
    + - QT_REQUIRE_VERSION
    +TabWidth: 4
    +UseTab: ForContinuationAndIndentation
    +...
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/.reviewboardrc Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,5 @@
    +REVIEWBOARD_URL = "https://reviews.imfreedom.org/"
    +REPOSITORY = "gplugin/gplugin"
    +REPOSITORY_TYPE = "mercurial"
    +LAND_DEST_BRANCH = "develop"
    +LAND_PUSH = False
    --- a/ChangeLog Thu Nov 07 23:09:55 2019 -0600
    +++ b/ChangeLog Sat Sep 05 20:05:36 2020 -0500
    @@ -1,8 +1,56 @@
    +0.30.0:
    + General
    + * Changed the website to the repository on keep.imfreedom.org.
    + (Gary Kramlich)
    + * Removed an unnecessary meson version check. (Gary Kramlich)
    + * Bumped the required meson version to 0.50.0. (Gary Kramlich)
    + * Removed gplugin-version.sh.in as it was used in the cmake build system.
    + (Gary Kramlich)
    + * Fixed licenses throughout the codebase. (Richard Laager)
    +
    + GPlugin
    + * Removed GPluginVersionCompareFunc and the GPluginPluginInfo::version-func
    + property as they aren't necessary with semantic versioning. (Gary Kramlich)
    + * Added new plugin state UNLOAD_FAILED. When a plugin fails to unload, it
    + now goes to UNLOAD_FAILED instead of LOADED. (Gary Kramlich)
    + * Added gplugin_manager_foreach and GPluginManagerForeachFunc to make it
    + easier to operate on all plugins. (Gary Kramlich)
    + * Added GPluginManager::unload-plugin-failed signal that gets emitted when
    + a plugin fails to unload.
    + * Renamed the GPluginManager::load-failed signal to
    + GPluginManager::load-plugin-failed and added the error, if any, that the
    + plugin returned. (Gary Kramlich)
    + * Added gplugin_manager_find_plugin_with_newest_version. (Gary Kramlich)
    + * Added an "error" property to GPluginPlugin with a getter. (Gary Kramlich)
    + * Removed the error parameter from the "plugin-load-failed" and
    + "plugin-unload-failed" signals. Call gplugin_plugin_get_error instead.
    + (Gary Kramlich)
    +
    + GPluginGtk
    + * Updated the GPluginGtkView widget to disable entries if there are in the
    + GPLUGIN_PLUGIN_STATE_LOAD_FAILED state. (Gary Kramlich)
    + * Updated the GPluginGtkPluginInfo widget to display the "error" property if
    + it is set, or "(none)". (Gary Kramlich)
    +
    + Lua Loader
    + * Removed the moonscript support from the Lua loader. (Gary Kramlich)
    + * Add support for Lua 5.3. (Richard Laager)
    +
    + Perl Loader
    + * After years of on again, off again work, the Perl loader finally works!
    + (Gary Kramlich, Richard Laager)
    +
    + Python Loader
    + * Bumped the Python 3 dependency to 3.5. (Gary Kramlich)
    + * Replaced mbstowcs with Py_DecodeLocale. (Gary Kramlich)
    + * Renamed the python loader from gplugin-python to gplugin-python3.
    + (Gary Kramlich)
    +
    0.29.0: 2019/11/07
    * Synchronize GPluginGtkStore with the plugin manager, make the enabled
    toggle button work in GPluginGtkView, and allow developers to decide
    whether or not to show column headers in GPlugingGtkView. (PR #37)
    - (Gary Kramlich)
    + (Gary Kramlich)
    * Added error messages when the unload method of a plugin loader is called.
    (PR #38) (Gary Kramlich)
    @@ -151,7 +199,7 @@
    * Moved the native loader to the loader-testing static library
    * Overhauled the native loader tests
    * Fixed a bug where in certain conditions a load-on-query plugin that failed
    - to load, would get it's info tracked twice.
    + to load, would get its info tracked twice.
    0.0.11: 2013/12/02
    * Added gplugin_get_option_group which implements options for adding paths
    --- a/HACKING Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,19 +0,0 @@
    -GPlugin uses hgflow[1] for development. All development takes place on the
    -develop branch which leaves default to be a stable release clone. All long
    -term feature/bug fix work should take place on a feature branch that when
    -closed, gets merged to develop.
    -
    -Releases are forked from develop and create a new branch for any testing/bug
    -fixes. When a release is deemed done, it's merged back into develop and into
    -default. hgflow takes care of all of this for us.
    -
    -To add a feature please fork https://bitbucket.org/gplugin/gplugin. Then clone
    -your fork. Once you have your fork cloned and hgflow setup and installed, type
    -
    - hg flow feature start $NAME_OF_YOUR_FEATURE$
    -
    -This will create a new branch for you to do your work on. When you submit a
    -pull request, make sure it's this branch that you're requesting I pull.
    -
    -[1] https://bitbucket.org/yujiewu/hgflow
    -
    --- a/HACKING.OSX Thu Nov 07 23:09:55 2019 -0600
    +++ b/HACKING.OSX Sat Sep 05 20:05:36 2020 -0500
    @@ -4,35 +4,16 @@
    support fink or macports, please let me know and I will merge your pull
    request.
    -Both the Lua and Python loaders are currently broken as we can't build the
    -gobject-introspection targets. Currently the only way to build is with:
    +Currently there is no known way to build the Perl loader on MacOS. So you
    +**MUST** run meson with `-Dperl5=false`.
    -```
    -export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$(brew --prefix libffi)/lib/pkgconfig"
    -meson -Dlua=false -Dpython=false -Dgobject-introspection=false build
    -cd build
    -ninja
    -```
    +## Lua
    -Lua
    -===
    -
    -For the Lua loader to work, you need to install lgi from luarocks either
    +For the Lua loader to work, you need to install `lgi` from luarocks either
    systemwide or to a virtual environment created by LuaDist, vert, or something
    -along those lines.
    -
    -When you're building lgi against homebrew you'll have to set the
    -PKG_CONFIG_PATH environment variable since homebrew does not install libffi
    -systemwide. This is easily done via:
    -
    - export PKG_CONFIG_PATH=$(brew --prefix libffi)/lib/pkgconfig
    +along those lines, then you just need to run `luarocks install lgi`.
    -and then running "luarocks install lgi"
    -
    -PYTHON
    -======
    +## Python
    -If you're using homebrew, you need to install pygobject3 with
    +If you're using homebrew, you need to install `pygobject3`.
    - brew install pygobject3 --with-python3
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/HACKING.md Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,100 @@
    +## clang-format
    +
    +GPlugin uses [clang-format][1] to automatically format code. Version 9 (or
    +higher) is required. Earlier versions will fail on the options in the
    +`.clang-format` file.
    +
    +You can use clang-format in one or more of the following ways:
    +* Integrate it into your editor. The [clang-format page][1] has examples.
    +* Use a Mercurial [hook script][2].
    +* Run `ninja clang-format` from your build directory.
    +* Worst case, run `clang-format -i SOME_FILE` manually.
    +
    +### GObject Functions
    +
    +GObject and GPlugin have functions which take pairs of name/value arguments:
    +* `g_object_get()`
    +* `g_object_new()`
    +* `gplugin_plugin_info_new()`
    +
    +If code calling one of these functions with such pairs has to be wrapped, the
    +pairs are easier to read and edit with manual formatting to group the name and
    +value on the same line (but otherwise following the usual formatting that
    +clang-format would apply):
    +```
    + /* clang-format off */
    + g_object_get(
    + G_OBJECT(info),
    + "name", &name,
    + "summary", &summary,
    + NULL);
    + /* clang-format on */
    +```
    +
    +### Debian
    +
    +Debian and derivatives (e.g. Ubuntu) ship clang-format version 9 in a package
    +named `clang-format-9`. The package named `clang-format` is currently an
    +older version. Additionally, the `clang-format-9` package will not place a
    +`clang-format` in the `$PATH`. Install as follows:
    +
    +```
    +sudo apt install clang-format-9
    +sudo update-alternatives --set clang-format /usr/bin/clang-format-9
    +```
    +
    +## convey
    +
    +GPlugin uses [Convey][3] for CI. You can run the integration tests locally.
    +
    +To initially setup Convey, which requires Docker:
    +
    +1. Install Convey. You can use a pre-built binary from [convey's Downloads
    + page][4]; put that (or a symlink) in your `$PATH` named `convey`.
    + For now, avoid version 0.14.
    +2. Install Docker. On Debian systems, use: `sudo apt install docker.io`
    +3. Add yourself to the `docker` group. Adding yourself to a group does not
    + add that group to your existing session, so the first time you will need to
    + get a shell in that group with `newgrp docker` or by rebooting.
    +
    +To run all of the integration tests, run the "all" metaplan:
    +```
    +convey all
    +```
    +
    +To run a specific test/platform, run the plan directly:
    +```
    +convey alpine-edge-amd64
    +```
    +
    +To get a list of metaplans and their plans:
    +```
    +convey list metaplans
    +```
    +
    +## hgflow
    +
    +GPlugin uses [hgflow][5] for development. All development takes place on the
    +develop branch which leaves default to be a stable release clone. All long
    +term feature/bug fix work should take place on a feature branch that when
    +closed, gets merged to develop.
    +
    +Releases are forked from develop and create a new branch for any testing/bug
    +fixes. When a release is deemed done, it's merged back into develop and into
    +default. hgflow takes care of all of this for us.
    +
    +To add a feature please clone `https://keep.imfreedom.org/gplugin/gplugin`.
    +Once you have your fork cloned and hgflow setup and installed, type:
    +```
    +hg flow feature start $NAME_OF_YOUR_FEATURE$
    +```
    +
    +This will create a new branch for you to do your work on. When your work has
    +been completed, please open a review request at
    +[reviews.imfreedom.org](https://reviews.imfreedom.org).
    +
    +[1]: https://clang.llvm.org/docs/ClangFormat.html
    +[2]: https://hg.mozilla.org/projects/nss/file/default/coreconf/precommit.clang-format.sh
    +[3]: https://keep.imfreedom.org/grim/convey
    +[4]: https://bintray.com/pidgin/releases/convey
    +[5]: https://hg.sr.ht/~wu/hgflow
    --- a/HISTORY.md Thu Nov 07 23:09:55 2019 -0600
    +++ b/HISTORY.md Sat Sep 05 20:05:36 2020 -0500
    @@ -32,10 +32,10 @@
    but come on, that should be a one time cost to the library author, instead of
    an additional startup cost to the plugin author. STRIKE TWO!
    -So with two strikes down, I continued researching libpeas, by now working on a
    -plugin for rhythmbox, and just found the API to be clunky and poorly
    -documented. Now maybe I just wasn't finding the write documentation, but this
    -was STRIKE THREE!
    +So with two strikes down, I continued researching libpeas, and discovered that
    +you can only use one language other than the native loader. We, the Pidgin,
    +developers have always aimed to support as many languages as possible, and this
    +just wasn't going to cut it. So there was STRIKE THREE!
    Libpeas had struck out for me, and as such I started GPlugin the very next day!
    --- a/INSTALL.md Thu Nov 07 23:09:55 2019 -0600
    +++ b/INSTALL.md Sat Sep 05 20:05:36 2020 -0500
    @@ -1,6 +1,7 @@
    # Dependencies
    GPlugin depends on the following at a bare minimum:
    +
    * glib-2.0 >= 2.40.0
    * gobject-introspection, libgirepository1.0-dev
    * meson >= 0.42.0
    @@ -9,6 +10,7 @@
    * a C compiler
    A full build (enabled by default) depends on the following:
    +
    * gtk-3
    * python3-dev, python-gi-dev, python3-gi
    * liblua5.1-0-dev, lua-lgi
    @@ -19,9 +21,9 @@
    # Building
    -GPlugin uses meson (http://mesonbuild.com/) as its build system. As such
    -compiling is a little bit different than your typical ./configure, make,
    -sudo make install. But luckily for you, not too much different.
    +GPlugin uses [meson](http://mesonbuild.com/) as its build system. As such
    +compiling is a little bit different than your typical `./configure`, `make`,
    +`sudo make install`.
    Meson requires you to build in a separate directory than your source. As such,
    these instructions use a separate build directory.
    @@ -35,5 +37,5 @@
    ```
    If you want/need to tweak the build system (to enable/disable certain loaders)
    -you can do so at any time by using mesonconf in the build directory.
    +you can do so at any time by using `meson configure` in the build directory.
    --- a/README.md Thu Nov 07 23:09:55 2019 -0600
    +++ b/README.md Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,10 @@
    # GPlugin
    +[ ![Download](https://api.bintray.com/packages/pidgin/releases/GPlugin/images/download.svg) ](https://bintray.com/pidgin/releases/GPlugin/_latestVersion)
    +[ ![Issues](https://img.shields.io/badge/Issues-YouTrack-ee3b8b.svg) ](https://issues.imfreedom.org/issues/GPLUGIN)
    +
    +
    +
    GPlugin is a GObject based library that implements a reusable plugin system.
    It supports loading plugins in multiple other languages via loaders. It relies
    heavily on [GObjectIntrospection](https://gi.readthedocs.io/) to expose its API
    @@ -7,13 +12,7 @@
    It has a simple API which makes it very easy to use in your application.
    For more information on using GPlugin in your application, please see the
    -[embedding](Embedding) page.
    -
    -## Build Status
    -
    -Default: [![Default Build Status](https://bamboo.pidgin.im/plugins/servlet/wittified/build-status/GPLUG-GPLUGIN)](https://bamboo.pidgin.im/browse/GPLUG-GPLUGIN)
    -
    -Develop: [![Develop Build Status](https://bamboo.pidgin.im/plugins/servlet/wittified/build-status/GPLUG-GPLUGIN0)](https://bamboo.pidgin.im/browse/GPLUG-GPLUGIN0/)
    +[embedding](https://docs.pidgin.im/gplugin/latest/chapter-embedding.html) page.
    ## History
    @@ -25,8 +24,5 @@
    ## API Reference
    -The API reference for the `default` branch can be found at
    -[docs.pidgin.im/gplugin/default](https://docs.pidgin.im/gplugin/default).
    -
    -The in-development API reference for the `develop` branch can be found at
    -[docs.pidgin.im/gplugin/develop](https://docs.pidgin.im/gplugin/develop).
    +The in-development API reference for the development branch can be found at
    +[docs.pidgin.im/gplugin/latest](https://docs.pidgin.im/gplugin/latest/).
    --- a/convey.yml Thu Nov 07 23:09:55 2019 -0600
    +++ b/convey.yml Sat Sep 05 20:05:36 2020 -0500
    @@ -6,7 +6,7 @@
    - REGISTRY=docker.io
    - REGISTRY_USERNAME
    - REGISTRY_PASSWORD
    - - DOCS_BUILD_IMAGE=${REGISTRY}/${REPOSITORY}:debian-buster-amd64
    + - DOCS_BUILD_IMAGE=${REGISTRY}/${REPOSITORY}:debian-bullseye-amd64
    - DOCS_VERSION=latest
    tasks:
    @@ -15,22 +15,22 @@
    files:
    - alpine-edge-amd64
    - debian-bullseye-amd64
    - - debian-buster-amd64
    + - debian-bullseye-arm64
    + - debian-bullseye-armhf
    - dist
    - - docs
    - elementary-juno-amd64
    - - fedora-29-amd64
    - fedora-30-amd64
    - fedora-31-amd64
    + - fedora-32-amd64
    - fedora-rawhide-amd64
    + - i18n
    - mingw-w64-i686
    - mingw-w64-x86_64
    - opensuse-tumbleweed-amd64
    - pvs-studio
    - scanbuild
    - - ubuntu-bionic-amd64
    - - ubuntu-cosmic-amd64
    - - ubuntu-disco-amd64
    + - ubuntu-focal-amd64
    + - valgrind
    import:
    type: docker/import
    @@ -116,15 +116,23 @@
    stages:
    - tasks: [import, build, export]
    - debian-buster-amd64:
    - environment: [DISTRO=debian, VERSION=buster]
    + clang-format:
    + environment: [TARGET=clang-format]
    stages:
    - - tasks: [import, build, export]
    + - tasks: [import, build-target]
    debian-bullseye-amd64:
    environment: [DISTRO=debian, VERSION=bullseye]
    stages:
    - tasks: [import, build, export]
    + debian-bullseye-arm64:
    + environment: [DISTRO=debian, VERSION=bullseye, ARCH=arm64]
    + stages:
    + - tasks: [import, build, export]
    + debian-bullseye-armhf:
    + environment: [DISTRO=debian, VERSION=bullseye, ARCH=armhf]
    + stages:
    + - tasks: [import, build, export]
    dist:
    environment: [TARGET=dist]
    @@ -136,10 +144,6 @@
    stages:
    - tasks: [import, build, export]
    - fedora-29-amd64:
    - environment: [DISTRO=fedora, VERSION=29]
    - stages:
    - - tasks: [import, build, export]
    fedora-30-amd64:
    environment: [DISTRO=fedora, VERSION=30]
    stages:
    @@ -148,6 +152,10 @@
    environment: [DISTRO=fedora, VERSION=31]
    stages:
    - tasks: [import, build, export]
    + fedora-32-amd64:
    + environment: [DISTRO=fedora, VERSION=32]
    + stages:
    + - tasks: [import, build, export]
    fedora-rawhide-amd64:
    environment: [DISTRO=fedora, VERSION=rawhide]
    stages:
    @@ -177,6 +185,11 @@
    - tasks: [logout]
    run: always
    + i18n:
    + environment: [TARGET=i18n]
    + stages:
    + - tasks: [import, build-target, export-target]
    +
    mingw-w64-i686:
    environment: [DISTRO=mingw, VERSION=w64, ARCH=i686, PKGBUILD_DIR=mingw-cross]
    stages:
    @@ -196,6 +209,7 @@
    - TARGET=pvs-studio
    - PVS_STUDIO_USERNAME
    - PVS_STUDIO_KEY
    + - PVS_IGNORED_CODES=V568
    stages:
    - tasks: [import, build-target]
    - tasks: [export-target]
    @@ -213,16 +227,8 @@
    stages:
    - tasks: [import, build-target]
    - ubuntu-bionic-amd64:
    - environment: [DISTRO=ubuntu, VERSION=bionic]
    - stages:
    - - tasks: [import, build, export]
    - ubuntu-cosmic-amd64:
    - environment: [DISTRO=ubuntu, VERSION=cosmic]
    - stages:
    - - tasks: [import, build, export]
    - ubuntu-disco-amd64:
    - environment: [DISTRO=ubuntu, VERSION=disco]
    + ubuntu-focal-amd64:
    + environment: [DISTRO=ubuntu, VERSION=focal]
    stages:
    - tasks: [import, build, export]
    @@ -236,10 +242,6 @@
    publish:
    stages:
    - environment:
    - - DISTRO=debian
    - - VERSION=buster
    - tasks: [import-artifacts, publish-deb, publish-debsrc]
    - - environment:
    - DISTRO=elementary
    - VERSION=juno
    tasks: [import-artifacts, publish-deb, publish-debsrc]
    @@ -253,15 +255,7 @@
    tasks: [import-artifacts, publish-rpm]
    - environment:
    - DISTRO=ubuntu
    - - VERSION=bionic
    - tasks: [import-artifacts, publish-deb, publish-debsrc]
    - - environment:
    - - DISTRO=ubuntu
    - - VERSION=cosmic
    - tasks: [import-artifacts, publish-deb, publish-debsrc]
    - - environment:
    - - DISTRO=ubuntu
    - - VERSION=disco
    + - VERSION=focal
    tasks: [import-artifacts, publish-deb, publish-debsrc]
    meta-plans:
    @@ -269,16 +263,25 @@
    plans:
    - alpine-edge-amd64
    - debian-bullseye-amd64
    - - debian-buster-amd64
    - dist
    - - docs
    - - elementary-juno-amd64
    - - fedora-29-amd64
    - fedora-30-amd64
    + - fedora-31-amd64
    + - fedora-32-amd64
    + - fedora-rawhide-amd64
    + - mingw-w64-i686
    + - mingw-w64-x86_64
    - opensuse-tumbleweed-amd64
    + - ubuntu-focal-amd64
    + tools:
    + plans:
    + - clang-format
    + - i18n
    - pvs-studio
    - scanbuild
    - - ubuntu-bionic-amd64
    - - ubuntu-cosmic-amd64
    - - ubuntu-disco-amd64
    + - valgrind
    + - simple
    + qemu-user-static:
    + plans:
    + - debian-bullseye-arm64
    + - debian-bullseye-armhf
    --- a/gplugin-gtk-viewer/gplugin-gtk-viewer-window.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk-viewer/gplugin-gtk-viewer-window.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,6 +1,5 @@
    /*
    - * talkatu
    - * Copyright (C) 2017-2018 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,38 +14,41 @@
    * You should have received a copy of the GNU Lesser General Public
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#include <gtk/gtk.h>
    +#include "gplugin-gtk-viewer-window.h"
    -#include "gplugin-gtk-viewer-window.h"
    +#include <gtk/gtk.h>
    struct _GPluginGtkViewerWindow {
    GtkWindow parent;
    };
    -G_DEFINE_TYPE(GPluginGtkViewerWindow, gplugin_gtk_viewer_window, GTK_TYPE_WINDOW)
    +G_DEFINE_TYPE(
    + GPluginGtkViewerWindow,
    + gplugin_gtk_viewer_window,
    + GTK_TYPE_WINDOW)
    /******************************************************************************
    * GObject Stuff
    *****************************************************************************/
    static void
    -gplugin_gtk_viewer_window_init(GPluginGtkViewerWindow *window) {
    +gplugin_gtk_viewer_window_init(GPluginGtkViewerWindow *window)
    +{
    gtk_widget_init_template(GTK_WIDGET(window));
    }
    static void
    -gplugin_gtk_viewer_window_class_init(GPluginGtkViewerWindowClass *klass) {
    +gplugin_gtk_viewer_window_class_init(GPluginGtkViewerWindowClass *klass)
    +{
    gtk_widget_class_set_template_from_resource(
    GTK_WIDGET_CLASS(klass),
    - "/org/bitbucket/gplugin/gplugin/viewer/window.ui"
    - );
    + "/org/bitbucket/gplugin/gplugin/viewer/window.ui");
    }
    /******************************************************************************
    * Public API
    *****************************************************************************/
    -GtkWidget *gplugin_gtk_viewer_window_new(void) {
    - return GTK_WIDGET(g_object_new(
    - GPLUGIN_GTK_VIEWER_TYPE_WINDOW,
    - NULL
    - ));
    +GtkWidget *
    +gplugin_gtk_viewer_window_new(void)
    +{
    + return GTK_WIDGET(g_object_new(GPLUGIN_GTK_VIEWER_TYPE_WINDOW, NULL));
    }
    --- a/gplugin-gtk-viewer/gplugin-gtk-viewer-window.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk-viewer/gplugin-gtk-viewer-window.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,6 +1,5 @@
    /*
    - * talkatu
    - * Copyright (C) 2017-2018 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -25,7 +24,12 @@
    #include <gtk/gtk.h>
    #define GPLUGIN_GTK_VIEWER_TYPE_WINDOW (gplugin_gtk_viewer_window_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginGtkViewerWindow, gplugin_gtk_viewer_window, GPLUGIN_GTK_VIEWER, WINDOW, GtkWindow)
    +G_DECLARE_FINAL_TYPE(
    + GPluginGtkViewerWindow,
    + gplugin_gtk_viewer_window,
    + GPLUGIN_GTK_VIEWER,
    + WINDOW,
    + GtkWindow)
    G_BEGIN_DECLS
    --- a/gplugin-gtk-viewer/gplugin-gtk-viewer.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk-viewer/gplugin-gtk-viewer.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -20,8 +20,8 @@
    #include <gtk/gtk.h>
    -#include <gplugin/gplugin.h>
    -#include <gplugin-gtk/gplugin-gtk.h>
    +#include <gplugin.h>
    +#include <gplugin-gtk.h>
    /******************************************************************************
    * Globals
    @@ -34,9 +34,10 @@
    * Callbacks
    *****************************************************************************/
    static gboolean
    -window_closed_cb(G_GNUC_UNUSED GtkWidget *w,
    - G_GNUC_UNUSED GdkEvent *e,
    - G_GNUC_UNUSED gpointer d)
    +window_closed_cb(
    + G_GNUC_UNUSED GtkWidget *w,
    + G_GNUC_UNUSED GdkEvent *e,
    + G_GNUC_UNUSED gpointer d)
    {
    gtk_main_quit();
    @@ -44,16 +45,20 @@
    }
    static void
    -selection_changed_cb(GtkTreeSelection *sel, gpointer data) {
    +selection_changed_cb(GtkTreeSelection *sel, gpointer data)
    +{
    GPluginGtkPluginInfo *info = GPLUGIN_GTK_PLUGIN_INFO(data);
    GPluginPlugin *plugin = NULL;
    GtkTreeModel *model = NULL;
    GtkTreeIter iter;
    if(gtk_tree_selection_get_selected(sel, &model, &iter)) {
    - gtk_tree_model_get(model, &iter,
    - GPLUGIN_GTK_STORE_PLUGIN_COLUMN, &plugin,
    - -1);
    + gtk_tree_model_get(
    + model,
    + &iter,
    + GPLUGIN_GTK_STORE_PLUGIN_COLUMN,
    + &plugin,
    + -1);
    }
    gplugin_gtk_plugin_info_set_plugin(info, plugin);
    @@ -63,10 +68,11 @@
    * Helpers
    *****************************************************************************/
    static gboolean
    -internal_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    +internal_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    {
    show_internal = TRUE;
    @@ -74,10 +80,11 @@
    }
    static gboolean
    -no_default_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    +no_default_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    {
    add_default_paths = FALSE;
    @@ -85,10 +92,11 @@
    }
    static gboolean
    -version_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    +version_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    {
    version_only = TRUE;
    @@ -98,7 +106,8 @@
    }
    static GtkWidget *
    -create_window(void) {
    +create_window(void)
    +{
    GtkWidget *window = NULL, *grid = NULL, *sw = NULL;
    GtkWidget *view = NULL, *info = NULL;
    GtkTreeSelection *sel = NULL;
    @@ -106,8 +115,11 @@
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "GPlugin Viewer");
    gtk_container_set_border_width(GTK_CONTAINER(window), 12);
    - g_signal_connect(G_OBJECT(window), "delete-event",
    - G_CALLBACK(window_closed_cb), NULL);
    + g_signal_connect(
    + G_OBJECT(window),
    + "delete-event",
    + G_CALLBACK(window_closed_cb),
    + NULL);
    grid = gtk_grid_new();
    gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
    @@ -115,10 +127,11 @@
    gtk_container_add(GTK_CONTAINER(window), grid);
    sw = gtk_scrolled_window_new(NULL, NULL);
    - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
    - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
    - GTK_SHADOW_IN);
    + gtk_scrolled_window_set_policy(
    + GTK_SCROLLED_WINDOW(sw),
    + GTK_POLICY_NEVER,
    + GTK_POLICY_AUTOMATIC);
    + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
    gtk_grid_attach(GTK_GRID(grid), sw, 0, 0, 1, 1);
    view = gplugin_gtk_view_new();
    @@ -129,8 +142,11 @@
    gtk_grid_attach(GTK_GRID(grid), info, 1, 0, 1, 1);
    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
    - g_signal_connect(G_OBJECT(sel), "changed",
    - G_CALLBACK(selection_changed_cb), info);
    + g_signal_connect(
    + G_OBJECT(sel),
    + "changed",
    + G_CALLBACK(selection_changed_cb),
    + info);
    return window;
    }
    @@ -138,6 +154,7 @@
    /******************************************************************************
    * Main Stuff
    *****************************************************************************/
    +/* clang-format off */
    static GOptionEntry entries[] = {
    {
    "internal", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    @@ -159,9 +176,11 @@
    NULL, 0, 0, 0, NULL, NULL, NULL,
    },
    };
    +/* clang-format on */
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    GError *error = NULL;
    GOptionContext *ctx = NULL;
    GtkWidget *window = NULL;
    @@ -212,4 +231,3 @@
    return 0;
    }
    -
    --- a/gplugin-gtk-viewer/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk-viewer/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -23,7 +23,7 @@
    gplugin_gtk_viewer],
    output : 'gplugin-gtk-viewer.1',
    install : true,
    - install_dir : join_paths(get_option('mandir'), 'man1'))
    + install_dir : get_option('mandir') / 'man1')
    endif
    endif
    --- a/gplugin-gtk/data/plugin-info.ui Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/data/plugin-info.ui Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    <?xml version="1.0" encoding="UTF-8"?>
    -<!-- Generated with glade 3.22.1 -->
    +<!-- Generated with glade 3.22.2 -->
    <interface>
    <requires lib="gtk+" version="3.10"/>
    <template class="GPluginGtkPluginInfo" parent="GtkBox">
    @@ -208,7 +208,7 @@
    <property name="yalign">0</property>
    </object>
    <packing>
    - <property name="expand">True</property>
    + <property name="expand">False</property>
    <property name="fill">True</property>
    <property name="position">1</property>
    </packing>
    @@ -240,7 +240,48 @@
    </packing>
    </child>
    <child>
    - <object class="GtkLabel" id="dependencies">
    + <object class="GtkBox" id="dependencies_box">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <child>
    + <placeholder/>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">6</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="error_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Error</property>
    + <property name="xalign">0</property>
    + <property name="yalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="error">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="xalign">0</property>
    @@ -256,7 +297,7 @@
    <packing>
    <property name="expand">False</property>
    <property name="fill">True</property>
    - <property name="position">6</property>
    + <property name="position">7</property>
    </packing>
    </child>
    <child>
    @@ -517,7 +558,7 @@
    <packing>
    <property name="expand">True</property>
    <property name="fill">True</property>
    - <property name="position">7</property>
    + <property name="position">8</property>
    </packing>
    </child>
    </template>
    @@ -535,6 +576,7 @@
    <widget name="loader_label"/>
    <widget name="internal_label"/>
    <widget name="load_on_query_label"/>
    + <widget name="error_label"/>
    </widgets>
    </object>
    </interface>
    --- a/gplugin-gtk/gplugin-gtk-plugin-info.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/gplugin-gtk-plugin-info.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,16 +15,17 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <gplugin.h>
    #include <gplugin-gtk/gplugin-gtk-plugin-info.h>
    -#include <gplugin/gplugin.h>
    +#include <glib/gi18n-lib.h>
    /**
    * SECTION:gplugin-gtk-plugin-info
    - * @Title: Plugin Info Gtk+ Widgets
    - * @Short_description: Gtk+ Widgets for plugins
    + * @title: Plugin Info Gtk Widgets
    + * @short_description: Gtk Widgets for plugins
    *
    - * #GPluginGtkPluginInfo is a Gtk+ widget that shows information about plugins.
    + * #GPluginGtkPluginInfo is a Gtk widget that shows information about plugins.
    */
    /**
    @@ -46,6 +47,7 @@
    GtkBox parent;
    GPluginPlugin *plugin;
    + gulong signal_id;
    GtkWidget *name;
    GtkWidget *version;
    @@ -53,7 +55,8 @@
    GtkWidget *website;
    GtkWidget *summary;
    GtkWidget *description;
    - GtkWidget *dependencies;
    + GtkWidget *dependencies_box;
    + GtkWidget *error;
    GtkWidget *expander;
    GtkWidget *id;
    GtkWidget *filename;
    @@ -71,7 +74,9 @@
    PROP_PLUGIN,
    N_PROPERTIES,
    };
    -static GParamSpec *properties[N_PROPERTIES] = {NULL,};
    +static GParamSpec *properties[N_PROPERTIES] = {
    + NULL,
    +};
    G_DEFINE_TYPE(GPluginGtkPluginInfo, gplugin_gtk_plugin_info, GTK_TYPE_BOX);
    @@ -79,8 +84,9 @@
    * Callbacks
    *****************************************************************************/
    static void
    -gplugin_gtk_plugin_info_expander_activate(GtkExpander *expander,
    - G_GNUC_UNUSED gpointer data)
    +gplugin_gtk_plugin_info_expander_activate(
    + GtkExpander *expander,
    + G_GNUC_UNUSED gpointer data)
    {
    if(gtk_expander_get_expanded(expander))
    gtk_expander_set_label(expander, "More");
    @@ -92,16 +98,17 @@
    * Helpers
    *****************************************************************************/
    static void
    -_gplugin_gtk_plugin_info_refresh(GPluginGtkPluginInfo *info,
    - GPluginPlugin *plugin)
    +_gplugin_gtk_plugin_info_refresh(GPluginGtkPluginInfo *info)
    {
    GtkWidget *widget = NULL;
    + GError *error = NULL;
    GList *children = NULL, *iter = NULL;
    gchar *markup = NULL;
    gchar *name = NULL, *version = NULL, *website = NULL;
    gchar *summary = NULL, *description = NULL, *id = NULL, *abi_version = NULL;
    gchar *loader = NULL;
    gchar **authors = NULL;
    + gchar **dependencies = NULL;
    guint32 abi_version_uint;
    gboolean loq = FALSE, internal = FALSE;
    const gchar *filename = NULL;
    @@ -112,12 +119,19 @@
    gtk_widget_destroy(GTK_WIDGET(iter->data));
    g_list_free(children);
    + /* remove all the children from the dependencies box */
    + children = gtk_container_get_children(GTK_CONTAINER(info->dependencies_box));
    + for(iter = children; iter; iter = iter->next)
    + gtk_widget_destroy(GTK_WIDGET(iter->data));
    + g_list_free(children);
    +
    /* now get the info if we can */
    - if(GPLUGIN_IS_PLUGIN(plugin)) {
    - GPluginPluginInfo *plugin_info = gplugin_plugin_get_info(plugin);
    - GPluginLoader *plugin_loader = gplugin_plugin_get_loader(plugin);
    + if(GPLUGIN_IS_PLUGIN(info->plugin)) {
    + GPluginPluginInfo *plugin_info = gplugin_plugin_get_info(info->plugin);
    + GPluginLoader *plugin_loader = gplugin_plugin_get_loader(info->plugin);
    - filename = gplugin_plugin_get_filename(plugin);
    + filename = gplugin_plugin_get_filename(info->plugin);
    + error = gplugin_plugin_get_error(info->plugin);
    if(plugin_loader && GPLUGIN_IS_LOADER(plugin_loader)) {
    const char *loader_name = G_OBJECT_TYPE_NAME(plugin_loader);
    @@ -125,30 +139,37 @@
    g_object_unref(G_OBJECT(plugin_loader));
    }
    - g_object_get(G_OBJECT(plugin_info),
    - "abi_version", &abi_version_uint,
    - "authors", &authors,
    - "summary", &summary,
    - "description", &description,
    - "id", &id,
    - "internal", &internal,
    - "load-on-query", &loq,
    - "name", &name,
    - "version", &version,
    - "website", &website,
    - NULL);
    + /* clang-format off */
    + g_object_get(
    + G_OBJECT(plugin_info),
    + "abi_version", &abi_version_uint,
    + "authors", &authors,
    + "summary", &summary,
    + "description", &description,
    + "dependencies", &dependencies,
    + "id", &id,
    + "internal", &internal,
    + "load-on-query", &loq,
    + "name", &name,
    + "version", &version,
    + "website", &website,
    + NULL);
    + /* clang-format on */
    /* fanagle the plugin name */
    - markup = g_markup_printf_escaped("<span font_size=\"large\" "
    - "font_weight=\"bold\">%s</span>",
    - (name) ? name : "Unnamed");
    + markup = g_markup_printf_escaped(
    + "<span font_size=\"large\" "
    + "font_weight=\"bold\">%s</span>",
    + (name) ? name : "Unnamed");
    g_free(name);
    name = markup;
    /* fanagle the website */
    if(website) {
    - markup = g_markup_printf_escaped("<a href=\"%s\">%s</a>",
    - website, website);
    + markup = g_markup_printf_escaped(
    + "<a href=\"%s\">%s</a>",
    + website,
    + website);
    g_free(website);
    website = markup;
    }
    @@ -163,10 +184,17 @@
    gtk_label_set_text(GTK_LABEL(info->version), (version) ? version : "");
    gtk_label_set_markup(GTK_LABEL(info->website), (website) ? website : "");
    gtk_label_set_text(GTK_LABEL(info->summary), (summary) ? summary : "");
    - gtk_label_set_text(GTK_LABEL(info->description), (description) ? description : "");
    + gtk_label_set_text(
    + GTK_LABEL(info->description),
    + (description) ? description : "");
    gtk_label_set_text(GTK_LABEL(info->id), (id) ? id : "");
    + gtk_label_set_text(
    + GTK_LABEL(info->error),
    + (error) ? error->message : "(none)");
    gtk_label_set_text(GTK_LABEL(info->filename), (filename) ? filename : "");
    - gtk_label_set_text(GTK_LABEL(info->abi_version), (abi_version) ? abi_version : "");
    + gtk_label_set_text(
    + GTK_LABEL(info->abi_version),
    + (abi_version) ? abi_version : "");
    gtk_label_set_text(GTK_LABEL(info->loader), (loader) ? loader : "Unknown");
    gtk_label_set_text(GTK_LABEL(info->internal), (internal) ? "Yes" : "No");
    gtk_label_set_text(GTK_LABEL(info->load_on_query), (loq) ? "Yes" : "No");
    @@ -177,6 +205,7 @@
    g_free(version);
    g_free(website);
    g_free(loader);
    + g_clear_error(&error);
    /* set the authors */
    if(authors) {
    @@ -186,19 +215,64 @@
    widget = gtk_label_new(authors[i]);
    gtk_widget_set_halign(widget, GTK_ALIGN_START);
    gtk_widget_set_valign(widget, GTK_ALIGN_START);
    - gtk_box_pack_start(GTK_BOX(info->authors_box), widget, TRUE, TRUE, 0);
    + gtk_box_pack_start(
    + GTK_BOX(info->authors_box),
    + widget,
    + TRUE,
    + TRUE,
    + 0);
    gtk_widget_show(widget);
    }
    }
    g_strfreev(authors);
    +
    + /* set the dependencies */
    + if(dependencies) {
    + gint i = 0;
    +
    + for(i = 0; dependencies[i]; i++) {
    + widget = gtk_label_new(dependencies[i]);
    + gtk_widget_set_halign(widget, GTK_ALIGN_START);
    + gtk_widget_set_valign(widget, GTK_ALIGN_START);
    + gtk_box_pack_start(
    + GTK_BOX(info->dependencies_box),
    + widget,
    + TRUE,
    + TRUE,
    + 0);
    + gtk_widget_show(widget);
    + }
    + } else {
    + widget = gtk_label_new(_("(none)"));
    + gtk_box_pack_start(
    + GTK_BOX(info->dependencies_box),
    + widget,
    + TRUE,
    + TRUE,
    + 0);
    + gtk_widget_show(widget);
    + }
    + g_strfreev(dependencies);
    +}
    +
    +/******************************************************************************
    + * Callbacks
    + *****************************************************************************/
    +static void
    +gplugin_gtk_plugin_info_state_cb(GObject *obj, GParamSpec *pspec, gpointer data)
    +{
    + _gplugin_gtk_plugin_info_refresh(GPLUGIN_GTK_PLUGIN_INFO(data));
    }
    /******************************************************************************
    * GObject Stuff
    *****************************************************************************/
    static void
    -gplugin_gtk_plugin_info_set_property(GObject *obj, guint prop_id,
    - const GValue *value, GParamSpec *pspec)
    +gplugin_gtk_plugin_info_set_property(
    + GObject *obj,
    + guint prop_id,
    + const GValue *value,
    + GParamSpec *pspec)
    {
    GPluginGtkPluginInfo *info = GPLUGIN_GTK_PLUGIN_INFO(obj);
    @@ -213,15 +287,17 @@
    }
    static void
    -gplugin_gtk_plugin_info_get_property(GObject *obj, guint prop_id,
    - GValue *value, GParamSpec *pspec)
    +gplugin_gtk_plugin_info_get_property(
    + GObject *obj,
    + guint prop_id,
    + GValue *value,
    + GParamSpec *pspec)
    {
    GPluginGtkPluginInfo *info = GPLUGIN_GTK_PLUGIN_INFO(obj);
    switch(prop_id) {
    case PROP_PLUGIN:
    - g_value_set_object(value,
    - gplugin_gtk_plugin_info_get_plugin(info));
    + g_value_set_object(value, gplugin_gtk_plugin_info_get_plugin(info));
    break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
    @@ -230,21 +306,30 @@
    }
    static void
    -gplugin_gtk_plugin_info_finalize(GObject *obj) {
    +gplugin_gtk_plugin_info_finalize(GObject *obj)
    +{
    GPluginGtkPluginInfo *info = GPLUGIN_GTK_PLUGIN_INFO(obj);
    + if(info->signal_id != 0) {
    + g_signal_handler_disconnect(G_OBJECT(info->plugin), info->signal_id);
    + }
    +
    g_clear_object(&info->plugin);
    G_OBJECT_CLASS(gplugin_gtk_plugin_info_parent_class)->finalize(obj);
    }
    static void
    -gplugin_gtk_plugin_info_init(GPluginGtkPluginInfo *info) {
    +gplugin_gtk_plugin_info_init(GPluginGtkPluginInfo *info)
    +{
    gtk_widget_init_template(GTK_WIDGET(info));
    +
    + info->signal_id = 0;
    }
    static void
    -gplugin_gtk_plugin_info_class_init(GPluginGtkPluginInfoClass *klass) {
    +gplugin_gtk_plugin_info_class_init(GPluginGtkPluginInfoClass *klass)
    +{
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    @@ -254,34 +339,82 @@
    /* properties */
    properties[PROP_PLUGIN] = g_param_spec_object(
    - "plugin", "plugin",
    + "plugin",
    + "plugin",
    "The GPluginPlugin who's info should be displayed",
    G_TYPE_OBJECT,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
    /* template stuff */
    gtk_widget_class_set_template_from_resource(
    widget_class,
    - "/org/bitbucket/gplugin/gplugin-gtk/plugin-info.ui"
    - );
    + "/org/bitbucket/gplugin/gplugin-gtk/plugin-info.ui");
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, name);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, version);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, authors_box);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, website);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, summary);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, description);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, dependencies);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, expander);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, id);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, filename);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, abi_version);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, loader);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, internal);
    - gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginInfo, load_on_query);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + name);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + version);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + authors_box);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + website);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + summary);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + description);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + dependencies_box);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + error);
    - gtk_widget_class_bind_template_callback(widget_class, gplugin_gtk_plugin_info_expander_activate);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + expander);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + id);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + filename);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + abi_version);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + loader);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + internal);
    + gtk_widget_class_bind_template_child(
    + widget_class,
    + GPluginGtkPluginInfo,
    + load_on_query);
    +
    + gtk_widget_class_bind_template_callback(
    + widget_class,
    + gplugin_gtk_plugin_info_expander_activate);
    }
    /******************************************************************************
    @@ -297,42 +430,64 @@
    * Returns: (transfer full): The new #GPluginGtkView widget.
    */
    GtkWidget *
    -gplugin_gtk_plugin_info_new(void) {
    +gplugin_gtk_plugin_info_new(void)
    +{
    return GTK_WIDGET(g_object_new(GPLUGIN_GTK_TYPE_PLUGIN_INFO, NULL));
    }
    /**
    * gplugin_gtk_plugin_info_set_plugin:
    - * @info: The #GPluginGtkPluginInfo instance
    - * @plugin: The #GPluginPlugin instance
    + * @info: The #GPluginGtkPluginInfo instance.
    + * @plugin: The #GPluginPlugin instance.
    *
    * Sets the #GPluginPlugin that should be displayed.
    *
    - * A @plugin value of NULL will clear the widget.
    + * A @plugin value of %NULL will clear the widget.
    */
    void
    -gplugin_gtk_plugin_info_set_plugin(GPluginGtkPluginInfo *info,
    - GPluginPlugin *plugin)
    +gplugin_gtk_plugin_info_set_plugin(
    + GPluginGtkPluginInfo *info,
    + GPluginPlugin *plugin)
    {
    g_return_if_fail(GPLUGIN_GTK_IS_PLUGIN_INFO(info));
    + if(info->signal_id != 0 && GPLUGIN_IS_PLUGIN(info->plugin)) {
    + g_signal_handler_disconnect(info->plugin, info->signal_id);
    + info->signal_id = 0;
    + }
    +
    if(g_set_object(&info->plugin, plugin)) {
    - _gplugin_gtk_plugin_info_refresh(info, plugin);
    + _gplugin_gtk_plugin_info_refresh(info);
    +
    + /* Connect a signal to refresh when the plugin's state changes. We
    + * can't use g_signal_connect_object because the plugin object never
    + * gets destroyed, as the manager and the loader both keep a reference
    + * to it and the GPluginGtkPluginInfo widget is reused for all plugins
    + * so that all means that we just have to manage the callback
    + * ourselves.
    + */
    + info->signal_id = g_signal_connect(
    + G_OBJECT(plugin),
    + "notify::state",
    + G_CALLBACK(gplugin_gtk_plugin_info_state_cb),
    + info);
    }
    }
    /**
    * gplugin_gtk_plugin_info_get_plugin:
    - * @info: The #GPluginGtkPluginInfo instance
    + * @info: The #GPluginGtkPluginInfo instance.
    *
    * Returns the #GPluginPlugin that's being displayed.
    *
    * Return Value: (transfer full): The #GPluginPlugin that's being
    - * displayed.
    + * displayed.
    */
    GPluginPlugin *
    -gplugin_gtk_plugin_info_get_plugin(GPluginGtkPluginInfo *info) {
    +gplugin_gtk_plugin_info_get_plugin(GPluginGtkPluginInfo *info)
    +{
    g_return_val_if_fail(GPLUGIN_GTK_IS_PLUGIN_INFO(info), NULL);
    - return (info->plugin) ? GPLUGIN_PLUGIN(g_object_ref(G_OBJECT(info->plugin))) : NULL;
    + return (info->plugin) ? GPLUGIN_PLUGIN(g_object_ref(G_OBJECT(info->plugin)))
    + : NULL;
    }
    --- a/gplugin-gtk/gplugin-gtk-plugin-info.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/gplugin-gtk-plugin-info.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,7 +15,8 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && !defined(GPLUGIN_GTK_COMPILATION)
    +#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && \
    + !defined(GPLUGIN_GTK_COMPILATION)
    #error "only <gplugin/gplugin-gtk.h> may be included directly"
    #endif
    @@ -32,14 +33,20 @@
    G_BEGIN_DECLS
    #define GPLUGIN_GTK_TYPE_PLUGIN_INFO (gplugin_gtk_plugin_info_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginGtkPluginInfo, gplugin_gtk_plugin_info, GPLUGIN_GTK, PLUGIN_INFO, GtkBox)
    +G_DECLARE_FINAL_TYPE(
    + GPluginGtkPluginInfo,
    + gplugin_gtk_plugin_info,
    + GPLUGIN_GTK,
    + PLUGIN_INFO,
    + GtkBox)
    GtkWidget *gplugin_gtk_plugin_info_new(void);
    -void gplugin_gtk_plugin_info_set_plugin(GPluginGtkPluginInfo *info, GPluginPlugin *plugin);
    +void gplugin_gtk_plugin_info_set_plugin(
    + GPluginGtkPluginInfo *info,
    + GPluginPlugin *plugin);
    GPluginPlugin *gplugin_gtk_plugin_info_get_plugin(GPluginGtkPluginInfo *info);
    G_END_DECLS
    #endif /* GPLUGIN_GTK_PLUGIN_INFO_H */
    -
    --- a/gplugin-gtk/gplugin-gtk-store.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/gplugin-gtk-store.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,16 +15,15 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#include <gplugin/gplugin.h>
    -
    +#include <gplugin.h>
    #include <gplugin-gtk/gplugin-gtk-store.h>
    /**
    * SECTION:gplugin-gtk-store
    - * @Title: GtkTreeModelStore for gplugins
    - * @Short_description: A store for plugins
    + * @title: GtkTreeModelStore for plugins
    + * @short_description: A store for plugins
    *
    - * #GPluginGtkStore is a GtkTreeModel populated with gplugins.
    + * #GPluginGtkStore is a GtkTreeModel populated with plugins.
    */
    /**
    @@ -35,6 +34,9 @@
    /**
    * GPluginGtkStoreColumns:
    + * @GPLUGIN_GTK_STORE_ENABLED_COLUMN: The disabled column. This is used when a
    + * plugin is in a state that can't be
    + * changed. So the row should be disabled.
    * @GPLUGIN_GTK_STORE_LOADED_COLUMN: The loaded column.
    * @GPLUGIN_GTK_STORE_PLUGIN_COLUMN: The plugin column.
    * @GPLUGIN_GTK_STORE_MARKUP_COLUMN: The markup column.
    @@ -59,6 +61,7 @@
    *****************************************************************************/
    static const GType column_types[] = {
    G_TYPE_BOOLEAN,
    + G_TYPE_BOOLEAN,
    G_TYPE_OBJECT,
    G_TYPE_STRING,
    };
    @@ -69,69 +72,106 @@
    * Helpers
    *****************************************************************************/
    static void
    -gplugin_gtk_store_add_plugin(GPluginGtkStore *store, GPluginPlugin *plugin) {
    +gplugin_gtk_store_add_plugin(GPluginGtkStore *store, GPluginPlugin *plugin)
    +{
    GtkTreeIter iter;
    GPluginPluginInfo *info = gplugin_plugin_get_info(plugin);
    GPluginPluginState state = gplugin_plugin_get_state(plugin);
    GString *str = g_string_new("");
    gchar *name = NULL, *summary = NULL;
    + gboolean loaded = FALSE, enabled = TRUE;
    - g_object_get(G_OBJECT(info),
    - "name", &name,
    - "summary", &summary,
    - NULL);
    + /* clang-format off */
    + g_object_get(
    + G_OBJECT(info),
    + "name", &name,
    + "summary", &summary,
    + NULL);
    + /* clang-format on */
    - g_string_append_printf(str, "<b>%s</b>\n",
    - (name) ? name : "<i>Unnamed</i>");
    - g_string_append_printf(str, "%s",
    - (summary) ? summary : "<i>No Summary</i>");
    + g_string_append_printf(
    + str,
    + "<b>%s</b>\n",
    + (name) ? name : "<i>Unnamed</i>");
    + g_string_append_printf(
    + str,
    + "%s",
    + (summary) ? summary : "<i>No Summary</i>");
    g_free(name);
    g_free(summary);
    + loaded = (state == GPLUGIN_PLUGIN_STATE_LOADED);
    + if(state == GPLUGIN_PLUGIN_STATE_UNLOAD_FAILED) {
    + loaded = TRUE;
    + enabled = FALSE;
    + }
    +
    gtk_list_store_append(GTK_LIST_STORE(store), &iter);
    - gtk_list_store_set(GTK_LIST_STORE(store), &iter,
    - GPLUGIN_GTK_STORE_LOADED_COLUMN, state == GPLUGIN_PLUGIN_STATE_LOADED,
    - GPLUGIN_GTK_STORE_PLUGIN_COLUMN, g_object_ref(plugin),
    - GPLUGIN_GTK_STORE_MARKUP_COLUMN, str->str,
    - -1);
    + gtk_list_store_set(
    + GTK_LIST_STORE(store),
    + &iter,
    + GPLUGIN_GTK_STORE_LOADED_COLUMN,
    + loaded,
    + GPLUGIN_GTK_STORE_ENABLED_COLUMN,
    + enabled,
    + GPLUGIN_GTK_STORE_PLUGIN_COLUMN,
    + g_object_ref(plugin),
    + GPLUGIN_GTK_STORE_MARKUP_COLUMN,
    + str->str,
    + -1);
    g_string_free(str, TRUE);
    g_object_unref(G_OBJECT(info));
    }
    static void
    -gplugin_gtk_store_add_plugin_by_id(GPluginGtkStore *store, const gchar * id) {
    +gplugin_gtk_store_add_plugin_by_id(GPluginGtkStore *store, const gchar *id)
    +{
    GSList *plugins = NULL, *l = NULL;
    plugins = gplugin_manager_find_plugins(id);
    for(l = plugins; l; l = l->next)
    gplugin_gtk_store_add_plugin(store, GPLUGIN_PLUGIN(l->data));
    - gplugin_manager_free_plugin_list(plugins);
    + g_slist_free_full(plugins, g_object_unref);
    }
    static gboolean
    -gplugin_gtk_store_update_plugin_state_cb(GtkTreeModel *model, GtkTreePath *path,
    - GtkTreeIter *iter, gpointer data)
    +gplugin_gtk_store_update_plugin_state_cb(
    + GtkTreeModel *model,
    + GtkTreePath *path,
    + GtkTreeIter *iter,
    + gpointer data)
    {
    GPluginPlugin *plugin_a = GPLUGIN_PLUGIN(data);
    GPluginPlugin *plugin_b = NULL;
    gboolean ret = FALSE;
    gtk_tree_model_get(
    - model, iter,
    - GPLUGIN_GTK_STORE_PLUGIN_COLUMN, &plugin_b,
    - -1
    - );
    + model,
    + iter,
    + GPLUGIN_GTK_STORE_PLUGIN_COLUMN,
    + &plugin_b,
    + -1);
    if(plugin_a == plugin_b) {
    - gboolean loaded = gplugin_plugin_get_state(plugin_a) == GPLUGIN_PLUGIN_STATE_LOADED;
    + GPluginPluginState state = gplugin_plugin_get_state(plugin_a);
    + gboolean loaded = (state == GPLUGIN_PLUGIN_STATE_LOADED);
    + gboolean enabled = TRUE;
    +
    + if(state == GPLUGIN_PLUGIN_STATE_UNLOAD_FAILED) {
    + loaded = TRUE;
    + enabled = FALSE;
    + }
    gtk_list_store_set(
    - GTK_LIST_STORE(model), iter,
    - GPLUGIN_GTK_STORE_LOADED_COLUMN, loaded,
    - -1
    - );
    + GTK_LIST_STORE(model),
    + iter,
    + GPLUGIN_GTK_STORE_LOADED_COLUMN,
    + loaded,
    + GPLUGIN_GTK_STORE_ENABLED_COLUMN,
    + enabled,
    + -1);
    /* tell gplugin_gtk_store_update_plugin_state that we're done */
    ret = TRUE;
    @@ -143,29 +183,42 @@
    }
    static void
    -gplugin_gtk_store_update_plugin_state(GPluginGtkStore *store,
    - GPluginPlugin *plugin)
    +gplugin_gtk_store_update_plugin_state(
    + GPluginGtkStore *store,
    + GPluginPlugin *plugin)
    {
    gtk_tree_model_foreach(
    GTK_TREE_MODEL(store),
    gplugin_gtk_store_update_plugin_state_cb,
    - plugin
    - );
    + plugin);
    }
    /******************************************************************************
    * Callbacks
    *****************************************************************************/
    static void
    -gplugin_gtk_store_plugin_loaded_cb(GObject *manager, GPluginPlugin *plugin,
    - gpointer data)
    +gplugin_gtk_store_plugin_loaded_cb(
    + GObject *manager,
    + GPluginPlugin *plugin,
    + gpointer data)
    {
    gplugin_gtk_store_update_plugin_state(GPLUGIN_GTK_STORE(data), plugin);
    }
    static void
    -gplugin_gtk_store_plugin_unloaded_cb(GObject *manager, GPluginPlugin *plugin,
    - gpointer data)
    +gplugin_gtk_store_plugin_unloaded_cb(
    + GObject *manager,
    + GPluginPlugin *plugin,
    + gpointer data)
    +{
    + gplugin_gtk_store_update_plugin_state(GPLUGIN_GTK_STORE(data), plugin);
    +}
    +
    +static void
    +gplugin_gtk_store_plugin_unload_failed_cb(
    + G_GNUC_UNUSED GObject *manager,
    + GPluginPlugin *plugin,
    + gpointer data)
    {
    gplugin_gtk_store_update_plugin_state(GPLUGIN_GTK_STORE(data), plugin);
    }
    @@ -174,7 +227,8 @@
    * GObject Implementation
    *****************************************************************************/
    static void
    -gplugin_gtk_store_constructed(GObject *obj) {
    +gplugin_gtk_store_constructed(GObject *obj)
    +{
    GObject *manager = NULL;
    GList *l, *ids = NULL;
    @@ -182,33 +236,49 @@
    ids = gplugin_manager_list_plugins();
    for(l = ids; l; l = l->next)
    - gplugin_gtk_store_add_plugin_by_id(GPLUGIN_GTK_STORE(obj),
    - (const gchar *)l->data);
    + gplugin_gtk_store_add_plugin_by_id(
    + GPLUGIN_GTK_STORE(obj),
    + (const gchar *)l->data);
    g_list_free(ids);
    manager = gplugin_manager_get_instance();
    - g_signal_connect(manager, "loaded-plugin",
    - G_CALLBACK(gplugin_gtk_store_plugin_loaded_cb), obj);
    - g_signal_connect(manager, "unloaded-plugin",
    - G_CALLBACK(gplugin_gtk_store_plugin_unloaded_cb), obj);
    + g_signal_connect(
    + manager,
    + "loaded-plugin",
    + G_CALLBACK(gplugin_gtk_store_plugin_loaded_cb),
    + obj);
    + g_signal_connect(
    + manager,
    + "unloaded-plugin",
    + G_CALLBACK(gplugin_gtk_store_plugin_unloaded_cb),
    + obj);
    + g_signal_connect(
    + manager,
    + "unload-plugin-failed",
    + G_CALLBACK(gplugin_gtk_store_plugin_unload_failed_cb),
    + obj);
    }
    static void
    -gplugin_gtk_store_dispose(GObject *obj) {
    +gplugin_gtk_store_dispose(GObject *obj)
    +{
    G_OBJECT_CLASS(gplugin_gtk_store_parent_class)->dispose(obj);
    }
    static void
    -gplugin_gtk_store_init(GPluginGtkStore *store) {
    - GType *types = (GType *)gplugin_gtk_get_store_column_types();
    +gplugin_gtk_store_init(GPluginGtkStore *store)
    +{
    + GType *types = (GType *)gplugin_gtk_store_get_column_types();
    - gtk_list_store_set_column_types(GTK_LIST_STORE(store),
    - GPLUGIN_GTK_STORE_N_COLUMNS,
    - types);
    + gtk_list_store_set_column_types(
    + GTK_LIST_STORE(store),
    + GPLUGIN_GTK_STORE_N_COLUMNS,
    + types);
    }
    static void
    -gplugin_gtk_store_class_init(GPluginGtkStoreClass *klass) {
    +gplugin_gtk_store_class_init(GPluginGtkStoreClass *klass)
    +{
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    obj_class->constructed = gplugin_gtk_store_constructed;
    @@ -222,26 +292,27 @@
    /**
    * gplugin_gtk_store_new:
    *
    - * Create a new GPluginGtkStore which is a prepopulated #GtkTreeStore.
    + * Create a new #GPluginGtkStore which is a prepopulated #GtkTreeStore.
    *
    - * Returns: (transfer full): A new GtkTreeModel prepopulated with all of the
    - * gplugins.
    + * Returns: (transfer full): A new #GtkTreeModel prepopulated with all of the
    + * plugins.
    */
    GPluginGtkStore *
    -gplugin_gtk_store_new(void) {
    +gplugin_gtk_store_new(void)
    +{
    return GPLUGIN_GTK_STORE(g_object_new(GPLUGIN_GTK_TYPE_STORE, NULL));
    }
    /**
    - * gplugin_gtk_get_store_column_types:
    + * gplugin_gtk_store_get_column_types:
    *
    * Returns the columns that #GPluginGtkStore's will use.
    *
    - * Returns: (transfer none): A list of #GTypes for the columes that the store
    + * Returns: (transfer none): A list of #GType's for the columns that the store
    * will use.
    */
    const GType *
    -gplugin_gtk_get_store_column_types(void) {
    +gplugin_gtk_store_get_column_types(void)
    +{
    return column_types;
    }
    -
    --- a/gplugin-gtk/gplugin-gtk-store.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/gplugin-gtk-store.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,7 +15,8 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && !defined(GPLUGIN_GTK_COMPILATION)
    +#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && \
    + !defined(GPLUGIN_GTK_COMPILATION)
    #error "only <gplugin/gplugin-gtk.h> may be included directly"
    #endif
    @@ -30,9 +31,15 @@
    G_BEGIN_DECLS
    #define GPLUGIN_GTK_TYPE_STORE (gplugin_gtk_store_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginGtkStore, gplugin_gtk_store, GPLUGIN_GTK, STORE, GtkListStore)
    +G_DECLARE_FINAL_TYPE(
    + GPluginGtkStore,
    + gplugin_gtk_store,
    + GPLUGIN_GTK,
    + STORE,
    + GtkListStore)
    typedef enum {
    + GPLUGIN_GTK_STORE_ENABLED_COLUMN,
    GPLUGIN_GTK_STORE_LOADED_COLUMN,
    GPLUGIN_GTK_STORE_PLUGIN_COLUMN,
    GPLUGIN_GTK_STORE_MARKUP_COLUMN,
    @@ -43,9 +50,8 @@
    GPluginGtkStore *gplugin_gtk_store_new(void);
    -const GType *gplugin_gtk_get_store_column_types(void);
    +const GType *gplugin_gtk_store_get_column_types(void);
    G_END_DECLS
    #endif /* GPLUGIN_GTK_STORE_H */
    -
    --- a/gplugin-gtk/gplugin-gtk-view.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/gplugin-gtk-view.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,17 +15,16 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#include <gplugin-gtk/gplugin-gtk-view.h>
    +#include <gplugin.h>
    #include <gplugin-gtk/gplugin-gtk-store.h>
    -
    -#include <gplugin/gplugin.h>
    +#include <gplugin-gtk/gplugin-gtk-view.h>
    /**
    * SECTION:gplugin-gtk-view
    - * @Title: Gtk+ View for gplugins
    - * @Short_description: A view for plugins
    + * @title: GtkTreeView for plugins
    + * @short_description: A view for plugins
    *
    - * #GPluginGtkView is a display widget for a list of GPlugins.
    + * #GPluginGtkView is a display widget for a list of plugins.
    */
    /**
    @@ -58,15 +57,18 @@
    PROP_SHOW_INTERNAL,
    N_PROPERTIES,
    };
    -static GParamSpec *properties[N_PROPERTIES] = {NULL,};
    +static GParamSpec *properties[N_PROPERTIES] = {
    + NULL,
    +};
    /******************************************************************************
    * Callbacks
    *****************************************************************************/
    static void
    -gplugin_gtk_view_plugin_toggled_cb(GtkCellRendererToggle *rend,
    - gchar *path,
    - gpointer data)
    +gplugin_gtk_view_plugin_toggled_cb(
    + GtkCellRendererToggle *rend,
    + gchar *path,
    + gpointer data)
    {
    GPluginGtkView *view = GPLUGIN_GTK_VIEW(data);
    GPluginPlugin *plugin = NULL;
    @@ -84,9 +86,9 @@
    gtk_tree_model_get(
    model,
    &iter,
    - GPLUGIN_GTK_STORE_PLUGIN_COLUMN, &plugin,
    - -1
    - );
    + GPLUGIN_GTK_STORE_PLUGIN_COLUMN,
    + &plugin,
    + -1);
    if(!GPLUGIN_IS_PLUGIN(plugin)) {
    return;
    @@ -124,15 +126,19 @@
    G_DEFINE_TYPE(GPluginGtkView, gplugin_gtk_view, GTK_TYPE_TREE_VIEW);
    static void
    -gplugin_gtk_view_set_property(GObject *obj, guint prop_id, const GValue *value,
    - GParamSpec *pspec)
    +gplugin_gtk_view_set_property(
    + GObject *obj,
    + guint prop_id,
    + const GValue *value,
    + GParamSpec *pspec)
    {
    GPluginGtkView *view = GPLUGIN_GTK_VIEW(obj);
    switch(prop_id) {
    case PROP_SHOW_INTERNAL:
    - gplugin_gtk_view_set_show_internal(view,
    - g_value_get_boolean(value));
    + gplugin_gtk_view_set_show_internal(
    + view,
    + g_value_get_boolean(value));
    break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
    @@ -141,15 +147,19 @@
    }
    static void
    -gplugin_gtk_view_get_property(GObject *obj, guint prop_id, GValue *value,
    - GParamSpec *pspec)
    +gplugin_gtk_view_get_property(
    + GObject *obj,
    + guint prop_id,
    + GValue *value,
    + GParamSpec *pspec)
    {
    GPluginGtkView *view = GPLUGIN_GTK_VIEW(obj);
    switch(prop_id) {
    case PROP_SHOW_INTERNAL:
    - g_value_set_boolean(value,
    - gplugin_gtk_view_get_show_internal(view));
    + g_value_set_boolean(
    + value,
    + gplugin_gtk_view_get_show_internal(view));
    break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
    @@ -158,17 +168,20 @@
    }
    static void
    -gplugin_gtk_view_constructed(GObject *obj) {
    +gplugin_gtk_view_constructed(GObject *obj)
    +{
    G_OBJECT_CLASS(gplugin_gtk_view_parent_class)->constructed(obj);
    }
    static void
    -gplugin_gtk_view_dispose(GObject *obj) {
    +gplugin_gtk_view_dispose(GObject *obj)
    +{
    G_OBJECT_CLASS(gplugin_gtk_view_parent_class)->dispose(obj);
    }
    static void
    -gplugin_gtk_view_init(GPluginGtkView *view) {
    +gplugin_gtk_view_init(GPluginGtkView *view)
    +{
    GtkTreeViewColumn *col = NULL;
    GtkCellRenderer *rend = NULL;
    @@ -179,12 +192,21 @@
    rend = gtk_cell_renderer_toggle_new();
    gtk_tree_view_column_pack_start(col, rend, FALSE);
    - g_signal_connect(G_OBJECT(rend), "toggled",
    - G_CALLBACK(gplugin_gtk_view_plugin_toggled_cb), view);
    + g_signal_connect(
    + G_OBJECT(rend),
    + "toggled",
    + G_CALLBACK(gplugin_gtk_view_plugin_toggled_cb),
    + view);
    gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
    - gtk_tree_view_column_add_attribute(col, rend, "active",
    - GPLUGIN_GTK_STORE_LOADED_COLUMN);
    + gtk_tree_view_column_set_attributes(
    + col,
    + rend,
    + "active",
    + GPLUGIN_GTK_STORE_LOADED_COLUMN,
    + "sensitive",
    + GPLUGIN_GTK_STORE_ENABLED_COLUMN,
    + NULL);
    /* create the markup column */
    col = gtk_tree_view_column_new();
    @@ -195,12 +217,19 @@
    gtk_tree_view_column_pack_start(col, rend, TRUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
    - gtk_tree_view_column_add_attribute(col, rend, "markup",
    - GPLUGIN_GTK_STORE_MARKUP_COLUMN);
    + gtk_tree_view_column_set_attributes(
    + col,
    + rend,
    + "markup",
    + GPLUGIN_GTK_STORE_MARKUP_COLUMN,
    + "sensitive",
    + GPLUGIN_GTK_STORE_ENABLED_COLUMN,
    + NULL);
    }
    static void
    -gplugin_gtk_view_class_init(GPluginGtkViewClass *klass) {
    +gplugin_gtk_view_class_init(GPluginGtkViewClass *klass)
    +{
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    obj_class->get_property = gplugin_gtk_view_get_property;
    @@ -216,11 +245,11 @@
    * Whether or not to show internal plugins.
    */
    properties[PROP_SHOW_INTERNAL] = g_param_spec_boolean(
    - "show-internal", "show-internal",
    + "show-internal",
    + "show-internal",
    "Whether or not to show internal plugins",
    FALSE,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
    g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    }
    @@ -232,33 +261,36 @@
    /**
    * gplugin_gtk_view_new:
    *
    - * Creates a new GPluginGtkView.
    + * Creates a new #GPluginGtkView.
    *
    * Returns: (transfer full): The new view.
    */
    GtkWidget *
    -gplugin_gtk_view_new(void) {
    +gplugin_gtk_view_new(void)
    +{
    GObject *ret = NULL;
    GPluginGtkStore *store = gplugin_gtk_store_new();
    - ret = g_object_new(GPLUGIN_GTK_TYPE_VIEW,
    - "model", GTK_TREE_MODEL(store),
    - NULL);
    + /* clang-format off */
    + ret = g_object_new(
    + GPLUGIN_GTK_TYPE_VIEW,
    + "model", GTK_TREE_MODEL(store),
    + NULL);
    + /* clang-format on */
    return GTK_WIDGET(ret);
    }
    /**
    * gplugin_gtk_view_set_show_internal:
    - * @view: The #GPluginGtkView instance
    + * @view: The #GPluginGtkView instance.
    * @show_internal: Whether or not to show internal plugins.
    *
    * This function will toggle whether or not the widget will show internal
    * plugins.
    */
    void
    -gplugin_gtk_view_set_show_internal(GPluginGtkView *view,
    - gboolean show_internal)
    +gplugin_gtk_view_set_show_internal(GPluginGtkView *view, gboolean show_internal)
    {
    g_return_if_fail(GPLUGIN_GTK_IS_VIEW(view));
    @@ -269,14 +301,14 @@
    /**
    * gplugin_gtk_view_get_show_internal:
    - * @view: The #GPluginGtkView instance
    + * @view: The #GPluginGtkView instance.
    *
    * Returns whether or not @view is showing internal plugins.
    */
    gboolean
    -gplugin_gtk_view_get_show_internal(GPluginGtkView *view) {
    +gplugin_gtk_view_get_show_internal(GPluginGtkView *view)
    +{
    g_return_val_if_fail(GPLUGIN_GTK_IS_VIEW(view), FALSE);
    return view->show_internal;
    }
    -
    --- a/gplugin-gtk/gplugin-gtk-view.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/gplugin-gtk-view.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,7 +15,8 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && !defined(GPLUGIN_GTK_COMPILATION)
    +#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && \
    + !defined(GPLUGIN_GTK_COMPILATION)
    #error "only <gplugin/gplugin-gtk.h> may be included directly"
    #endif
    @@ -30,14 +31,20 @@
    G_BEGIN_DECLS
    #define GPLUGIN_GTK_TYPE_VIEW (gplugin_gtk_view_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginGtkView, gplugin_gtk_view, GPLUGIN_GTK, VIEW, GtkTreeView)
    +G_DECLARE_FINAL_TYPE(
    + GPluginGtkView,
    + gplugin_gtk_view,
    + GPLUGIN_GTK,
    + VIEW,
    + GtkTreeView)
    GtkWidget *gplugin_gtk_view_new(void);
    -void gplugin_gtk_view_set_show_internal(GPluginGtkView *view, gboolean show_internal);
    +void gplugin_gtk_view_set_show_internal(
    + GPluginGtkView *view,
    + gboolean show_internal);
    gboolean gplugin_gtk_view_get_show_internal(GPluginGtkView *view);
    G_END_DECLS
    #endif /* GPLUGIN_GTK_VIEW_H */
    -
    --- a/gplugin-gtk/gplugin-gtk.h.in Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/gplugin-gtk.h.in Sat Sep 05 20:05:36 2020 -0500
    @@ -1,18 +1,18 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #ifndef GPLUGIN_GTK_H
    #define GPLUGIN_GTK_H
    @@ -26,4 +26,3 @@
    #undef GPLUGIN_GTK_GLOBAL_HEADER_INSIDE
    #endif /* GPLUGIN_GTK_H */
    -
    --- a/gplugin-gtk/gplugin-gtk.xml.in Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/gplugin-gtk.xml.in Sat Sep 05 20:05:36 2020 -0500
    @@ -28,7 +28,7 @@
    toplevel="True"/>
    </glade-widget-classes>
    - <glade-widget-group name="gplugin-gtk" title="GPlugin Gtk+ Widgets">
    + <glade-widget-group name="gplugin-gtk" title="GPlugin Gtk Widgets">
    <glade-widget-class-ref name="GPluginGtkPluginInfo"/>
    <glade-widget-class-ref name="GPluginGtkView"/>
    <glade-widget-class-ref name="GPluginGtkStore"/>
    --- a/gplugin-gtk/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -49,14 +49,14 @@
    output : 'gplugin-gtk.h',
    configuration : conf,
    install : true,
    - install_dir : join_paths(get_option('includedir'), 'gplugin-1.0'))
    + install_dir : get_option('includedir') / 'gplugin-1.0')
    gplugin_gtk_xml = configure_file(
    input : 'gplugin-gtk.xml.in',
    output: 'gplugin-gtk.xml',
    configuration : version_conf,
    install: true,
    - install_dir : join_paths(get_option('datadir'), 'glade', 'catalogs'),
    + install_dir : get_option('datadir') / 'glade' / 'catalogs',
    )
    ###############################################################################
    @@ -96,13 +96,13 @@
    )
    pkgconfig.generate(
    + gplugin_gtk,
    name : 'libgplugin-gtk',
    - description : 'Gtk+ widgets for GPlugin',
    - version : meson.project_version(),
    + description : 'Gtk widgets for GPlugin',
    filebase : 'gplugin-gtk',
    subdirs : 'gplugin-1.0',
    - libraries : gplugin_gtk,
    - requires : ['glib-2.0', 'gobject-2.0', 'gmodule-2.0', 'gplugin'],
    + libraries : [gplugin],
    + requires : [GLIB, GOBJECT, GMODULE],
    variables : [
    'plugindir=${libdir}',
    ],
    @@ -111,7 +111,7 @@
    ###############################################################################
    # GObject Introspection
    ###############################################################################
    -if get_option('gobject-introspection')
    +if get_option('introspection')
    gplugin_gtk_gir = gnome.generate_gir(
    gplugin_gtk,
    sources : GPLUGIN_GTK_SOURCES + GPLUGIN_GTK_HEADERS +
    @@ -120,7 +120,7 @@
    includes : [gplugin_gir[0], 'Gtk-3.0'],
    namespace : 'GPluginGtk',
    symbol_prefix : 'gplugin_gtk',
    - nsversion : '@0@.0'.format(GPLUGIN_MAJOR_VERSION),
    + nsversion : '1.0',
    install : true,
    export_packages: ['gplugin-gtk'],
    extra_args : ['--quiet', '-DGPLUGIN_GTK_COMPILATION'])
    --- a/gplugin-gtk/reference/gplugin-gtk-docs.xml Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/reference/gplugin-gtk-docs.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -1,20 +1,21 @@
    <?xml version="1.0"?>
    -<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
    - "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
    -
    +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
    + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
    <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
    <!ENTITY version SYSTEM "version.xml">
    ]>
    <book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
    <bookinfo>
    - <title>GPlugin Gtk+ Reference Manual</title>
    + <title>GPlugin Gtk Reference Manual</title>
    <abstract>
    - <title>GPlugin Gtk+ &version;</title>
    + <title>GPluginGtk &version;</title>
    <para>
    - GPlugin Gtk+ is a GObject based library that implements a reusable plugin system that
    - supports loading plugins in other languages
    - via loaders. GPlugin also implements
    - dependencies among the plugins.
    + GPluginGtk is a collection of Gtk widgets to make it easier to
    + integrate GPlugin into applications.
    + </para>
    + <para>
    + The latest version of this documentation can be found on-line at
    + <ulink role="online-location" url="https://docs.pidgin.im/gplugin-gtk/latest">https://docs.pidgin.im/gplugin-gtk/latest/</ulink>.
    </para>
    </abstract>
    </bookinfo>
    @@ -41,10 +42,6 @@
    <title>Index of deprecated symbols</title>
    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
    </index>
    -<!-- <index id="api-index-2.11.0" role="2.11.0">
    - <title>Index of new symbols in 2.11.0</title>
    - <xi:include href="xml/api-index-2.11.0.xml"><xi:fallback /></xi:include>
    - </index>
    - -->
    +
    <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
    </book>
    --- a/gplugin-gtk/reference/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin-gtk/reference/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -15,7 +15,7 @@
    ]
    fixxref_args = [
    - '--extra-dir', join_paths(meson.current_build_dir(), '../../gplugin/reference')
    + '--extra-dir=' + (meson.build_root() / 'gplugin/reference/html')
    ]
    gplugin_gtk_version_xml = configure_file(
    @@ -28,6 +28,7 @@
    gnome.gtkdoc(DOC_MODULE,
    main_xml : DOC_MODULE + '-docs.xml',
    + namespace : 'gplugin_gtk',
    src_dir : gplugin_gtk_inc,
    dependencies : gplugin_gtk_dep,
    ignore_headers : ignore_hfiles,
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin-query/gplugin-query.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,368 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <string.h>
    +
    +#include <glib.h>
    +#include <glib/gi18n.h>
    +
    +#include <gplugin.h>
    +
    +/******************************************************************************
    + * Globals
    + *****************************************************************************/
    +static gint verbosity = 0;
    +static gboolean show_internal = FALSE;
    +static gboolean output_paths = FALSE;
    +static gboolean exit_early = FALSE;
    +
    +/******************************************************************************
    + * Helpers
    + *****************************************************************************/
    +static gboolean
    +verbosity_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    +{
    + verbosity++;
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +full_verbosity_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    +{
    + verbosity = 1 << 11;
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +internal_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    +{
    + show_internal = TRUE;
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +version_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    +{
    + printf("gplugin-query %s\n", GPLUGIN_VERSION);
    +
    + exit_early = TRUE;
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +list_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    +{
    + output_paths = TRUE;
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +output_plugin(const gchar *id)
    +{
    + GSList *plugins = NULL, *l = NULL;
    + gboolean first = TRUE, header_output = FALSE;
    +
    +#define FORMAT "%-13s"
    +#define MAIN_FORMAT_NEL " " FORMAT ": "
    +#define MAIN_FORMAT MAIN_FORMAT_NEL "%s\n"
    +#define STR_OR_EMPTY(str) ((str) ? (str) : "")
    +
    + plugins = gplugin_manager_find_plugins(id);
    + if(plugins == NULL) {
    + printf("%s not found\n", id);
    +
    + return FALSE;
    + }
    +
    + for(l = plugins; l; l = l->next) {
    + GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data);
    + GPluginPluginInfo *info = gplugin_plugin_get_info(plugin);
    + gboolean internal, loq;
    + guint32 abi_version;
    + gchar *name, *version;
    + gchar *license_id, *license_text, *license_url;
    + gchar *icon_name, *summary, *description, *category, *website;
    + gchar **authors, **dependencies;
    + gint i = 0;
    +
    + internal = gplugin_plugin_info_get_internal(info);
    +
    + if(!show_internal && internal)
    + continue;
    +
    + if(!header_output) {
    + printf("%s:\n", id);
    + header_output = TRUE;
    + }
    +
    + /* clang-format off */
    + g_object_get(
    + G_OBJECT(info),
    + "abi-version", &abi_version,
    + "load-on-query", &loq,
    + "name", &name,
    + "version", &version,
    + "license-id", &license_id,
    + "license-text", &license_text,
    + "license-url", &license_url,
    + "icon-name", &icon_name,
    + "summary", &summary,
    + "description", &description,
    + "category", &category,
    + "authors", &authors,
    + "website", &website,
    + "dependencies", &dependencies,
    + NULL);
    + /* clang-format on */
    +
    + if(!first)
    + printf("\n");
    +
    + printf(MAIN_FORMAT, "name", STR_OR_EMPTY(name));
    + if(verbosity > 0)
    + printf(MAIN_FORMAT, "category", STR_OR_EMPTY(category));
    + printf(MAIN_FORMAT, "version", STR_OR_EMPTY(version));
    + if(verbosity > 0) {
    + printf(MAIN_FORMAT, "license", STR_OR_EMPTY(license_id));
    + printf(MAIN_FORMAT, "license url", STR_OR_EMPTY(license_url));
    + }
    + printf(MAIN_FORMAT, "summary", STR_OR_EMPTY(summary));
    + if(verbosity > 0) {
    + printf(MAIN_FORMAT_NEL, "authors");
    + if(authors) {
    + for(i = 0; authors[i]; i++) {
    + if(i > 0)
    + printf(" ");
    + printf("%s\n", STR_OR_EMPTY(authors[i]));
    + }
    + } else {
    + printf("\n");
    + }
    + printf(MAIN_FORMAT, "website", STR_OR_EMPTY(website));
    + }
    + if(verbosity > 1) {
    + printf(
    + MAIN_FORMAT,
    + "filename",
    + STR_OR_EMPTY(gplugin_plugin_get_filename(plugin)));
    + }
    + if(verbosity > 2) {
    + GPluginLoader *loader = gplugin_plugin_get_loader(plugin);
    +
    + printf(MAIN_FORMAT_NEL "%08x\n", "abi version", abi_version);
    + printf(MAIN_FORMAT, "internal", (internal) ? "yes" : "no");
    + printf(MAIN_FORMAT, "load on query", (loq) ? "yes" : "no");
    + printf(MAIN_FORMAT, "loader", G_OBJECT_TYPE_NAME(loader));
    +
    + g_object_unref(G_OBJECT(loader));
    + }
    + if(verbosity > 0)
    + printf(MAIN_FORMAT, "description", STR_OR_EMPTY(description));
    + if(verbosity > 2) {
    + printf(MAIN_FORMAT_NEL, "dependencies");
    + if(dependencies) {
    + for(i = 0; dependencies[i]; i++) {
    + if(i > 0)
    + printf(" ");
    + printf("%s\n", STR_OR_EMPTY(dependencies[i]));
    + }
    + } else {
    + printf("\n");
    + }
    + }
    +
    + g_free(name);
    + g_free(version);
    + g_free(license_id);
    + g_free(license_text);
    + g_free(license_url);
    + g_free(icon_name);
    + g_free(summary);
    + g_free(description);
    + g_free(category);
    + g_strfreev(authors);
    + g_free(website);
    + g_strfreev(dependencies);
    + g_object_unref(G_OBJECT(info));
    +
    + if(first)
    + first = FALSE;
    + }
    +
    + g_slist_free_full(plugins, g_object_unref);
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +output_plugins(GList *plugins)
    +{
    + GList *l = NULL;
    + gboolean ret = TRUE, first = TRUE;
    +
    + for(l = plugins; l; l = l->next) {
    + if(!first)
    + printf("\n");
    +
    + if(!output_plugin(l->data))
    + ret = FALSE;
    +
    + if(first)
    + first = FALSE;
    + }
    +
    + return ret;
    +}
    +
    +/******************************************************************************
    + * Main Stuff
    + *****************************************************************************/
    +/* clang-format off */
    +static GOptionEntry entries[] = {
    + {
    + "internal", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    + internal_cb, N_("Show internal plugins"),
    + NULL,
    + }, {
    + "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    + verbosity_cb, N_("Increase verbosity"),
    + NULL,
    + }, {
    + "full-verbose", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    + full_verbosity_cb, N_("Increase verbosity to eleven"),
    + NULL,
    + }, {
    + "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    + version_cb, N_("Display the version and exit"),
    + NULL,
    + }, {
    + "list", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    + list_cb, N_("Display all search paths and exit"),
    + NULL,
    + }, {
    + NULL, 0, 0, 0, NULL, NULL, NULL,
    + }
    +};
    +/* clang-format on */
    +
    +gint
    +main(gint argc, gchar **argv)
    +{
    + GError *error = NULL;
    + GOptionContext *ctx = NULL;
    + GOptionGroup *group = NULL;
    + gint i = 0, ret = 0;
    +
    + gplugin_init();
    +
    + ctx = g_option_context_new("PLUGIN-ID...");
    + g_option_context_set_summary(ctx, _("Query installed plugins"));
    + g_option_context_set_translation_domain(ctx, GETTEXT_PACKAGE);
    + g_option_context_add_main_entries(ctx, entries, NULL);
    +
    + group = gplugin_get_option_group();
    + g_option_context_add_group(ctx, group);
    +
    + g_option_context_parse(ctx, &argc, &argv, &error);
    + g_option_context_free(ctx);
    +
    + if(error) {
    + fprintf(stderr, "%s\n", error->message);
    +
    + g_error_free(error);
    +
    + gplugin_uninit();
    +
    + return EXIT_FAILURE;
    + }
    +
    + if(output_paths) {
    + GList *path = NULL;
    +
    + for(path = gplugin_manager_get_paths(); path; path = path->next) {
    + printf("%s\n", (gchar *)path->data);
    + }
    +
    + exit_early = TRUE;
    + }
    +
    + if(exit_early) {
    + gplugin_uninit();
    +
    + return 0;
    + }
    +
    + gplugin_manager_refresh();
    +
    + /* check if the user gave us atleast one plugin, and output them */
    + if(argc > 1) {
    + GQueue *plugins = g_queue_new();
    +
    + for(i = 1; i < argc; i++) {
    + if(!argv[i])
    + continue;
    +
    + if(strlen(argv[i]) == 0)
    + continue;
    +
    + g_queue_push_tail(plugins, argv[i]);
    + }
    +
    + if(!output_plugins(plugins->head))
    + ret = EXIT_FAILURE;
    +
    + g_queue_free(plugins);
    + } else {
    + GList *plugins = gplugin_manager_list_plugins();
    +
    + if(!output_plugins(plugins))
    + ret = EXIT_FAILURE;
    +
    + g_list_free(plugins);
    + }
    +
    + gplugin_uninit();
    +
    + return ret;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin-query/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,17 @@
    +gplugin_query = executable('gplugin-query',
    + 'gplugin-query.c',
    + dependencies : [gplugin_dep, GLIB, GOBJECT],
    + install : true)
    +
    +if get_option('help2man')
    + custom_target('gplugin-query.1',
    + command : [help2man,
    + '--name=Query installed plugins', '--section=1',
    + '--help-option=--help-all', '--no-info',
    + '--output', '@OUTPUT@',
    + gplugin_query],
    + output : 'gplugin-query.1',
    + install : true,
    + install_dir : get_option('mandir') / 'man1')
    +endif
    +
    --- a/gplugin-version.sh.in Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1 +0,0 @@
    -export GPLUGIN_VERSION=@GPLUGIN_MAJOR_VERSION@.@GPLUGIN_MINOR_VERSION@.@GPLUGIN_MICRO_VERSION@
    --- a/gplugin/gplugin-core.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-core.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -16,20 +16,19 @@
    */
    #include <glib.h>
    -#include <glib/gi18n.h>
    #include <glib-object.h>
    +#include <glib/gi18n-lib.h>
    #include <gplugin/gplugin-core.h>
    +#include <gplugin/gplugin-plugin.h>
    #include <gplugin/gplugin-private.h>
    -#include <gplugin/gplugin-plugin.h>
    /**
    * SECTION:gplugin-core
    - * @Title: Core API
    - * @Short_description: the core api
    + * @title: Core API
    + * @short_description: the core API
    *
    - * This section contains the core api of gplugin, which includes #gplugin_init
    - * and #gplugin_uninit.
    + * This section contains the core API of GPlugin.
    */
    /******************************************************************************
    @@ -60,7 +59,8 @@
    * gplugin_init().
    */
    void
    -gplugin_init(void) {
    +gplugin_init(void)
    +{
    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    @@ -73,7 +73,7 @@
    * Uninitializes the GPlugin library
    */
    void
    -gplugin_uninit(void) {
    +gplugin_uninit(void)
    +{
    gplugin_manager_private_uninit();
    }
    -
    --- a/gplugin/gplugin-core.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-core.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -25,7 +25,7 @@
    #include <glib.h>
    #include <glib-object.h>
    -#define GPLUGIN_DOMAIN (g_quark_from_static_string("gplugin"))
    +#define GPLUGIN_DOMAIN (g_quark_from_static_string("gplugin"))
    G_BEGIN_DECLS
    @@ -35,4 +35,3 @@
    G_END_DECLS
    #endif /* GPLUGIN_CORE_H */
    -
    --- a/gplugin/gplugin-file-tree.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-file-tree.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -17,13 +17,14 @@
    #include "gplugin-file-tree.h"
    -#include <glib/gi18n.h>
    +#include <glib/gi18n-lib.h>
    /******************************************************************************
    * FileTreeEntry API
    *****************************************************************************/
    GPluginFileTreeEntry *
    -gplugin_file_tree_entry_new(const gchar *filename) {
    +gplugin_file_tree_entry_new(const gchar *filename)
    +{
    GPluginFileTreeEntry *entry = NULL;
    g_return_val_if_fail(filename, NULL);
    @@ -35,8 +36,8 @@
    /* we have to use e->filename, because g_utf8_strrchr returns a pointer
    * in the string given too it, and not a new copy.
    */
    - entry->extension = g_utf8_strrchr(entry->filename, -1,
    - g_utf8_get_char("."));
    + entry->extension =
    + g_utf8_strrchr(entry->filename, -1, g_utf8_get_char("."));
    /* if we have an extension, we need to iterate past the . */
    if(entry->extension)
    @@ -46,7 +47,8 @@
    }
    void
    -gplugin_file_tree_entry_free(GPluginFileTreeEntry *entry) {
    +gplugin_file_tree_entry_free(GPluginFileTreeEntry *entry)
    +{
    g_return_if_fail(entry);
    g_free(entry->filename);
    @@ -60,10 +62,11 @@
    * Helpers
    *****************************************************************************/
    static gboolean
    -gplugin_file_tree_free_helper(GNode *n, G_GNUC_UNUSED gpointer d) {
    +gplugin_file_tree_free_helper(GNode *n, G_GNUC_UNUSED gpointer d)
    +{
    GPluginFileTreeEntry *e = n->data;
    - if (e)
    + if(e)
    gplugin_file_tree_entry_free(e);
    return FALSE;
    @@ -81,7 +84,8 @@
    * GPluginFileTreeEntry instance for the files in that directory.
    */
    GNode *
    -gplugin_file_tree_new(GList *paths) {
    +gplugin_file_tree_new(GList *paths)
    +{
    GList *iter = NULL;
    GNode *root = NULL;
    @@ -97,9 +101,10 @@
    dir = g_dir_open(path, 0, &error);
    if(error) {
    - g_debug(_("Failed to open %s: %s"),
    - path,
    - (error->message) ? error->message : _("unknown failure"));
    + g_debug(
    + _("Failed to open %s: %s"),
    + path,
    + (error->message) ? error->message : _("unknown failure"));
    g_error_free(error);
    error = NULL;
    @@ -115,11 +120,16 @@
    /* now run through all of the files and add then to the dir node */
    while((filename = g_dir_read_name(dir)) != NULL) {
    GNode *file = NULL;
    + gchar *test_filename = g_build_filename(path, filename, NULL);
    - entry = gplugin_file_tree_entry_new(filename);
    - file = g_node_new(entry);
    + if(g_file_test(test_filename, G_FILE_TEST_IS_REGULAR)) {
    + entry = gplugin_file_tree_entry_new(filename);
    + file = g_node_new(entry);
    - g_node_prepend(node_dir, file);
    + g_node_prepend(node_dir, file);
    + }
    +
    + g_free(test_filename);
    }
    /* close the dir */
    @@ -136,13 +146,17 @@
    * Free's a previously created file tree.
    */
    void
    -gplugin_file_tree_free(GNode *root) {
    +gplugin_file_tree_free(GNode *root)
    +{
    g_return_if_fail(root);
    - g_node_traverse(root, G_POST_ORDER,
    - G_TRAVERSE_LEAVES | G_TRAVERSE_NON_LEAVES,
    - -1, gplugin_file_tree_free_helper, NULL);
    + g_node_traverse(
    + root,
    + G_POST_ORDER,
    + G_TRAVERSE_LEAVES | G_TRAVERSE_NON_LEAVES,
    + -1,
    + gplugin_file_tree_free_helper,
    + NULL);
    g_node_destroy(root);
    }
    -
    --- a/gplugin/gplugin-file-tree.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-file-tree.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -33,4 +33,3 @@
    G_END_DECLS
    #endif /* GPLUGIN_FILE_TREE_H */
    -
    --- a/gplugin/gplugin-loader-tests.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-loader-tests.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,7 +19,6 @@
    #include <stdlib.h>
    #include <glib.h>
    -#include <glib/gi18n.h>
    #include <gplugin.h>
    @@ -35,13 +34,14 @@
    * Tests
    *****************************************************************************/
    static void
    -gplugin_test_loader_full(gconstpointer d) {
    +gplugin_test_loader_full(gconstpointer d)
    +{
    GPluginPlugin *plugin = NULL;
    GPluginPluginInfo *info = NULL;
    GError *error = NULL;
    gchar *id = NULL;
    gchar **authors = NULL;
    - const gchar * const r_authors[] = { "author1", NULL };
    + const gchar *const r_authors[] = {"author1", NULL};
    gint i;
    id = g_strdup_printf("gplugin/%s-basic-plugin", (const gchar *)d);
    @@ -54,8 +54,7 @@
    g_assert_cmpstr(gplugin_plugin_info_get_id(info), ==, id);
    g_free(id);
    - g_assert_cmpuint(gplugin_plugin_info_get_abi_version(info), ==,
    - 0x01020304);
    + g_assert_cmpuint(gplugin_plugin_info_get_abi_version(info), ==, 0x01020304);
    g_assert_cmpstr(gplugin_plugin_info_get_name(info), ==, "basic plugin");
    authors = (gchar **)gplugin_plugin_info_get_authors(info);
    @@ -66,30 +65,39 @@
    g_assert_cmpstr(gplugin_plugin_info_get_version(info), ==, "version");
    g_assert_cmpstr(gplugin_plugin_info_get_license_id(info), ==, "license");
    g_assert_cmpstr(gplugin_plugin_info_get_summary(info), ==, "summary");
    - g_assert_cmpstr(gplugin_plugin_info_get_description(info), ==,
    - "description");
    + g_assert_cmpstr(
    + gplugin_plugin_info_get_description(info),
    + ==,
    + "description");
    g_assert_cmpstr(gplugin_plugin_info_get_website(info), ==, "website");
    g_object_unref(G_OBJECT(info));
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_QUERIED);
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_QUERIED);
    gplugin_manager_load_plugin(plugin, &error);
    g_assert_no_error(error);
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_LOADED);
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_LOADED);
    gplugin_manager_unload_plugin(plugin, &error);
    g_assert_no_error(error);
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_QUERIED);
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_QUERIED);
    g_object_unref(G_OBJECT(plugin));
    }
    static void
    -gplugin_test_loader_load_failed(gconstpointer d) {
    +gplugin_test_loader_load_failed(gconstpointer d)
    +{
    GPluginPlugin *plugin = NULL;
    GError *error = NULL;
    gchar *id = NULL;
    @@ -102,15 +110,26 @@
    ret = gplugin_manager_load_plugin(plugin, &error);
    g_assert_false(ret);
    +
    g_assert_error(error, GPLUGIN_DOMAIN, 0);
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    + g_clear_error(&error);
    +
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    +
    + /* make sure that the plugin's error was set */
    + error = gplugin_plugin_get_error(plugin);
    + g_assert_error(error, GPLUGIN_DOMAIN, 0);
    + g_clear_error(&error);
    g_object_unref(G_OBJECT(plugin));
    }
    static void
    -gplugin_test_loader_load_exception(gconstpointer d) {
    +gplugin_test_loader_load_exception(gconstpointer d)
    +{
    GPluginPlugin *plugin = NULL;
    GError *error = NULL;
    gchar *id = NULL;
    @@ -123,15 +142,26 @@
    ret = gplugin_manager_load_plugin(plugin, &error);
    g_assert_false(ret);
    +
    g_assert_error(error, GPLUGIN_DOMAIN, 0);
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    + g_clear_error(&error);
    +
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    +
    + /* make sure that the plugin's error was set */
    + error = gplugin_plugin_get_error(plugin);
    + g_assert_error(error, GPLUGIN_DOMAIN, 0);
    + g_clear_error(&error);
    g_object_unref(G_OBJECT(plugin));
    }
    static void
    -gplugin_test_loader_unload_failed(gconstpointer d) {
    +gplugin_test_loader_unload_failed(gconstpointer d)
    +{
    GPluginPlugin *plugin = NULL;
    GError *error = NULL;
    gchar *id = NULL;
    @@ -145,25 +175,38 @@
    ret = gplugin_manager_load_plugin(plugin, &error);
    g_assert_true(ret);
    g_assert_no_error(error);
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_LOADED);
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_LOADED);
    ret = gplugin_manager_unload_plugin(plugin, &error);
    g_assert_false(ret);
    +
    g_assert_error(error, GPLUGIN_DOMAIN, 0);
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_LOADED);
    + g_clear_error(&error);
    +
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_UNLOAD_FAILED);
    +
    + /* make sure that the plugin's error was set */
    + error = gplugin_plugin_get_error(plugin);
    + g_assert_error(error, GPLUGIN_DOMAIN, 0);
    + g_clear_error(&error);
    g_object_unref(G_OBJECT(plugin));
    }
    static void
    -gplugin_test_loader_dependencies(gconstpointer d) {
    +gplugin_test_loader_dependencies(gconstpointer d)
    +{
    GPluginPlugin *plugin = NULL;
    GPluginPluginInfo *info = NULL;
    gchar *id = NULL;
    gchar **deps = NULL;
    - const gchar * const r_deps[] = { "dependency1", "dependency2", NULL };
    + const gchar *const r_deps[] = {"dependency1", "dependency2", NULL};
    gint i = 0;
    id = g_strdup_printf("gplugin/%s-dependent-plugin", (const gchar *)d);
    @@ -185,16 +228,17 @@
    * Main
    *****************************************************************************/
    static GPluginTestLoaderFunction test_functions[] = {
    - { "/loaders/%s/full", gplugin_test_loader_full },
    - { "/loaders/%s/load-failed", gplugin_test_loader_load_failed },
    - { "/loaders/%s/load-exception", gplugin_test_loader_load_exception },
    - { "/loaders/%s/unload-failed", gplugin_test_loader_unload_failed },
    - { "/loaders/%s/dependencies", gplugin_test_loader_dependencies },
    - { NULL, NULL },
    + {"/loaders/%s/full", gplugin_test_loader_full},
    + {"/loaders/%s/load-failed", gplugin_test_loader_load_failed},
    + {"/loaders/%s/load-exception", gplugin_test_loader_load_exception},
    + {"/loaders/%s/unload-failed", gplugin_test_loader_unload_failed},
    + {"/loaders/%s/dependencies", gplugin_test_loader_dependencies},
    + {NULL, NULL},
    };
    void
    -gplugin_loader_tests_add_tests(const gchar *short_name) {
    +gplugin_loader_tests_add_tests(const gchar *short_name)
    +{
    gint i = 0;
    for(i = 0; test_functions[i].path; i++) {
    @@ -207,8 +251,10 @@
    }
    void
    -gplugin_loader_tests_main(const gchar *loader_dir, const gchar *plugin_dir,
    - const gchar *short_name)
    +gplugin_loader_tests_main(
    + const gchar *loader_dir,
    + const gchar *plugin_dir,
    + const gchar *short_name)
    {
    gplugin_init();
    @@ -224,4 +270,3 @@
    gplugin_loader_tests_add_tests(short_name);
    }
    -
    --- a/gplugin/gplugin-loader-tests.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-loader-tests.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -24,9 +24,11 @@
    G_BEGIN_DECLS
    void gplugin_loader_tests_add_tests(const gchar *short_name);
    -void gplugin_loader_tests_main(const gchar *loader_dir, const gchar *plugin_dir, const gchar *short_name);
    +void gplugin_loader_tests_main(
    + const gchar *loader_dir,
    + const gchar *plugin_dir,
    + const gchar *short_name);
    G_END_DECLS
    #endif /* GPLUGIN_OPTIONS_H */
    -
    --- a/gplugin/gplugin-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,9 +15,16 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <gplugin/gplugin-core.h>
    #include <gplugin/gplugin-loader.h>
    -#include <gplugin/gplugin-core.h>
    +/**
    + * SECTION:gplugin-loader
    + * @title: Plugin Loader
    + * @short_description: Abstract class for loading plugins
    + *
    + * GPluginLoader defines the base behavior for loaders of all languages.
    + */
    /**
    * GPLUGIN_TYPE_LOADER:
    @@ -26,18 +33,9 @@
    */
    /**
    - * SECTION:gplugin-loader
    - * @Title: Plugin Loader Interface
    - * @Short_description: interface for loading plugins
    - *
    - * A PluginLoader has to implement the interface described here for GPlugin to
    - * be able to use it to load plugins.
    - */
    -
    -/**
    * GPluginLoader:
    *
    - * An abstract data type that should not be accessed directly.
    + * An abstract class that should not be accessed directly.
    */
    /**
    @@ -52,20 +50,22 @@
    * @unload: The unload vfunc is called when the plugin manager wants to unload
    * a previously loaded plugin from this loader.
    *
    - * #GPluginLoader class defines the behavior for loading plugins.
    + * #GPluginLoaderClass defines the behavior for loading plugins.
    */
    /******************************************************************************
    - * GObject Stuff
    + * GObject Implementation
    *****************************************************************************/
    G_DEFINE_ABSTRACT_TYPE(GPluginLoader, gplugin_loader, G_TYPE_OBJECT);
    static void
    -gplugin_loader_init(G_GNUC_UNUSED GPluginLoader *loader) {
    +gplugin_loader_init(G_GNUC_UNUSED GPluginLoader *loader)
    +{
    }
    static void
    -gplugin_loader_class_init(G_GNUC_UNUSED GPluginLoaderClass *klass) {
    +gplugin_loader_class_init(G_GNUC_UNUSED GPluginLoaderClass *klass)
    +{
    }
    /******************************************************************************
    @@ -74,18 +74,21 @@
    /**
    * gplugin_loader_query_plugin:
    - * @loader: #GPluginLoader instance performing the query
    - * @filename: filename to query
    - * @error: return location for a GError, or NULL
    + * @loader: The #GPluginLoader instance performing the query.
    + * @filename: The filename to query.
    + * @error: (nullable): The return location for a #GError, or %NULL.
    *
    - * This function is called by the plugin manager to ask a loader to query the
    - * given file and determine if it's a usable plugin.
    + * This function is called by the plugin manager to ask @loader to query
    + * @filename and determine if it's a usable plugin.
    *
    - * Return value: (transfer full): A #GPluginPlugin instance or NULL on failure
    + * Return value: (transfer full): A #GPluginPlugin instance or %NULL on
    + * failure.
    */
    GPluginPlugin *
    -gplugin_loader_query_plugin(GPluginLoader *loader,
    - const gchar *filename, GError **error)
    +gplugin_loader_query_plugin(
    + GPluginLoader *loader,
    + const gchar *filename,
    + GError **error)
    {
    GPluginLoaderClass *klass = NULL;
    @@ -104,18 +107,20 @@
    /**
    * gplugin_loader_load_plugin:
    - * @loader: #GPluginLoader instance performing the load
    - * @plugin: #GPluginPlugin instance to load
    - * @error: return location for a GError, or NULL
    + * @loader: The #GPluginLoader instance performing the load.
    + * @plugin: The #GPluginPlugin instance to load.
    + * @error: (nullable): The return location for a #GError, or %NULL.
    *
    - * This function is called by the plugin manager to ask a loader to load the
    - * given plugin.
    + * This function is called by the plugin manager to ask @loader to load
    + * @plugin.
    *
    - * Return value: TRUE if @plugin was loaded successfully, FALSE otherwise
    + * Return value: %TRUE if @plugin was loaded successfully, %FALSE otherwise.
    */
    gboolean
    -gplugin_loader_load_plugin(GPluginLoader *loader,
    - GPluginPlugin *plugin, GError **error)
    +gplugin_loader_load_plugin(
    + GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    GPluginLoaderClass *klass = NULL;
    gboolean ret = FALSE;
    @@ -129,9 +134,12 @@
    if(klass && klass->load)
    ret = klass->load(loader, plugin, error);
    - if (!ret && error && *error == NULL) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0,
    - "Failed to load plugin : unknown");
    + if(!ret && error && *error == NULL) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "Failed to load plugin : unknown");
    }
    return ret;
    @@ -139,18 +147,20 @@
    /**
    * gplugin_loader_unload_plugin:
    - * @loader: #GPluginLoader instance performing the unload
    - * @plugin: #GPluginPlugin instance to unload
    - * @error: return location for a GError, or NULL
    + * @loader: The #GPluginLoader instance performing the unload.
    + * @plugin: The #GPluginPlugin instance to unload.
    + * @error: (nullable): The return location for a #GError, or %NULL.
    *
    - * This function is called by the plugin manager to ask a loader to unload the
    - * given plugin.
    + * This function is called by the plugin manager to ask @loader to unload
    + * @plugin.
    *
    - * Return value: TRUE if @plugin was unloaded successfully, FALSE otherwise
    + * Return value: %TRUE if @plugin was unloaded successfully, %FALSE otherwise.
    */
    gboolean
    -gplugin_loader_unload_plugin(GPluginLoader *loader,
    - GPluginPlugin *plugin, GError **error)
    +gplugin_loader_unload_plugin(
    + GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    GPluginLoaderClass *klass = NULL;
    gboolean ret = FALSE;
    @@ -164,30 +174,39 @@
    if(klass && klass->unload)
    ret = klass->unload(loader, plugin, error);
    - if (!ret && error && *error == NULL) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0,
    - "Failed to unload plugin : unknown");
    + if(!ret && error && *error == NULL) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "Failed to unload plugin : unknown");
    }
    return ret;
    }
    /**
    - * gplugin_loader_class_get_supported_extensions:
    - * @klass: #GPluginLoader instance
    + * gplugin_loader_get_supported_extensions:
    + * @loader: The #GPluginLoader instance.
    *
    - * Returns a #GSList of string for which extensions the loader supports.
    + * Returns a #GSList of strings containing the extensions that the loader
    + * supports. Each extension should not include the dot. For example: so,
    + * dll, py, etc.
    *
    * Return value: (element-type utf8) (transfer container): A #GSList of
    * extensions that the loader supports.
    */
    GSList *
    -gplugin_loader_class_get_supported_extensions(GPluginLoaderClass *klass) {
    - g_return_val_if_fail(GPLUGIN_IS_LOADER_CLASS(klass), NULL);
    +gplugin_loader_get_supported_extensions(GPluginLoader *loader)
    +{
    + GPluginLoaderClass *klass = NULL;
    - if(klass->supported_extensions)
    - return klass->supported_extensions(klass);
    + g_return_val_if_fail(GPLUGIN_IS_LOADER(loader), NULL);
    +
    + klass = GPLUGIN_LOADER_GET_CLASS(loader);
    + if(klass != NULL && klass->supported_extensions) {
    + return klass->supported_extensions(loader);
    + }
    return NULL;
    }
    -
    --- a/gplugin/gplugin-loader.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-loader.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -28,7 +28,12 @@
    G_BEGIN_DECLS
    #define GPLUGIN_TYPE_LOADER (gplugin_loader_get_type())
    -G_DECLARE_DERIVABLE_TYPE(GPluginLoader, gplugin_loader, GPLUGIN, LOADER, GObject)
    +G_DECLARE_DERIVABLE_TYPE(
    + GPluginLoader,
    + gplugin_loader,
    + GPLUGIN,
    + LOADER,
    + GObject)
    /* circular include so this needs to be after the G_DECLARE_* marcro */
    #include <gplugin/gplugin-plugin.h>
    @@ -38,25 +43,36 @@
    GObjectClass gparent;
    /*< public >*/
    - GSList *(*supported_extensions)(GPluginLoaderClass *klass);
    + GSList *(*supported_extensions)(GPluginLoader *loader);
    +
    + GPluginPlugin *(
    + *query)(GPluginLoader *loader, const gchar *filename, GError **error);
    - GPluginPlugin *(*query)(GPluginLoader *loader, const gchar *filename, GError **error);
    -
    - gboolean (*load)(GPluginLoader *loader, GPluginPlugin *plugin, GError **error);
    - gboolean (*unload)(GPluginLoader *loader, GPluginPlugin *plugin, GError **error);
    + gboolean (
    + *load)(GPluginLoader *loader, GPluginPlugin *plugin, GError **error);
    + gboolean (
    + *unload)(GPluginLoader *loader, GPluginPlugin *plugin, GError **error);
    /*< private >*/
    gpointer reserved[4];
    };
    -GSList *gplugin_loader_class_get_supported_extensions(GPluginLoaderClass *klass);
    +GSList *gplugin_loader_get_supported_extensions(GPluginLoader *loader);
    +
    +GPluginPlugin *gplugin_loader_query_plugin(
    + GPluginLoader *loader,
    + const gchar *filename,
    + GError **error);
    -GPluginPlugin *gplugin_loader_query_plugin(GPluginLoader *loader, const gchar *filename, GError **error);
    -
    -gboolean gplugin_loader_load_plugin(GPluginLoader *loader, GPluginPlugin *plugin, GError **error);
    -gboolean gplugin_loader_unload_plugin(GPluginLoader *loader, GPluginPlugin *plugin, GError **error);
    +gboolean gplugin_loader_load_plugin(
    + GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error);
    +gboolean gplugin_loader_unload_plugin(
    + GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error);
    G_END_DECLS
    #endif /* GPLUGIN_LOADER_H */
    -
    --- a/gplugin/gplugin-manager.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-manager.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,17 +19,14 @@
    #include <string.h>
    #include <glib.h>
    -#include <glib/gi18n.h>
    -
    -#include <gplugin/gplugin-manager.h>
    -#include <gplugin/gplugin-core.h>
    +#include <glib/gi18n-lib.h>
    +#include <gplugin-native.h>
    +#include <gplugin/gplugin-core.h>
    +#include <gplugin/gplugin-file-tree.h>
    +#include <gplugin/gplugin-manager.h>
    #include <gplugin/gplugin-private.h>
    -#include <gplugin/gplugin-native.h>
    -
    -#include <gplugin/gplugin-file-tree.h>
    -
    /**
    * SECTION:gplugin-manager
    * @Title: Manager API
    @@ -39,6 +36,15 @@
    * loading, unloading, querying, checking for new plugins, and so on.
    */
    +/**
    + * GPluginManagerForeachFunc:
    + * @id: The id of the plugin.
    + * @plugins: A #GSList of each plugin that has the id @id.
    + * @data: User data passed to gplugin_manager_foreach().
    + *
    + * A callback function for gplugin_manager_foreach().
    + */
    +
    /******************************************************************************
    * Enums
    *****************************************************************************/
    @@ -48,6 +54,7 @@
    SIG_LOAD_FAILED,
    SIG_UNLOADING,
    SIG_UNLOADED,
    + SIG_UNLOAD_FAILED,
    N_SIGNALS,
    };
    @@ -61,7 +68,8 @@
    GHashTable *plugins;
    GHashTable *plugins_filename_view;
    - GHashTable *loaders;
    + GSList *loaders;
    + GHashTable *loaders_by_extension;
    gboolean refresh_needed;
    } GPluginManager;
    @@ -76,47 +84,76 @@
    GList *(*get_paths)(GPluginManager *manager);
    - void (*register_loader)(GPluginManager *manager, GType type);
    - void (*unregister_loader)(GPluginManager *manager, GType type);
    + gboolean (
    + *register_loader)(GPluginManager *manager, GType type, GError **error);
    + gboolean (*unregister_loader)(
    + GPluginManager *manager,
    + GType type,
    + GError **error);
    void (*refresh)(GPluginManager *manager);
    + void (*foreach)(
    + GPluginManager *manager,
    + GPluginManagerForeachFunc func,
    + gpointer data);
    +
    GSList *(*find_plugins)(GPluginManager *manager, const gchar *id);
    + GSList *(*find_plugins_with_state)(
    + GPluginManager *manager,
    + GPluginPluginState state);
    GList *(*list_plugins)(GPluginManager *manager);
    - GSList *(*get_plugin_dependencies)(GPluginManager *manager, GPluginPlugin *plugin, GError **error);
    + GSList *(*get_plugin_dependencies)(
    + GPluginManager *manager,
    + GPluginPlugin *plugin,
    + GError **error);
    - gboolean (*load_plugin)(GPluginManager *manager,
    - GPluginPlugin *plugin,
    - GError **error);
    - gboolean (*unload_plugin)(GPluginManager *manager,
    - GPluginPlugin *plugin,
    - GError **error);
    + gboolean (*load_plugin)(
    + GPluginManager *manager,
    + GPluginPlugin *plugin,
    + GError **error);
    + gboolean (*unload_plugin)(
    + GPluginManager *manager,
    + GPluginPlugin *plugin,
    + GError **error);
    /* signals */
    - gboolean (*loading_plugin)(GObject *manager,
    - GPluginPlugin *plugin,
    - GError **error);
    - void (*loaded_plugin)(GObject *manager,
    - GPluginPlugin *plugin);
    + gboolean (*loading_plugin)(
    + GObject *manager,
    + GPluginPlugin *plugin,
    + GError **error);
    + void (*loaded_plugin)(GObject *manager, GPluginPlugin *plugin);
    void (*load_failed)(GObject *manager, GPluginPlugin *plugin);
    - gboolean (*unloading_plugin)(GObject *manager,
    - GPluginPlugin *plugin,
    - GError **error);
    - void (*unloaded_plugin)(GObject *manager,
    - GPluginPlugin *plugin);
    -
    + gboolean (*unloading_plugin)(
    + GObject *manager,
    + GPluginPlugin *plugin,
    + GError **error);
    + void (*unloaded_plugin)(GObject *manager, GPluginPlugin *plugin);
    + void (*unload_plugin_failed)(GObject *manager, GPluginPlugin *plugin);
    } GPluginManagerClass;
    -#define GPLUGIN_TYPE_MANAGER (gplugin_manager_get_type())
    -#define GPLUGIN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GPLUGIN_TYPE_MANAGER, GPluginManager))
    -#define GPLUGIN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GPLUGIN_TYPE_MANAGER, GPluginManagerClass))
    -#define GPLUGIN_IS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GPLUGIN_TYPE_MANAGER))
    -#define GPLUGIN_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GPLUGIN_TYPE_MANAGER))
    -#define GPLUGIN_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GPLUGIN_TYPE_MANAGER, GPluginManagerClass))
    +#define GPLUGIN_TYPE_MANAGER (gplugin_manager_get_type())
    +#define GPLUGIN_MANAGER(obj) \
    + (G_TYPE_CHECK_INSTANCE_CAST((obj), GPLUGIN_TYPE_MANAGER, GPluginManager))
    +#define GPLUGIN_MANAGER_CLASS(klass) \
    + (G_TYPE_CHECK_CLASS_CAST( \
    + (klass), \
    + GPLUGIN_TYPE_MANAGER, \
    + GPluginManagerClass))
    +#define GPLUGIN_IS_MANAGER(obj) \
    + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GPLUGIN_TYPE_MANAGER))
    +#define GPLUGIN_IS_MANAGER_CLASS(klass) \
    + (G_TYPE_CHECK_CLASS_TYPE((klass), GPLUGIN_TYPE_MANAGER))
    +#define GPLUGIN_MANAGER_GET_CLASS(obj) \
    + (G_TYPE_INSTANCE_GET_CLASS( \
    + (obj), \
    + GPLUGIN_TYPE_MANAGER, \
    + GPluginManagerClass))
    -#define GPLUGIN_MANAGER_INSTANCE (GPLUGIN_MANAGER(gplugin_manager_get_instance()))
    +#define GPLUGIN_MANAGER_INSTANCE \
    + (GPLUGIN_MANAGER(gplugin_manager_get_instance()))
    G_DEFINE_TYPE(GPluginManager, gplugin_manager, G_TYPE_OBJECT);
    @@ -124,15 +161,19 @@
    * Globals
    *****************************************************************************/
    GPluginManager *instance = NULL;
    -static guint signals[N_SIGNALS] = {0, };
    -const gchar *dependency_pattern = "^(?P<id>.+?)((?P<op>\\<=|\\<|==|=|\\>=|\\>)(?P<version>.+))?$";
    +static guint signals[N_SIGNALS] = {
    + 0,
    +};
    +const gchar *dependency_pattern =
    + "^(?P<id>.+?)((?P<op>\\<=|\\<|==|=|\\>=|\\>)(?P<version>.+))?$";
    GRegex *dependency_regex = NULL;
    /******************************************************************************
    * Helpers
    *****************************************************************************/
    static guint
    -gplugin_manager_str_hash(gconstpointer v) {
    +gplugin_manager_str_hash(gconstpointer v)
    +{
    if(v == NULL)
    return g_str_hash("");
    @@ -140,9 +181,10 @@
    }
    static gboolean
    -gplugin_manager_remove_list_value(G_GNUC_UNUSED gpointer k,
    - gpointer v,
    - G_GNUC_UNUSED gpointer d)
    +gplugin_manager_remove_list_value(
    + G_GNUC_UNUSED gpointer k,
    + gpointer v,
    + G_GNUC_UNUSED gpointer d)
    {
    GSList *l = NULL;
    @@ -156,100 +198,212 @@
    return TRUE;
    }
    +/*
    + * gplugin_manager_find_loader_by_type:
    + * @manager: The #GPluginManager instance.
    + * @type: The #GType of the loader to find.
    + *
    + * Looks up a #GPluginLoader instance by its type.
    + *
    + * Returns: (transfer none): The #GPluginLoader instance or %NULL.
    + */
    +static GPluginLoader *
    +gplugin_manager_find_loader_by_type(GPluginManager *manager, GType type)
    +{
    + GSList *l = NULL;
    +
    + g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL);
    + g_return_val_if_fail(g_type_is_a(type, GPLUGIN_TYPE_LOADER), NULL);
    +
    + for(l = manager->loaders; l; l = l->next) {
    + if(G_OBJECT_TYPE(l->data) == type) {
    + return GPLUGIN_LOADER(l->data);
    + }
    + }
    +
    + return NULL;
    +}
    +
    +static void
    +gplugin_manager_foreach_unload_plugin(
    + gpointer key,
    + gpointer value,
    + G_GNUC_UNUSED gpointer data)
    +{
    + GList *l = NULL;
    + gchar *id = (gchar *)key;
    +
    + for(l = (GList *)value; l; l = l->next) {
    + GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data);
    + GPluginLoader *loader = NULL;
    + GError *error = NULL;
    +
    + if(gplugin_plugin_get_state(plugin) != GPLUGIN_PLUGIN_STATE_LOADED) {
    + continue;
    + }
    +
    + loader = gplugin_plugin_get_loader(plugin);
    + if(!gplugin_loader_unload_plugin(loader, plugin, &error)) {
    + g_warning(
    + "failed to unload plugin with id %s: %s",
    + id,
    + error ? error->message : "unknown");
    + g_clear_error(&error);
    + }
    + g_object_unref(G_OBJECT(loader));
    + }
    +}
    +
    +static gchar *
    +gplugin_manager_normalize_path(const gchar *path)
    +{
    + if(g_str_has_suffix(path, G_DIR_SEPARATOR_S)) {
    + return g_strdup(path);
    + }
    +
    + return g_strdup_printf("%s%s", path, G_DIR_SEPARATOR_S);
    +}
    +
    +static gint
    +gplugin_manager_compare_paths(gconstpointer a, gconstpointer b)
    +{
    + gchar *keya = NULL, *keyb = NULL;
    + gint r = 0;
    +
    + keya = g_utf8_collate_key_for_filename((const gchar *)a, -1);
    + keyb = g_utf8_collate_key_for_filename((const gchar *)b, -1);
    +
    + r = strcmp(keya, keyb);
    +
    + g_free(keya);
    + g_free(keyb);
    +
    + return r;
    +}
    +
    /******************************************************************************
    * Manager implementation
    *****************************************************************************/
    static void
    -gplugin_manager_real_append_path(GPluginManager *manager,
    - const gchar *path)
    +gplugin_manager_real_append_path(GPluginManager *manager, const gchar *path)
    {
    GList *l = NULL;
    + gchar *normalized = NULL;
    - if(!path)
    + if(!path) {
    return;
    + }
    +
    + normalized = gplugin_manager_normalize_path(path);
    - for(l = manager->paths->head; l; l = l->next)
    - if(strcmp(l->data, path) == 0)
    - return;
    -
    - g_queue_push_tail(manager->paths, g_strdup(path));
    + l = g_queue_find_custom(
    + manager->paths,
    + normalized,
    + gplugin_manager_compare_paths);
    + if(l == NULL) {
    + g_queue_push_tail(manager->paths, normalized);
    + } else {
    + g_free(normalized);
    + }
    }
    static void
    -gplugin_manager_real_prepend_path(GPluginManager *manager,
    - const gchar *path)
    +gplugin_manager_real_prepend_path(GPluginManager *manager, const gchar *path)
    {
    GList *l = NULL;
    + gchar *normalized = NULL;
    - if(!path)
    + if(!path) {
    return;
    + }
    +
    + normalized = gplugin_manager_normalize_path(path);
    - for(l = manager->paths->head; l; l = l->next)
    - if(strcmp(l->data, path) == 0)
    - return;
    -
    - g_queue_push_head(manager->paths, g_strdup(path));
    + l = g_queue_find_custom(
    + manager->paths,
    + normalized,
    + gplugin_manager_compare_paths);
    + if(l == NULL) {
    + g_queue_push_head(manager->paths, normalized);
    + } else {
    + g_free(normalized);
    + }
    }
    static void
    -gplugin_manager_real_remove_path(GPluginManager *manager,
    - const gchar *path)
    +gplugin_manager_real_remove_path(GPluginManager *manager, const gchar *path)
    {
    - GList *l = NULL, *link = NULL;
    + GList *l = NULL;
    + gchar *normalized = NULL;
    g_return_if_fail(path != NULL);
    - for(l = manager->paths->head; l; l = l->next)
    - if(strcmp(l->data, path) == 0) {
    - g_free(l->data);
    - link = l;
    - }
    + normalized = gplugin_manager_normalize_path(path);
    - if(link)
    - g_queue_delete_link(manager->paths, link);
    + l = g_queue_find_custom(
    + manager->paths,
    + normalized,
    + gplugin_manager_compare_paths);
    + if(l != NULL) {
    + g_free(l->data);
    + g_queue_delete_link(manager->paths, l);
    + }
    +
    + g_free(normalized);
    }
    static void
    -gplugin_manager_real_remove_paths(GPluginManager *manager) {
    - g_queue_clear(manager->paths);
    +gplugin_manager_real_remove_paths(GPluginManager *manager)
    +{
    + g_queue_clear_full(manager->paths, g_free);
    }
    static GList *
    -gplugin_manager_real_get_paths(GPluginManager *manager) {
    +gplugin_manager_real_get_paths(GPluginManager *manager)
    +{
    return manager->paths->head;
    }
    -static void
    -gplugin_manager_real_register_loader(GPluginManager *manager,
    - GType type)
    +static gboolean
    +gplugin_manager_real_register_loader(
    + GPluginManager *manager,
    + GType type,
    + GError **error)
    {
    GPluginLoader *loader = NULL;
    - GPluginLoaderClass *lo_class = NULL;
    - GSList *l = NULL;
    + GSList *l = NULL, *exts = NULL;
    +
    + g_return_val_if_fail(g_type_is_a(type, GPLUGIN_TYPE_LOADER), FALSE);
    - g_return_if_fail(g_type_is_a(type, GPLUGIN_TYPE_LOADER));
    + loader = gplugin_manager_find_loader_by_type(manager, type);
    + if(GPLUGIN_IS_LOADER(loader)) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("loader %s was already registered"),
    + g_type_name(type));
    + return FALSE;
    + }
    /* Create the loader instance first. If we can't create it, we bail */
    loader = g_object_new(type, NULL);
    if(!GPLUGIN_IS_LOADER(loader)) {
    - g_warning(_("failed to create loader instance for %s"),
    - g_type_name(type));
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("failed to create loader instance for %s"),
    + g_type_name(type));
    - return;
    + return FALSE;
    }
    - /* grab the class of the loader */
    - lo_class = GPLUGIN_LOADER_GET_CLASS(loader);
    - if(!lo_class) {
    - g_warning(_("failed to get the loader class for %s"), g_type_name(type));
    - g_object_unref(G_OBJECT(loader));
    + manager->loaders =
    + g_slist_prepend(manager->loaders, g_object_ref(G_OBJECT(loader)));
    - return;
    - }
    -
    - for(l = gplugin_loader_class_get_supported_extensions(lo_class);
    - l; l = l->next)
    - {
    + exts = gplugin_loader_get_supported_extensions(loader);
    + for(l = exts; l; l = l->next) {
    GSList *existing = NULL, *ll = NULL;
    const gchar *ext = (const gchar *)l->data;
    @@ -257,7 +411,7 @@
    * we can prepend our loader. But before we add ours, we remove any
    * old copies we might have of ours.
    */
    - existing = g_hash_table_lookup(manager->loaders, ext);
    + existing = g_hash_table_lookup(manager->loaders_by_extension, ext);
    for(ll = existing; ll; ll = ll->next) {
    if(G_OBJECT_TYPE(ll->data) == type) {
    GPluginLoader *old = GPLUGIN_LOADER(ll->data);
    @@ -273,40 +427,56 @@
    existing = g_slist_prepend(existing, g_object_ref(G_OBJECT(loader)));
    /* Now insert the updated slist back into the hash table */
    - g_hash_table_insert(manager->loaders, g_strdup(ext), existing);
    + g_hash_table_insert(
    + manager->loaders_by_extension,
    + g_strdup(ext),
    + existing);
    }
    + g_slist_free(exts);
    /* make a note that we need to refresh */
    manager->refresh_needed = TRUE;
    /* we remove our initial reference from the loader now to avoid a leak */
    g_object_unref(G_OBJECT(loader));
    +
    + return TRUE;
    }
    -static void
    -gplugin_manager_real_unregister_loader(GPluginManager *manager,
    - GType type)
    +static gboolean
    +gplugin_manager_real_unregister_loader(
    + GPluginManager *manager,
    + GType type,
    + GError **error)
    {
    - GPluginLoaderClass *klass = NULL;
    - GSList *exts = NULL;
    + GPluginLoader *loader = NULL;
    + GSList *l = NULL, *exts = NULL;
    - g_return_if_fail(g_type_is_a(type, GPLUGIN_TYPE_LOADER));
    + g_return_val_if_fail(g_type_is_a(type, GPLUGIN_TYPE_LOADER), FALSE);
    - klass = g_type_class_ref(type);
    - if(!klass)
    - return;
    + loader = gplugin_manager_find_loader_by_type(manager, type);
    + if(!GPLUGIN_IS_LOADER(loader)) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("loader %s is not registered"),
    + g_type_name(type));
    - for(exts = gplugin_loader_class_get_supported_extensions(klass);
    - exts; exts = exts->next) {
    + return FALSE;
    + }
    +
    + exts = gplugin_loader_get_supported_extensions(loader);
    + for(l = exts; l; l = l->next) {
    GSList *los = NULL;
    - GSList *l = NULL;
    + GSList *ll = NULL;
    const gchar *ext = NULL;
    ext = (const gchar *)exts->data;
    - los = g_hash_table_lookup(manager->loaders, ext);
    + los = g_hash_table_lookup(manager->loaders_by_extension, ext);
    - for(l = los; l; l = l->next) {
    - GPluginLoader *lo = GPLUGIN_LOADER(l->data);
    + for(ll = los; ll; ll = ll->next) {
    + GPluginLoader *lo = GPLUGIN_LOADER(ll->data);
    /* check if this is not the loader we're looking for */
    if(G_OBJECT_TYPE(lo) != type)
    @@ -318,10 +488,14 @@
    * just update it.
    */
    los = g_slist_remove(los, lo);
    - if(los)
    - g_hash_table_insert(manager->loaders, g_strdup(ext), los);
    - else
    - g_hash_table_remove(manager->loaders, ext);
    + if(los) {
    + g_hash_table_insert(
    + manager->loaders_by_extension,
    + g_strdup(ext),
    + los);
    + } else {
    + g_hash_table_remove(manager->loaders_by_extension, ext);
    + }
    /* kill our ref to the loader */
    g_object_unref(G_OBJECT(lo));
    @@ -330,12 +504,17 @@
    break;
    }
    }
    + g_slist_free(exts);
    - g_type_class_unref(klass);
    + manager->loaders = g_slist_remove(manager->loaders, loader);
    + g_object_unref(G_OBJECT(loader));
    +
    + return TRUE;
    }
    static void
    -gplugin_manager_real_refresh(GPluginManager *manager) {
    +gplugin_manager_real_refresh(GPluginManager *manager)
    +{
    GNode *root = NULL;
    GList *error_messages = NULL, *l = NULL;
    gchar *error_message = NULL;
    @@ -374,57 +553,54 @@
    /* Build the path and see if we need to probe it! */
    filename = g_build_filename(path, e->filename, NULL);
    - plugin = g_hash_table_lookup(manager->plugins_filename_view, filename);
    + plugin = g_hash_table_lookup(
    + manager->plugins_filename_view,
    + filename);
    if(plugin && GPLUGIN_IS_PLUGIN(plugin)) {
    - GPluginPluginState state =
    - gplugin_plugin_get_state(plugin);
    + GPluginPluginState state = gplugin_plugin_get_state(plugin);
    - /* The plugin is in our "view", check it's state. If it's
    + /* The plugin is in our "view", check its state. If it's
    * queried or loaded, move on to the next one.
    */
    if(state == GPLUGIN_PLUGIN_STATE_QUERIED ||
    - state == GPLUGIN_PLUGIN_STATE_LOADED)
    - {
    + state == GPLUGIN_PLUGIN_STATE_LOADED) {
    g_free(filename);
    continue;
    }
    }
    /* grab the list of loaders for this extension */
    - for(l = g_hash_table_lookup(manager->loaders, e->extension); l;
    - l = l->next)
    - {
    - if(!l->data)
    - continue;
    -
    - loader = GPLUGIN_LOADER(l->data);
    - if(!GPLUGIN_IS_LOADER(loader)) {
    - loader = NULL;
    + l = g_hash_table_lookup(
    + manager->loaders_by_extension,
    + e->extension);
    + for(; l; l = l->next) {
    + if(!GPLUGIN_IS_LOADER(l->data)) {
    continue;
    }
    + loader = GPLUGIN_LOADER(l->data);
    +
    /* Try to probe the plugin with the current loader */
    - plugin = gplugin_loader_query_plugin(loader,
    - filename,
    - &error);
    + plugin =
    + gplugin_loader_query_plugin(loader, filename, &error);
    - /* Check the GError, if it's set, output it's message and
    + /* Check the GError, if it's set, output its message and
    * try the next loader.
    */
    - if(plugin == NULL || error) {
    + if(error) {
    errors++;
    - error_message =
    - g_strdup_printf(_("failed to query '%s' with " \
    - "loader '%s': %s"), filename,
    - G_OBJECT_TYPE_NAME(loader),
    - (error) ? error->message : _("Unknown"));
    - error_messages = g_list_prepend(error_messages, error_message);
    + error_message = g_strdup_printf(
    + _("failed to query '%s' with "
    + "loader '%s': %s"),
    + filename,
    + G_OBJECT_TYPE_NAME(loader),
    + error->message);
    + error_messages =
    + g_list_prepend(error_messages, error_message);
    - if(error)
    - g_error_free(error);
    -
    + g_error_free(error);
    error = NULL;
    loader = NULL;
    @@ -469,21 +645,28 @@
    /* throw a warning if the info->id is NULL */
    if(id == NULL) {
    - error_message =
    - g_strdup_printf(_("Plugin %s has a NULL id."),
    - real_filename);
    - error_messages = g_list_prepend(error_messages, error_message);
    + error_message = g_strdup_printf(
    + _("Plugin %s has a NULL id."),
    + real_filename);
    + g_free(real_filename);
    + g_object_unref(G_OBJECT(info));
    +
    + error_messages =
    + g_list_prepend(error_messages, error_message);
    +
    + continue;
    }
    /* now insert into our view */
    - g_hash_table_replace(manager->plugins_filename_view,
    - real_filename,
    - g_object_ref(G_OBJECT(plugin)));
    + g_hash_table_replace(
    + manager->plugins_filename_view,
    + real_filename,
    + g_object_ref(G_OBJECT(plugin)));
    /* Grab the list of plugins with our id and prepend the new
    * plugin to it before updating it.
    */
    - l = g_hash_table_lookup(manager->plugins, id); //-V1004
    + l = g_hash_table_lookup(manager->plugins, id);
    for(ll = l; ll; ll = ll->next) {
    GPluginPlugin *splugin = GPLUGIN_PLUGIN(ll->data);
    gchar *sfilename = gplugin_plugin_get_filename(splugin);
    @@ -505,26 +688,30 @@
    GError *error = NULL;
    gboolean loaded;
    - loaded = gplugin_loader_load_plugin(loader,
    - plugin,
    - &error);
    + loaded =
    + gplugin_loader_load_plugin(loader, plugin, &error);
    if(!loaded) {
    - error_message =
    - g_strdup_printf(_("failed to load %s during query: %s"),
    - filename,
    - (error) ? error->message : _("Unknown"));
    - error_messages = g_list_prepend(error_messages, error_message);
    + error_message = g_strdup_printf(
    + _("failed to load %s during query: %s"),
    + filename,
    + (error) ? error->message : _("Unknown"));
    + error_messages =
    + g_list_prepend(error_messages, error_message);
    errors++;
    g_error_free(error);
    } else {
    - gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOADED);
    + gplugin_plugin_set_state(
    + plugin,
    + GPLUGIN_PLUGIN_STATE_LOADED);
    }
    } else {
    /* finally set the plugin state queried */
    - gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_QUERIED);
    + gplugin_plugin_set_state(
    + plugin,
    + GPLUGIN_PLUGIN_STATE_QUERIED);
    /* if errors is greater than 0 set
    * manager->refresh_needed to TRUE.
    @@ -537,6 +724,10 @@
    g_object_unref(G_OBJECT(info));
    + /* since the plugin is now stored in our hash tables we
    + * need to remove this function's reference to it.
    + */
    + g_object_unref(G_OBJECT(plugin));
    }
    g_free(filename);
    @@ -558,31 +749,63 @@
    gplugin_file_tree_free(root);
    }
    +static void
    +gplugin_manager_real_foreach(
    + GPluginManager *manager,
    + GPluginManagerForeachFunc func,
    + gpointer data)
    +{
    + GHashTableIter iter;
    + gpointer id = NULL, plugins = NULL;
    +
    + g_hash_table_iter_init(&iter, manager->plugins);
    + while(g_hash_table_iter_next(&iter, &id, &plugins)) {
    + func((gchar *)id, (GSList *)plugins, data);
    + }
    +}
    +
    static GSList *
    -gplugin_manager_real_find_plugins(GPluginManager *manager,
    - const gchar *id)
    +gplugin_manager_real_find_plugins(GPluginManager *manager, const gchar *id)
    {
    GSList *plugins_list = NULL, *l;
    g_return_val_if_fail(id != NULL, NULL);
    - for(l = g_hash_table_lookup(manager->plugins, id); l; l = l->next) {
    - GPluginPlugin *plugin = NULL;
    -
    - if(l->data == NULL)
    - continue;
    -
    - plugin = GPLUGIN_PLUGIN(l->data);
    -
    - plugins_list = g_slist_prepend(plugins_list,
    - g_object_ref(G_OBJECT(plugin)));
    - }
    + l = g_hash_table_lookup(manager->plugins, id);
    + plugins_list = g_slist_copy_deep(l, (GCopyFunc)g_object_ref, NULL);
    return plugins_list;
    }
    +static GSList *
    +gplugin_manager_real_find_plugins_with_state(
    + GPluginManager *manager,
    + GPluginPluginState state)
    +{
    + GSList *plugins = NULL;
    + GHashTableIter iter;
    + gpointer value = NULL;
    +
    + g_hash_table_iter_init(&iter, manager->plugins);
    + while(g_hash_table_iter_next(&iter, NULL, &value)) {
    + GSList *l = NULL;
    +
    + for(l = (GSList *)value; l != NULL; l = l->next) {
    + GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data);
    +
    + if(gplugin_plugin_get_state(plugin) == state) {
    + plugins =
    + g_slist_prepend(plugins, g_object_ref(G_OBJECT(plugin)));
    + }
    + }
    + }
    +
    + return plugins;
    +}
    +
    static GList *
    -gplugin_manager_real_list_plugins(GPluginManager *manager) {
    +gplugin_manager_real_list_plugins(GPluginManager *manager)
    +{
    GQueue *queue = g_queue_new();
    GList *ret = NULL;
    GHashTableIter iter;
    @@ -601,178 +824,132 @@
    }
    static gboolean
    -gplugin_manager_load_dependencies(GPluginPlugin *plugin,
    - GPluginPluginInfo *info,
    - GError **error)
    +gplugin_manager_load_dependencies(
    + GPluginPlugin *plugin,
    + GPluginPluginInfo *info,
    + GError **error)
    {
    - const gchar * const *dependencies = NULL;
    - gint i = 0;
    -
    - /* now walk through any dependencies the plugin has and load them. If they
    - * fail to load we need to fail as well.
    - */
    - dependencies = gplugin_plugin_info_get_dependencies(info);
    - if(dependencies != NULL) {
    - gboolean all_found = TRUE;
    -
    - for(i = 0; dependencies[i]; i++) {
    - gboolean found = FALSE;
    - gchar **ors = NULL;
    - gint o = 0;
    -
    - ors = g_strsplit(dependencies[i], "|", 0);
    - for(o = 0; ors[o]; o++) {
    - GMatchInfo *match = NULL;
    - GSList *matches = NULL, *m = NULL;
    - gchar *oid = NULL, *oop = NULL, *over = NULL;
    + GSList *dependencies = NULL, *l = NULL;
    + GError *ourerror = NULL;
    + gboolean all_loaded = TRUE;
    - if(!g_regex_match(dependency_regex, ors[o], 0, &match)) {
    - continue;
    - }
    -
    - /* grab the or'd id, op, and version */
    - oid = g_match_info_fetch_named(match, "id");
    - oop = g_match_info_fetch_named(match, "op");
    - over = g_match_info_fetch_named(match, "version");
    -
    - /* free the match info */
    - g_match_info_free(match);
    -
    - /* now look for a plugin matching the id */
    - matches = gplugin_manager_find_plugins(oid);
    - if(matches == NULL) {
    - g_free(oid);
    - g_free(oop);
    - g_free(over);
    - continue;
    - }
    -
    - /* now iterate the matches and check if we need to check their
    - * version.
    - */
    - for(m = matches; m; m = m->next) {
    - GPluginPlugin *dplugin = GPLUGIN_PLUGIN(g_object_ref(G_OBJECT(m->data)));
    - gboolean ret = FALSE;
    + dependencies = gplugin_manager_get_plugin_dependencies(plugin, &ourerror);
    + if(ourerror != NULL) {
    + g_propagate_error(error, ourerror);
    - if(oop && over) {
    - /* we need to check the version, so grab the info to
    - * get the version and check it.
    - */
    - GPluginPluginInfo *dinfo = NULL;
    - const gchar *dver = NULL;
    - gboolean satisfied = FALSE;
    - gint res = 0;
    -
    - dinfo = gplugin_plugin_get_info(dplugin);
    - dver = gplugin_plugin_info_get_version(dinfo);
    -
    - res = gplugin_version_compare(dver, over, error);
    - g_object_unref(G_OBJECT(dinfo));
    + return FALSE;
    + }
    - if(res < 0) {
    - /* dver is greather than over */
    - if(g_strcmp0(oop, ">") == 0)
    - satisfied = TRUE;
    - } else if(res == 0) {
    - /* dver is equal to over */
    - if(g_strcmp0(oop, ">=") == 0 ||
    - g_strcmp0(oop, "<=") == 0 ||
    - g_strcmp0(oop, "=") == 0 ||
    - g_strcmp0(oop, "==") == 0)
    - {
    - satisfied = TRUE;
    - }
    - } else if(res > 0) {
    - if(g_strcmp0(oop, "<") == 0)
    - satisfied = TRUE;
    - }
    + for(l = dependencies; l != NULL; l = l->next) {
    + GPluginPlugin *dependency = GPLUGIN_PLUGIN(l->data);
    + gboolean loaded = FALSE;
    - if(satisfied)
    - found = TRUE;
    - }
    -
    - ret = gplugin_manager_load_plugin(dplugin, error);
    -
    -# warning need to figure out dependencies
    -// gplugin_plugin_add_dependent_plugin(dplugin, plugin);
    -
    - g_object_unref(G_OBJECT(dplugin));
    + loaded = gplugin_manager_load_plugin(dependency, &ourerror);
    - if(ret) {
    - found = TRUE;
    - break;
    - }
    - }
    -
    - g_free(oid);
    - g_free(oop);
    - g_free(over);
    + if(!loaded || ourerror != NULL) {
    + if(ourerror != NULL) {
    + g_propagate_error(error, ourerror);
    + }
    - if(found)
    - break;
    - }
    - g_strfreev(ors);
    -
    - if(!found)
    - all_found = FALSE;
    - }
    -
    - if(!all_found) {
    - return FALSE;
    + all_loaded = FALSE;
    + break;
    }
    }
    - return TRUE;
    + g_slist_free_full(dependencies, g_object_unref);
    +
    + return all_loaded;
    }
    static GSList *
    -gplugin_manager_real_get_plugin_dependencies(G_GNUC_UNUSED GPluginManager *manager,
    - GPluginPlugin *plugin,
    - GError **error)
    +gplugin_manager_real_get_plugin_dependencies(
    + G_GNUC_UNUSED GPluginManager *manager,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    GPluginPluginInfo *info = NULL;
    GSList *ret = NULL;
    - const gchar * const *dependencies = NULL;
    - gint idx = 0;
    + const gchar *const *dependencies = NULL;
    + gint i = 0;
    info = gplugin_plugin_get_info(plugin);
    dependencies = gplugin_plugin_info_get_dependencies(info);
    + g_object_unref(G_OBJECT(info));
    if(dependencies == NULL) {
    return NULL;
    }
    - for(idx = 0; dependencies[idx] != NULL; idx++) {
    - GPluginPlugin *dep = gplugin_manager_find_plugin(dependencies[idx]);
    + for(i = 0; dependencies[i] != NULL; i++) {
    + gboolean found = FALSE;
    + gchar **ors = NULL;
    + gint o = 0;
    +
    + ors = g_strsplit(dependencies[i], "|", 0);
    + for(o = 0; ors[o]; o++) {
    + GMatchInfo *match = NULL;
    + GSList *matches = NULL;
    + gchar *oid = NULL, *oop = NULL, *over = NULL;
    +
    + if(!g_regex_match(dependency_regex, ors[o], 0, &match)) {
    + continue;
    + }
    +
    + /* grab the or'd id, op, and version */
    + oid = g_match_info_fetch_named(match, "id");
    + oop = g_match_info_fetch_named(match, "op");
    + over = g_match_info_fetch_named(match, "version");
    +
    + /* free the match info */
    + g_match_info_free(match);
    - if(!GPLUGIN_IS_PLUGIN(dep)) {
    - if(error) {
    - *error = g_error_new(
    - GPLUGIN_DOMAIN,
    - 0,
    - "failed to find plugin dependency '%s'",
    - dependencies[idx]
    - );
    + /* now look for a plugin matching the id */
    + matches = gplugin_manager_find_plugins_with_version(oid, oop, over);
    + g_free(oid);
    + g_free(oop);
    + g_free(over);
    +
    + if(matches == NULL) {
    + continue;
    + }
    +
    + /* prepend the first found match to our return value */
    + ret = g_slist_prepend(ret, g_object_ref(matches->data));
    + g_slist_free_full(matches, g_object_unref);
    +
    + found = TRUE;
    - g_slist_free_full(ret, g_object_unref);
    + break;
    + }
    + g_strfreev(ors);
    - return NULL;
    - }
    + if(!found) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("failed to find dependency %s for %s"),
    + dependencies[i],
    + gplugin_plugin_info_get_id(info));
    +
    + g_slist_free_full(ret, g_object_unref);
    +
    + return NULL;
    }
    -
    - ret = g_slist_prepend(ret, dep);
    }
    return ret;
    }
    static gboolean
    -gplugin_manager_real_load_plugin(GPluginManager *manager,
    - GPluginPlugin *plugin,
    - GError **error)
    +gplugin_manager_real_load_plugin(
    + GPluginManager *manager,
    + GPluginPlugin *plugin,
    + GError **ret_error)
    {
    GPluginPluginInfo *info = NULL;
    GPluginLoader *loader = NULL;
    + GError *error = NULL;
    gboolean ret = TRUE;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), FALSE);
    @@ -784,16 +961,22 @@
    /* now try to get the plugin info from the plugin */
    info = gplugin_plugin_get_info(plugin);
    if(info == NULL) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0,
    - _("Plugin %s did not return value plugin info"),
    - gplugin_plugin_get_filename(plugin));
    + gchar *filename = gplugin_plugin_get_filename(plugin);
    +
    + g_set_error(
    + ret_error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("Plugin %s did not return value plugin info"),
    + filename);
    + g_free(filename);
    gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    return FALSE;
    }
    - if(!gplugin_manager_load_dependencies(plugin, info, error)) {
    + if(!gplugin_manager_load_dependencies(plugin, info, ret_error)) {
    g_object_unref(G_OBJECT(info));
    return FALSE;
    @@ -805,41 +988,73 @@
    loader = gplugin_plugin_get_loader(plugin);
    if(!GPLUGIN_IS_LOADER(loader)) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0,
    - _("The loader for %s is not a loader. This "
    - "should not happened!"),
    - gplugin_plugin_get_filename(plugin));
    + gchar *filename = gplugin_plugin_get_filename(plugin);
    +
    + g_set_error(
    + ret_error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("The loader for %s is not a loader. This "
    + "should not happened!"),
    + filename);
    + g_free(filename);
    gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    return FALSE;
    }
    - g_signal_emit(manager, signals[SIG_LOADING], 0, plugin, error, &ret);
    + g_signal_emit(manager, signals[SIG_LOADING], 0, plugin, &error, &ret);
    if(!ret) {
    + /* Set the plugin's error. */
    + g_object_set(G_OBJECT(plugin), "error", error, NULL);
    +
    + g_propagate_error(ret_error, error);
    +
    gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    return ret;
    }
    - ret = gplugin_loader_load_plugin(loader, plugin, error);
    - gplugin_plugin_set_state(plugin, (ret) ? GPLUGIN_PLUGIN_STATE_LOADED :
    - GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    + ret = gplugin_loader_load_plugin(loader, plugin, &error);
    + if(ret) {
    + /* If the plugin successfully loaded but returned an error, ignore the
    + * error.
    + */
    + g_clear_error(&error);
    +
    + /* Likewise, make sure the plugin's error is set to NULL. */
    + g_object_set(G_OBJECT(plugin), "error", NULL, NULL);
    - if(ret)
    + gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOADED);
    g_signal_emit(manager, signals[SIG_LOADED], 0, plugin);
    - else
    + } else {
    + /* Set the error on the plugin as well. This has to be before we
    + * propagate the error, because error is invalidate at that point.
    + */
    + g_object_set(G_OBJECT(plugin), "error", error, NULL);
    +
    + /* Set the state after the error is set, because people might connect
    + * to the notify signal on the state property.
    + */
    + gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    +
    g_signal_emit(manager, signals[SIG_LOAD_FAILED], 0, plugin);
    + g_propagate_error(ret_error, error);
    + }
    +
    return ret;
    }
    static gboolean
    -gplugin_manager_real_unload_plugin(GPluginManager *manager,
    - GPluginPlugin *plugin,
    - GError **error)
    +gplugin_manager_real_unload_plugin(
    + GPluginManager *manager,
    + GPluginPlugin *plugin,
    + GError **ret_error)
    {
    GPluginLoader *loader = NULL;
    + GError *error = NULL;
    gboolean ret = TRUE;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), FALSE);
    @@ -849,76 +1064,121 @@
    loader = gplugin_plugin_get_loader(plugin);
    if(!GPLUGIN_IS_LOADER(loader)) {
    - g_set_error_literal(error, GPLUGIN_DOMAIN, 0,
    - _("Plugin loader is not a loader"));
    + g_set_error_literal(
    + ret_error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("Plugin loader is not a loader"));
    return FALSE;
    }
    - g_signal_emit(manager, signals[SIG_UNLOADING], 0, plugin, error, &ret);
    - if(!ret)
    + g_signal_emit(manager, signals[SIG_UNLOADING], 0, plugin, &error, &ret);
    + if(!ret) {
    + /* Set the plugin's error. */
    + g_object_set(G_OBJECT(plugin), "error", error, NULL);
    +
    + g_propagate_error(ret_error, error);
    +
    return ret;
    + }
    +
    + ret = gplugin_loader_unload_plugin(loader, plugin, &error);
    + if(ret) {
    + /* If the plugin successfully loaded but returned an error, ignore the
    + * error.
    + */
    + g_clear_error(&error);
    - ret = gplugin_loader_unload_plugin(loader, plugin, error);
    - if(ret) {
    + /* Like, make sure the plugin's error is set to NULL. */
    + g_object_set(G_OBJECT(plugin), "error", NULL, NULL);
    +
    gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_QUERIED);
    + g_signal_emit(manager, signals[SIG_UNLOADED], 0, plugin);
    + } else {
    + /* Set the error on the plugin as well. This has to be before we
    + * propagate the error, because error is invalidate at that point.
    + */
    + g_object_set(G_OBJECT(plugin), "error", error, NULL);
    - g_signal_emit(manager, signals[SIG_UNLOADED], 0, plugin, error);
    + /* Set the state after the error is set, because people might connect
    + * to the notify signal on the state property.
    + */
    + gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_UNLOAD_FAILED);
    +
    + g_signal_emit(manager, signals[SIG_UNLOAD_FAILED], 0, plugin);
    +
    + g_propagate_error(ret_error, error);
    }
    return ret;
    }
    static gboolean
    -gplugin_manager_loading_cb(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_manager_loading_cb(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    static gboolean
    -gplugin_manager_unloading_cb(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_manager_unloading_cb(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    /******************************************************************************
    - * Object Stuff
    + * GObject Implementation
    *****************************************************************************/
    static void
    -gplugin_manager_finalize(GObject *obj) {
    +gplugin_manager_finalize(GObject *obj)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER(obj);
    g_queue_free_full(manager->paths, g_free);
    manager->paths = NULL;
    + /* unload all of the loaded plugins */
    + g_hash_table_foreach(
    + manager->plugins,
    + gplugin_manager_foreach_unload_plugin,
    + NULL);
    +
    /* free all the data in the plugins hash table and destroy it */
    - g_hash_table_foreach_remove(manager->plugins,
    - gplugin_manager_remove_list_value,
    - NULL);
    + g_hash_table_foreach_remove(
    + manager->plugins,
    + gplugin_manager_remove_list_value,
    + NULL);
    g_clear_pointer(&manager->plugins, g_hash_table_destroy);
    /* destroy the filename view */
    g_clear_pointer(&manager->plugins_filename_view, g_hash_table_destroy);
    + /* clean up our list of loaders */
    + g_slist_free_full(manager->loaders, g_object_unref);
    + manager->loaders = NULL;
    +
    /* free all the data in the loaders hash table and destroy it */
    - g_hash_table_foreach_remove(manager->loaders,
    - gplugin_manager_remove_list_value,
    - NULL);
    - g_clear_pointer(&manager->loaders, g_hash_table_destroy);
    + g_hash_table_foreach_remove(
    + manager->loaders_by_extension,
    + gplugin_manager_remove_list_value,
    + NULL);
    + g_clear_pointer(&manager->loaders_by_extension, g_hash_table_destroy);
    /* call the base class's destructor */
    G_OBJECT_CLASS(gplugin_manager_parent_class)->finalize(obj);
    }
    static void
    -gplugin_manager_class_init(GPluginManagerClass *klass) {
    +gplugin_manager_class_init(GPluginManagerClass *klass)
    +{
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    - GPluginManagerClass *manager_class =
    - GPLUGIN_MANAGER_CLASS(klass);
    + GPluginManagerClass *manager_class = GPLUGIN_MANAGER_CLASS(klass);
    obj_class->finalize = gplugin_manager_finalize;
    @@ -928,17 +1188,20 @@
    manager_class->remove_paths = gplugin_manager_real_remove_paths;
    manager_class->get_paths = gplugin_manager_real_get_paths;
    - manager_class->register_loader =
    - gplugin_manager_real_register_loader;
    - manager_class->unregister_loader =
    - gplugin_manager_real_unregister_loader;
    + manager_class->register_loader = gplugin_manager_real_register_loader;
    + manager_class->unregister_loader = gplugin_manager_real_unregister_loader;
    manager_class->refresh = gplugin_manager_real_refresh;
    + manager_class->foreach = gplugin_manager_real_foreach;
    +
    manager_class->find_plugins = gplugin_manager_real_find_plugins;
    + manager_class->find_plugins_with_state =
    + gplugin_manager_real_find_plugins_with_state;
    manager_class->list_plugins = gplugin_manager_real_list_plugins;
    - manager_class->get_plugin_dependencies = gplugin_manager_real_get_plugin_dependencies;
    + manager_class->get_plugin_dependencies =
    + gplugin_manager_real_get_plugin_dependencies;
    manager_class->load_plugin = gplugin_manager_real_load_plugin;
    manager_class->unload_plugin = gplugin_manager_real_unload_plugin;
    @@ -958,17 +1221,18 @@
    *
    * Return FALSE to stop loading
    */
    - signals[SIG_LOADING] =
    - g_signal_new("loading-plugin",
    - G_OBJECT_CLASS_TYPE(manager_class),
    - G_SIGNAL_RUN_LAST,
    - G_STRUCT_OFFSET(GPluginManagerClass,
    - loading_plugin),
    - gplugin_boolean_accumulator, NULL,
    - NULL,
    - G_TYPE_BOOLEAN,
    - 2,
    - G_TYPE_OBJECT, G_TYPE_POINTER);
    + signals[SIG_LOADING] = g_signal_new(
    + "loading-plugin",
    + G_OBJECT_CLASS_TYPE(manager_class),
    + G_SIGNAL_RUN_LAST,
    + G_STRUCT_OFFSET(GPluginManagerClass, loading_plugin),
    + gplugin_boolean_accumulator,
    + NULL,
    + NULL,
    + G_TYPE_BOOLEAN,
    + 2,
    + G_TYPE_OBJECT,
    + G_TYPE_POINTER);
    /**
    * GPluginManager::loaded-plugin:
    @@ -977,36 +1241,36 @@
    *
    * emitted after a plugin is loaded.
    */
    - signals[SIG_LOADED] =
    - g_signal_new("loaded-plugin",
    - G_OBJECT_CLASS_TYPE(manager_class),
    - G_SIGNAL_RUN_LAST,
    - G_STRUCT_OFFSET(GPluginManagerClass,
    - loaded_plugin),
    - NULL, NULL,
    - NULL,
    - G_TYPE_NONE,
    - 1,
    - G_TYPE_OBJECT);
    + signals[SIG_LOADED] = g_signal_new(
    + "loaded-plugin",
    + G_OBJECT_CLASS_TYPE(manager_class),
    + G_SIGNAL_RUN_LAST,
    + G_STRUCT_OFFSET(GPluginManagerClass, loaded_plugin),
    + NULL,
    + NULL,
    + NULL,
    + G_TYPE_NONE,
    + 1,
    + G_TYPE_OBJECT);
    /**
    - * GPluginManager::load-failed:
    + * GPluginManager::load-plugin-failed:
    * @manager: The #GPluginPluginManager instance.
    * @plugin: The #GPluginPlugin that failed to load.
    *
    * emitted after a plugin fails to load.
    */
    - signals[SIG_LOAD_FAILED] =
    - g_signal_new("load-failed",
    - G_OBJECT_CLASS_TYPE(manager_class),
    - G_SIGNAL_RUN_LAST,
    - G_STRUCT_OFFSET(GPluginManagerClass, load_failed),
    - NULL,
    - NULL,
    - NULL,
    - G_TYPE_NONE,
    - 1,
    - G_TYPE_OBJECT);
    + signals[SIG_LOAD_FAILED] = g_signal_new(
    + "load-plugin-failed",
    + G_OBJECT_CLASS_TYPE(manager_class),
    + G_SIGNAL_RUN_LAST,
    + G_STRUCT_OFFSET(GPluginManagerClass, load_failed),
    + NULL,
    + NULL,
    + NULL,
    + G_TYPE_NONE,
    + 1,
    + G_TYPE_OBJECT);
    /**
    * GPluginManager::unloading-plugin:
    @@ -1017,40 +1281,63 @@
    *
    * Return FALSE to stop unloading
    */
    - signals[SIG_UNLOADING] =
    - g_signal_new("unloading-plugin",
    - G_OBJECT_CLASS_TYPE(manager_class),
    - G_SIGNAL_RUN_LAST,
    - G_STRUCT_OFFSET(GPluginManagerClass,
    - unloading_plugin),
    - gplugin_boolean_accumulator, NULL,
    - NULL,
    - G_TYPE_BOOLEAN,
    - 2,
    - G_TYPE_OBJECT, G_TYPE_POINTER);
    + signals[SIG_UNLOADING] = g_signal_new(
    + "unloading-plugin",
    + G_OBJECT_CLASS_TYPE(manager_class),
    + G_SIGNAL_RUN_LAST,
    + G_STRUCT_OFFSET(GPluginManagerClass, unloading_plugin),
    + gplugin_boolean_accumulator,
    + NULL,
    + NULL,
    + G_TYPE_BOOLEAN,
    + 2,
    + G_TYPE_OBJECT,
    + G_TYPE_POINTER);
    /**
    * GPluginManager::unloaded-plugin:
    * @manager: the #gpluginpluginmanager instance. treat as a #gobject.
    * @plugin: the #gpluginplugin that's about to be loaded.
    *
    - * emitted after a plugin is unloaded.
    + * emitted after a plugin is successfully unloaded.
    */
    - signals[SIG_UNLOADED] =
    - g_signal_new("unloaded-plugin",
    - G_OBJECT_CLASS_TYPE(manager_class),
    - G_SIGNAL_RUN_LAST,
    - G_STRUCT_OFFSET(GPluginManagerClass,
    - unloaded_plugin),
    - NULL, NULL,
    - NULL,
    - G_TYPE_NONE,
    - 1,
    - G_TYPE_OBJECT);
    + signals[SIG_UNLOADED] = g_signal_new(
    + "unloaded-plugin",
    + G_OBJECT_CLASS_TYPE(manager_class),
    + G_SIGNAL_RUN_LAST,
    + G_STRUCT_OFFSET(GPluginManagerClass, unloaded_plugin),
    + NULL,
    + NULL,
    + NULL,
    + G_TYPE_NONE,
    + 1,
    + G_TYPE_OBJECT);
    +
    + /**
    + * GPluginManager::unload-plugin-failed:
    + * @manager: The #GPluginManager instance.
    + * @plugin: The #GPluginPlugin instance that failed to unload.
    + * @error: A #GError instance.
    + *
    + * Emitted when @manager was asked to unload @plugin, but @plugin returned
    + * %FALSE when its unload function was called.
    + */
    + signals[SIG_UNLOAD_FAILED] = g_signal_new(
    + "unload-plugin-failed",
    + G_OBJECT_CLASS_TYPE(manager_class),
    + G_SIGNAL_RUN_LAST,
    + G_STRUCT_OFFSET(GPluginManagerClass, unload_plugin_failed),
    + NULL,
    + NULL,
    + NULL,
    + G_TYPE_NONE,
    + 1,
    + G_TYPE_OBJECT);
    }
    static void
    -gplugin_manager_init(GPluginManager *manager) {
    +gplugin_manager_init(GPluginManager *manager)
    +{
    manager->paths = g_queue_new();
    /* the plugins hashtable is keyed on a plugin id and holds a GSList of all
    @@ -1065,40 +1352,53 @@
    manager->plugins_filename_view =
    g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
    - /* The loaders hash table is keyed on the supported extensions of the
    - * loader. Which means that a loader that supports multiple extensions
    - * will be in the table multiple times.
    + /* The loaders_by_extension hash table is keyed on the supported extensions
    + * of the loader. Which means that a loader that supports multiple
    + * extensions will be in the table multiple times.
    *
    * We deal with collisions by using a GSList for the value which will hold
    * references to instances of the actual loaders.
    *
    - * Storing this in this method allows up to quickly figure out which loader
    + * Storing this in this method allows us to quickly figure out which loader
    * to use by the filename and helps us to avoid iterating the loaders table
    * again and again.
    */
    - manager->loaders =
    - g_hash_table_new_full(gplugin_manager_str_hash, g_str_equal,
    - g_free, NULL);
    + manager->loaders_by_extension = g_hash_table_new_full(
    + gplugin_manager_str_hash,
    + g_str_equal,
    + g_free,
    + NULL);
    }
    /******************************************************************************
    * Private API
    *****************************************************************************/
    void
    -gplugin_manager_private_init(void) {
    - if (instance != NULL) {
    +gplugin_manager_private_init(void)
    +{
    + GError *error = NULL;
    +
    + if(instance != NULL) {
    return;
    }
    instance = g_object_new(GPLUGIN_TYPE_MANAGER, NULL);
    - gplugin_manager_register_loader(GPLUGIN_TYPE_NATIVE_LOADER);
    + if(!gplugin_manager_register_loader(GPLUGIN_TYPE_NATIVE_LOADER, &error)) {
    + if(error != NULL) {
    + g_error("failed to register loader: %s", error->message);
    + g_error_free(error);
    + } else {
    + g_error("failed to register loader: unknown failure");
    + }
    + }
    dependency_regex = g_regex_new(dependency_pattern, 0, 0, NULL);
    }
    void
    -gplugin_manager_private_uninit(void) {
    +gplugin_manager_private_uninit(void)
    +{
    g_regex_unref(dependency_regex);
    g_clear_object(&instance);
    @@ -1110,12 +1410,13 @@
    /**
    * gplugin_manager_append_path:
    - * @path: A path to add to the end of the plugin search paths
    + * @path: A path to add to the end of the plugin search paths.
    *
    * Adds @path to the end of the list of paths to search for plugins.
    */
    void
    -gplugin_manager_append_path(const gchar *path) {
    +gplugin_manager_append_path(const gchar *path)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    @@ -1129,12 +1430,13 @@
    /**
    * gplugin_manager_prepend_path:
    - * @path: A path to add to the beginning of the plugin search paths
    + * @path: A path to add to the beginning of the plugin search paths.
    *
    * Adds @path to the beginning of the list of paths to search for plugins.
    */
    void
    -gplugin_manager_prepend_path(const gchar *path) {
    +gplugin_manager_prepend_path(const gchar *path)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    @@ -1148,12 +1450,13 @@
    /**
    * gplugin_manager_remove_path:
    - * @path: A path to remove from the plugin search paths
    + * @path: A path to remove from the plugin search paths.
    *
    * Removes @path from the list of paths to search for plugins.
    */
    void
    -gplugin_manager_remove_path(const gchar *path) {
    +gplugin_manager_remove_path(const gchar *path)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    @@ -1171,7 +1474,8 @@
    * Clears all paths that are set to search for plugins.
    */
    void
    -gplugin_manager_remove_paths(void) {
    +gplugin_manager_remove_paths(void)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    @@ -1187,10 +1491,12 @@
    * gplugin_manager_add_default_paths:
    *
    * Adds the path that GPlugin was installed to to the plugin search path, as
    - * well as ${XDG_CONFIG_HOME}/gplugin.
    + * well as `${XDG_CONFIG_HOME}/gplugin` so users can install additional loaders
    + * themselves.
    */
    void
    -gplugin_manager_add_default_paths(void) {
    +gplugin_manager_add_default_paths(void)
    +{
    gchar *path;
    path = g_build_filename(PREFIX, LIBDIR, "gplugin", NULL);
    @@ -1202,19 +1508,17 @@
    g_free(path);
    }
    -
    /**
    * gplugin_manager_add_app_paths:
    * @prefix: The installation prefix for the application.
    * @appname: The name of the application whose paths to add.
    *
    * Adds the application installation path for @appname. This will add
    - * $prefix/@appname/plugins to the list as well as
    - * ${XDG_CONFIG_HOME}/@appname/plugins.
    + * `@prefix/@appname/plugins` to the list as well as
    + * `${XDG_CONFIG_HOME}/@appname/plugins`.
    */
    void
    -gplugin_manager_add_app_paths(const gchar *prefix,
    - const gchar *appname)
    +gplugin_manager_add_app_paths(const gchar *prefix, const gchar *appname)
    {
    gchar *path;
    @@ -1232,13 +1536,14 @@
    /**
    * gplugin_manager_get_paths:
    *
    - * Gets the list of paths which will be search for plugins.
    + * Gets the list of paths which will be searched for plugins.
    *
    - * Return value: (element-type utf8) (transfer none): list of paths which will
    - * be searched for plugins.
    + * Returns: (element-type utf8) (transfer none): The list of paths which will
    + * be searched for plugins.
    */
    GList *
    -gplugin_manager_get_paths(void) {
    +gplugin_manager_get_paths(void)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    @@ -1253,38 +1558,64 @@
    /**
    * gplugin_manager_register_loader:
    - * @type: #GType of a #GPluginLoader
    + * @type: #GType of a #GPluginLoader.
    + * @error: (out) (nullable): The return address for a #GError.
    *
    * Registers @type as an available loader.
    + *
    + * Returns: %TRUE if the loader was successfully register, %FALSE otherwise
    + * with @error set.
    */
    -void
    -gplugin_manager_register_loader(GType type) {
    +gboolean
    +gplugin_manager_register_loader(GType type, GError **error)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    - g_return_if_fail(GPLUGIN_IS_MANAGER(manager));
    + g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE);
    klass = GPLUGIN_MANAGER_GET_CLASS(manager);
    if(klass && klass->register_loader)
    - klass->register_loader(manager, type);
    + return klass->register_loader(manager, type, error);
    +
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "register_loader method not implemented");
    +
    + return FALSE;
    }
    /**
    * gplugin_manager_unregister_loader:
    - * @type: #GType of a #GPluginLoader
    + * @type: #GType of a #GPluginLoader.
    + * @error: (out) (nullable): The return address for a #GError.
    *
    * Unregisters @type as an available loader.
    + *
    + * Returns: %TRUE if the loader was successfully unregistered, %FALSE
    + * otherwise with @error set.
    */
    -void
    -gplugin_manager_unregister_loader(GType type) {
    +gboolean
    +gplugin_manager_unregister_loader(GType type, GError **error)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    - g_return_if_fail(GPLUGIN_IS_MANAGER(manager));
    + g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE);
    klass = GPLUGIN_MANAGER_GET_CLASS(manager);
    if(klass && klass->unregister_loader)
    - klass->unregister_loader(manager, type);
    + return klass->unregister_loader(manager, type, error);
    +
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "unregister_loader method not implemented");
    +
    + return FALSE;
    }
    /**
    @@ -1293,7 +1624,8 @@
    * Forces a refresh of all plugins found in the search paths.
    */
    void
    -gplugin_manager_refresh(void) {
    +gplugin_manager_refresh(void)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    @@ -1305,18 +1637,40 @@
    }
    /**
    + * gplugin_manager_foreach:
    + * @func: (scope call): The #GPluginManagerForeachFunc to call.
    + * @data: User data to pass to func.
    + *
    + * Calls @func for each plugin that is known.
    + */
    +void
    +gplugin_manager_foreach(GPluginManagerForeachFunc func, gpointer data)
    +{
    + GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    + GPluginManagerClass *klass = NULL;
    +
    + g_return_if_fail(GPLUGIN_IS_MANAGER(manager));
    + g_return_if_fail(func != NULL);
    +
    + klass = GPLUGIN_MANAGER_GET_CLASS(manager);
    + if(klass && klass->foreach)
    + klass->foreach(manager, func, data);
    +}
    +
    +/**
    * gplugin_manager_find_plugins:
    - * @id: id string of the plugin to find
    + * @id: id string of the plugin to find.
    *
    * Finds all plugins matching @id.
    *
    - * Return value: (element-type GPlugin.Plugin) (transfer full): A #GSList of
    - * referenced #GPluginPlugin's matching @id. Call
    - * #gplugin_manager_free_plugin_list on the returned value
    - * when you're done with it.
    + * Returns: (element-type GPlugin.Plugin) (transfer full): A #GSList of
    + * referenced #GPluginPlugin's matching @id. Call
    + * g_slist_free_full() with a `DestroyNotify` of g_object_unref() on
    + * the returned value when you're done with it.
    */
    GSList *
    -gplugin_manager_find_plugins(const gchar *id) {
    +gplugin_manager_find_plugins(const gchar *id)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    @@ -1330,30 +1684,106 @@
    }
    /**
    - * gplugin_manager_free_plugin_list:
    - * @plugins_list: (element-type GPlugin.Plugin): Returned value from
    - * #gplugin_manager_find_plugins
    + * gplugin_manager_find_plugins_with_version:
    + * @id: The ID of the plugin to find.
    + * @op: one of <, <=, =, ==, >=, >.
    + * @version: The version to compare against.
    + *
    + * Similar to gplugin_manager_find_plugins() but only returns plugins whose
    + * versions match @op and @version. This is primarily used for dependency
    + * loading where a plugin may depend on a specific range of versions of another
    + * plugin.
    *
    - * Frees the return value of #gplugin_manager_find_plugins.
    + * Returns: (element-type GPlugin.Plugin) (transfer full): A #GSList of
    + * referenced #GPluginPlugin's matching @id. Call
    + * g_slist_free_full() with a `DestroyNotify` of g_object_unref() on
    + * the returned value when you're done with it.
    */
    -void
    -gplugin_manager_free_plugin_list(GSList *plugins_list) {
    - GSList *l = NULL;
    +GSList *
    +gplugin_manager_find_plugins_with_version(
    + const gchar *id,
    + const gchar *op,
    + const gchar *version)
    +{
    + GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    + GSList *plugins = NULL, *filtered = NULL, *l = NULL;
    - g_return_if_fail(plugins_list != NULL);
    + g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL);
    - for(l = plugins_list; l; l = l->next) {
    - GPluginPlugin *plugin = NULL;
    + plugins = gplugin_manager_find_plugins(id);
    - if(l->data == NULL)
    - continue;
    -
    - plugin = GPLUGIN_PLUGIN(l->data);
    -
    - g_object_unref(G_OBJECT(plugin));
    + if(op == NULL && version == NULL) {
    + /* we weren't actually passed an operator and a version so just return
    + * the list we have based on the id.
    + */
    + return plugins;
    }
    - g_slist_free(plugins_list);
    + for(l = plugins; l; l = l->next) {
    + GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data);
    + GPluginPluginInfo *info = NULL;
    + const gchar *found_version = NULL;
    + gint result = 0;
    + gboolean keep = FALSE;
    +
    + /* get the plugin's version from it's info */
    + info = gplugin_plugin_get_info(plugin);
    + found_version = gplugin_plugin_info_get_version(info);
    +
    + /* now compare the version of the plugin to passed in version. This
    + * should be done in this order so it's easier to track the operators.
    + * IE: we want to keep the inequality the same.
    + */
    + result = gplugin_version_compare(found_version, version);
    +
    + /* we need to keep info around until we're done using found_version */
    + g_object_unref(G_OBJECT(info));
    +
    + if(result < 0) {
    + keep = (g_strcmp0(op, "<") == 0 || g_strcmp0(op, "<=") == 0);
    + } else if(result == 0) {
    + keep =
    + (g_strcmp0(op, "=") == 0 || g_strcmp0(op, "==") == 0 ||
    + g_strcmp0(op, "<=") == 0 || g_strcmp0(op, ">=") == 0);
    + } else if(result > 0) {
    + keep = (g_strcmp0(op, ">") == 0 || g_strcmp0(op, ">=") == 0);
    + }
    +
    + if(keep) {
    + filtered =
    + g_slist_prepend(filtered, g_object_ref(G_OBJECT(plugin)));
    + }
    + }
    +
    + g_slist_free_full(plugins, g_object_unref);
    +
    + return g_slist_reverse(filtered);
    +}
    +
    +/**
    + * gplugin_manager_find_plugins_with_state:
    + * @state: The #GPluginPluginState to look for.
    + *
    + * Finds all plugins that currently have a state of @state.
    + *
    + * Returns: (element-type GPlugin.Plugin) (transfer full): A #GSList of
    + * referenced #GPluginPlugin's whose state is @state. Call
    + * g_slist_free_full() with a `DestroyNotify` of g_object_unref() on
    + * the returned value when you're done with it.
    + */
    +GSList *
    +gplugin_manager_find_plugins_with_state(GPluginPluginState state)
    +{
    + GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    + GPluginManagerClass *klass = NULL;
    +
    + g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL);
    +
    + klass = GPLUGIN_MANAGER_GET_CLASS(manager);
    + if(klass && klass->find_plugins_with_state)
    + return klass->find_plugins_with_state(manager, state);
    +
    + return NULL;
    }
    /**
    @@ -1368,7 +1798,8 @@
    * if no plugin matching @id was found.
    */
    GPluginPlugin *
    -gplugin_manager_find_plugin(const gchar *id) {
    +gplugin_manager_find_plugin(const gchar *id)
    +{
    GSList *plugins_list = NULL;
    GPluginPlugin *plugin = NULL;
    @@ -1380,24 +1811,99 @@
    plugin = GPLUGIN_PLUGIN(g_object_ref(G_OBJECT(plugins_list->data)));
    - gplugin_manager_free_plugin_list(plugins_list);
    + g_slist_free_full(plugins_list, g_object_unref);
    return plugin;
    }
    /**
    + * gplugin_manager_find_plugin_with_newest_version:
    + * @id: The id of the plugin to find.
    + *
    + * Calls gplugin_manager_find_plugins() with @id, and then returns the plugins
    + * with the highest version number or %NULL if no plugins with @id are found.
    + *
    + * Returns: (transfer full): The #GPluginPlugin with an id of @id that has the
    + * highest version number, or %NULL if no plugins were found with @id.
    + */
    +GPluginPlugin *
    +gplugin_manager_find_plugin_with_newest_version(const gchar *id)
    +{
    + GPluginPlugin *plugin_a = NULL;
    + GPluginPluginInfo *info_a = NULL;
    + const gchar *version_a = NULL;
    + GSList *l = NULL;
    +
    + g_return_val_if_fail(id != NULL, NULL);
    +
    + l = gplugin_manager_find_plugins(id);
    + for(; l != NULL; l = g_slist_delete_link(l, l)) {
    + GPluginPlugin *plugin_b = NULL;
    + GPluginPluginInfo *info_b = NULL;
    + const gchar *version_b = NULL;
    + gint cmp = 0;
    +
    + if(!GPLUGIN_IS_PLUGIN(l->data)) {
    + continue;
    + }
    +
    + plugin_b = GPLUGIN_PLUGIN(l->data);
    + info_b = gplugin_plugin_get_info(plugin_b);
    +
    + /* If this is the first plugin we've found, set the plugin_a values and
    + * continue.
    + */
    + if(!GPLUGIN_IS_PLUGIN(plugin_a)) {
    + plugin_a = plugin_b;
    + info_a = info_b;
    +
    + version_a = gplugin_plugin_info_get_version(info_a);
    +
    + continue;
    + }
    +
    + /* At this point, we've seen another plugin, so we need to compare
    + * their versions.
    + */
    + version_b = gplugin_plugin_info_get_version(info_b);
    +
    + cmp = gplugin_version_compare(version_a, version_b);
    + if(cmp < 0) {
    + /* plugin_b has a newer version, so set the plugin_a pointers to
    + * the plugin_b pointers as well as the version pointers.
    + */
    + g_set_object(&plugin_a, plugin_b);
    + g_set_object(&info_a, info_b);
    +
    + version_a = version_b;
    + }
    +
    + /* Clean up the plugin_b pointers. */
    + g_clear_object(&plugin_b);
    + g_clear_object(&info_b);
    + }
    +
    + g_clear_object(&info_a);
    +
    + return plugin_a;
    +}
    +
    +/**
    * gplugin_manager_get_plugin_dependencies:
    * @plugin: The #GPluginPlugin whose dependencies to get.
    - * @error: Return address for a #GError.
    + * @error: (out) (nullable): Return address for a #GError.
    *
    * Returns a list of all the #GPluginPlugin's that @plugin depends on.
    *
    * Return value: (element-type GPlugin.Plugin) (transfer full): A #GSList of
    - * #GPluginPlugin's that @plugin depends on, or NULL on error
    - * with @error set.
    + * #GPluginPlugin's that @plugin depends on, or %NULL on error
    + * with @error set. Call g_slist_free_full() with a
    + * `DestroyNotify` of g_object_unref() on the returned value when
    + * you're done with it.
    */
    GSList *
    -gplugin_manager_get_plugin_dependencies(GPluginPlugin *plugin, GError **error) {
    +gplugin_manager_get_plugin_dependencies(GPluginPlugin *plugin, GError **error)
    +{
    GPluginManager *manager = NULL;
    GPluginManagerClass *klass = NULL;
    @@ -1416,18 +1922,19 @@
    /**
    * gplugin_manager_load_plugin:
    - * @plugin: #GPluginPlugin instance
    - * @error: (out): return location for a #GError or null
    + * @plugin: #GPluginPlugin instance.
    + * @error: (out) (nullable): return location for a #GError or %NULL.
    *
    - * Loads @plugin and all of it's dependencies. If a dependency can not be
    + * Loads @plugin and all of its dependencies. If a dependency can not be
    * loaded, @plugin will not be loaded either. However, any other plugins that
    * @plugin depends on that were loaded from this call, will not be unloaded.
    *
    - * Return value: TRUE if @plugin was loaded successfully or already loaded,
    - * FALSE otherwise.
    + * Return value: %TRUE if @plugin was loaded successfully or already loaded,
    + * %FALSE otherwise.
    */
    gboolean
    -gplugin_manager_load_plugin(GPluginPlugin *plugin, GError **error) {
    +gplugin_manager_load_plugin(GPluginPlugin *plugin, GError **error)
    +{
    GPluginManager *manager = NULL;
    GPluginManagerClass *klass = NULL;
    @@ -1445,16 +1952,17 @@
    /**
    * gplugin_manager_unload_plugin:
    - * @plugin: #GPluginPlugin instance
    - * @error: (out): return location for a #GError or null
    + * @plugin: #GPluginPlugin instance.
    + * @error: (out) (nullable): Return location for a #GError or %NULL.
    *
    * Unloads @plugin. If @plugin has dependencies, they are not unloaded.
    *
    - * Return value: TRUE if @plugin was unloaded successfully or not loaded,
    - * FALSE otherwise.
    + * Returns: %TRUE if @plugin was unloaded successfully or not loaded, %FALSE
    + * otherwise.
    */
    gboolean
    -gplugin_manager_unload_plugin(GPluginPlugin *plugin, GError **error) {
    +gplugin_manager_unload_plugin(GPluginPlugin *plugin, GError **error)
    +{
    GPluginManager *manager = NULL;
    GPluginManagerClass *klass = NULL;
    @@ -1476,11 +1984,12 @@
    * Returns a #GList of all plugin id's. Each id should be queried directly
    * for more information.
    *
    - * Return value: (element-type utf8) (transfer full): A #GList of each unique
    - * plugin id.
    + * Return value: (element-type utf8) (transfer container): A #GList of each
    + * unique plugin id.
    */
    GList *
    -gplugin_manager_list_plugins(void) {
    +gplugin_manager_list_plugins(void)
    +{
    GPluginManager *manager = GPLUGIN_MANAGER_INSTANCE;
    GPluginManagerClass *klass = NULL;
    @@ -1493,7 +2002,6 @@
    return NULL;
    }
    -
    /**
    * gplugin_manager_get_instance:
    *
    @@ -1503,14 +2011,14 @@
    * This is provided so that signals can be connected and should not be tinkered
    * with in any way.
    *
    - * Return Value: (transfer none): The #GObject that is the instance of the
    - * plugin manager.
    + * Returns: (transfer none): The #GObject that is the instance of the plugin
    + * manager.
    */
    GObject *
    -gplugin_manager_get_instance(void) {
    +gplugin_manager_get_instance(void)
    +{
    if(instance)
    return G_OBJECT(instance);
    return NULL;
    }
    -
    --- a/gplugin/gplugin-manager.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-manager.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -29,6 +29,11 @@
    G_BEGIN_DECLS
    +typedef void (*GPluginManagerForeachFunc)(
    + const gchar *id,
    + GSList *plugins,
    + gpointer data);
    +
    void gplugin_manager_append_path(const gchar *path);
    void gplugin_manager_prepend_path(const gchar *path);
    void gplugin_manager_remove_path(const gchar *path);
    @@ -39,17 +44,26 @@
    GList *gplugin_manager_get_paths(void);
    -void gplugin_manager_register_loader(GType type);
    -void gplugin_manager_unregister_loader(GType type);
    +gboolean gplugin_manager_register_loader(GType type, GError **error);
    +gboolean gplugin_manager_unregister_loader(GType type, GError **error);
    void gplugin_manager_refresh(void);
    +void gplugin_manager_foreach(GPluginManagerForeachFunc func, gpointer data);
    +
    GSList *gplugin_manager_find_plugins(const gchar *id);
    -void gplugin_manager_free_plugin_list(GSList *plugins_list);
    +GSList *gplugin_manager_find_plugins_with_version(
    + const gchar *id,
    + const gchar *op,
    + const gchar *version);
    +GSList *gplugin_manager_find_plugins_with_state(GPluginPluginState state);
    GPluginPlugin *gplugin_manager_find_plugin(const gchar *id);
    +GPluginPlugin *gplugin_manager_find_plugin_with_newest_version(const gchar *id);
    -GSList *gplugin_manager_get_plugin_dependencies(GPluginPlugin *plugin, GError **error);
    +GSList *gplugin_manager_get_plugin_dependencies(
    + GPluginPlugin *plugin,
    + GError **error);
    gboolean gplugin_manager_load_plugin(GPluginPlugin *plugin, GError **error);
    gboolean gplugin_manager_unload_plugin(GPluginPlugin *plugin, GError **error);
    @@ -61,4 +75,3 @@
    G_END_DECLS
    #endif /* GPLUGIN_MANAGER_H */
    -
    --- a/gplugin/gplugin-native-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-native-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,30 +15,20 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <gmodule.h>
    +#include <glib/gi18n-lib.h>
    +
    #include <gplugin/gplugin-core.h>
    -#include <gplugin/gplugin-private.h>
    -
    #include <gplugin/gplugin-native-loader.h>
    #include <gplugin/gplugin-native-plugin.h>
    #include <gplugin/gplugin-native-private.h>
    -
    -#include <glib/gi18n.h>
    -
    -#include <gmodule.h>
    +#include <gplugin/gplugin-private.h>
    #define GPLUGIN_QUERY_SYMBOL "gplugin_query"
    #define GPLUGIN_LOAD_SYMBOL "gplugin_load"
    #define GPLUGIN_UNLOAD_SYMBOL "gplugin_unload"
    /**
    - * SECTION:gplugin-native-loader
    - * @Title: Native Loader API
    - * @Short_description: API for the native plugin loader
    - *
    - * Basic API for the native plugin loader.
    - */
    -
    -/**
    * GPLUGIN_TYPE_NATIVE_LOADER:
    *
    * The standard _get_type macro for #GPluginNativeLoader.
    @@ -67,16 +57,22 @@
    * Helpers
    *****************************************************************************/
    static gpointer
    -gplugin_native_loader_lookup_symbol(GModule *module,
    - const gchar *name, GError **error)
    +gplugin_native_loader_lookup_symbol(
    + GModule *module,
    + const gchar *name,
    + GError **error)
    {
    gpointer symbol = NULL;
    g_return_val_if_fail(module != NULL, NULL);
    if(!g_module_symbol(module, name, &symbol)) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0,
    - _("symbol %s was not found"), name);
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("symbol %s was not found"),
    + name);
    return NULL;
    }
    @@ -85,8 +81,10 @@
    }
    static GModule *
    -gplugin_native_loader_open(const gchar *filename, GModuleFlags flags,
    - GError **error)
    +gplugin_native_loader_open(
    + const gchar *filename,
    + GModuleFlags flags,
    + GError **error)
    {
    GModule *module = NULL;
    @@ -97,9 +95,12 @@
    if(error) {
    const gchar *msg = g_module_error();
    - *error = g_error_new(GPLUGIN_DOMAIN, 0,
    - _("Failed to open plugin '%s': %s"),
    - filename, (msg) ? msg : _("Unknown error"));
    + *error = g_error_new(
    + GPLUGIN_DOMAIN,
    + 0,
    + _("Failed to open plugin '%s': %s"),
    + filename,
    + (msg) ? msg : _("Unknown error"));
    }
    return NULL;
    @@ -109,12 +110,13 @@
    * GPluginLoaderInterface API
    *****************************************************************************/
    static GSList *
    -gplugin_native_loader_class_supported_extensions(G_GNUC_UNUSED GPluginLoaderClass *klass) {
    +gplugin_native_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *l)
    +{
    GSList *exts = g_slist_append(NULL, G_MODULE_SUFFIX);
    #if defined(__APPLE__) || defined(__MACH__)
    - /* G_MODULE_SUFFIX only requests so on not windows, and both .so and
    - * .dylib are use on macos, so add dylib if we're on macos.
    + /* G_MODULE_SUFFIX only requests `.so` on not windows, and both .so and
    + * .dylib are used on macos, so add dylib if we're on macos.
    * See: https://gitlab.gnome.org/GNOME/glib/issues/1413
    */
    exts = g_slist_append(exts, "dylib");
    @@ -124,25 +126,28 @@
    }
    static GPluginPluginInfo *
    -gplugin_native_loader_open_and_query(const gchar *filename,
    - GModule **module,
    - GModuleFlags flags,
    - GPluginNativePluginQueryFunc *query,
    - GError **error)
    +gplugin_native_loader_open_and_query(
    + const gchar *filename,
    + GModule **module,
    + GModuleFlags flags,
    + GPluginNativePluginQueryFunc *query,
    + GError **error)
    {
    GPluginPluginInfo *info = NULL;
    *module = gplugin_native_loader_open(filename, flags, error);
    - if (*module == NULL) {
    + if(*module == NULL) {
    return NULL;
    - } else if (error && *error) {
    + } else if(error && *error) {
    g_module_close(*module);
    *module = NULL;
    return NULL;
    }
    - *query = gplugin_native_loader_lookup_symbol(*module, GPLUGIN_QUERY_SYMBOL,
    - error);
    + *query = gplugin_native_loader_lookup_symbol(
    + *module,
    + GPLUGIN_QUERY_SYMBOL,
    + error);
    if((*query == NULL) || (error && *error)) {
    g_module_close(*module);
    @@ -161,9 +166,10 @@
    }
    static GPluginPlugin *
    -gplugin_native_loader_query(GPluginLoader *loader,
    - const gchar *filename,
    - GError **error)
    +gplugin_native_loader_query(
    + GPluginLoader *loader,
    + const gchar *filename,
    + GError **error)
    {
    GPluginPlugin *plugin = NULL;
    GPluginPluginInfo *info = NULL;
    @@ -172,13 +178,20 @@
    GPluginNativePluginUnloadFunc unload = NULL;
    GModule *module = NULL;
    - info = gplugin_native_loader_open_and_query(filename, &module, 0, &query,
    - error);
    + info = gplugin_native_loader_open_and_query(
    + filename,
    + &module,
    + 0,
    + &query,
    + error);
    if(!GPLUGIN_IS_PLUGIN_INFO(info)) {
    - if (error && *error == NULL) {
    - g_set_error_literal(error, GPLUGIN_DOMAIN, 0,
    - _("the query function did not return a "
    - "GPluginPluginInfo instance"));
    + if(error && *error == NULL) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("the query function did not return a "
    + "GPluginPluginInfo instance"));
    }
    return NULL;
    @@ -188,14 +201,20 @@
    g_module_close(module);
    g_object_unref(G_OBJECT(info));
    - info = gplugin_native_loader_open_and_query(filename, &module,
    - G_MODULE_BIND_LOCAL,
    - &query, error);
    + info = gplugin_native_loader_open_and_query(
    + filename,
    + &module,
    + G_MODULE_BIND_LOCAL,
    + &query,
    + error);
    if(!GPLUGIN_IS_PLUGIN_INFO(info)) {
    - if (error && *error == NULL) {
    - g_set_error_literal(error, GPLUGIN_DOMAIN, 0,
    - _("the query function did not return a "
    - "GPluginPluginInfo instance"));
    + if(error && *error == NULL) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("the query function did not return a "
    + "GPluginPluginInfo instance"));
    }
    return NULL;
    @@ -203,9 +222,8 @@
    }
    /* now look for the load symbol */
    - load = gplugin_native_loader_lookup_symbol(module,
    - GPLUGIN_LOAD_SYMBOL,
    - error);
    + load =
    + gplugin_native_loader_lookup_symbol(module, GPLUGIN_LOAD_SYMBOL, error);
    if(error && *error) {
    g_module_close(module);
    g_object_unref(G_OBJECT(info));
    @@ -213,34 +231,39 @@
    }
    /* now look for the unload symbol */
    - unload = gplugin_native_loader_lookup_symbol(module,
    - GPLUGIN_UNLOAD_SYMBOL,
    - error);
    + unload = gplugin_native_loader_lookup_symbol(
    + module,
    + GPLUGIN_UNLOAD_SYMBOL,
    + error);
    if(error && *error) {
    g_module_close(module);
    g_object_unref(G_OBJECT(info));
    return NULL;
    }
    - /* claim ownership of the info object */
    - g_object_ref_sink(G_OBJECT(info));
    -
    /* now create the actual plugin instance */
    - plugin = g_object_new(GPLUGIN_TYPE_NATIVE_PLUGIN,
    - "module", module,
    - "info", info,
    - "load-func", load,
    - "unload-func", unload,
    - "loader", loader,
    - "filename", filename,
    - NULL);
    + /* clang-format off */
    + plugin = g_object_new(
    + GPLUGIN_TYPE_NATIVE_PLUGIN,
    + "module", module,
    + "info", info,
    + "load-func", load,
    + "unload-func", unload,
    + "loader", loader,
    + "filename", filename,
    + NULL);
    + /* clang-format on */
    /* now that the plugin instance owns the info, remove our ref */
    g_object_unref(G_OBJECT(info));
    if(!GPLUGIN_IS_NATIVE_PLUGIN(plugin)) {
    - g_set_error_literal(error, GPLUGIN_DOMAIN, 0,
    - _("failed to create plugin instance"));
    + g_module_close(module);
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("failed to create plugin instance"));
    return NULL;
    }
    @@ -248,9 +271,10 @@
    }
    static gboolean
    -gplugin_native_loader_load(G_GNUC_UNUSED GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - GError **error)
    +gplugin_native_loader_load(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    GPluginNativePluginLoadFunc func;
    @@ -260,7 +284,7 @@
    /* get and call the function */
    g_object_get(G_OBJECT(plugin), "load-func", &func, NULL);
    if(!func(GPLUGIN_NATIVE_PLUGIN(plugin), error)) {
    - if (error && *error == NULL)
    + if(error && *error == NULL)
    g_set_error_literal(error, GPLUGIN_DOMAIN, 0, _("unknown failure"));
    return FALSE;
    @@ -270,9 +294,10 @@
    }
    static gboolean
    -gplugin_native_loader_unload(G_GNUC_UNUSED GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - GError **error)
    +gplugin_native_loader_unload(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    GPluginNativePluginUnloadFunc func;
    @@ -284,16 +309,18 @@
    /* validate the function */
    if(func == NULL) {
    - const char *filename = gplugin_plugin_get_filename(plugin);
    + gchar *filename = gplugin_plugin_get_filename(plugin);
    g_warning(_("unload function for %s is NULL"), filename);
    + g_free(filename);
    +
    return FALSE;
    }
    /* now call the function */
    if(!func(GPLUGIN_NATIVE_PLUGIN(plugin), error)) {
    - if (error && *error == NULL)
    + if(error && *error == NULL)
    g_set_error_literal(error, GPLUGIN_DOMAIN, 0, _("unknown failure"));
    return FALSE;
    @@ -303,16 +330,17 @@
    }
    static void
    -gplugin_native_loader_init(G_GNUC_UNUSED GPluginNativeLoader *loader) {
    +gplugin_native_loader_init(G_GNUC_UNUSED GPluginNativeLoader *loader)
    +{
    }
    static void
    -gplugin_native_loader_class_init(GPluginNativeLoaderClass *klass) {
    - GPluginLoaderClass *loader_class =
    - GPLUGIN_LOADER_CLASS(klass);
    +gplugin_native_loader_class_init(GPluginNativeLoaderClass *klass)
    +{
    + GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
    loader_class->supported_extensions =
    - gplugin_native_loader_class_supported_extensions;
    + gplugin_native_loader_supported_extensions;
    loader_class->query = gplugin_native_loader_query;
    loader_class->load = gplugin_native_loader_load;
    loader_class->unload = gplugin_native_loader_unload;
    --- a/gplugin/gplugin-native-loader.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-native-loader.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -32,9 +32,13 @@
    G_BEGIN_DECLS
    #define GPLUGIN_TYPE_NATIVE_LOADER (gplugin_native_loader_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginNativeLoader, gplugin_native_loader, GPLUGIN, NATIVE_LOADER, GPluginLoader)
    +G_DECLARE_FINAL_TYPE(
    + GPluginNativeLoader,
    + gplugin_native_loader,
    + GPLUGIN,
    + NATIVE_LOADER,
    + GPluginLoader)
    G_END_DECLS
    #endif /* GPLUGIN_NATIVE_LOADER_H */
    -
    --- a/gplugin/gplugin-native-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-native-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,25 +15,13 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#include <gplugin/gplugin-native-plugin.h>
    -
    -#include <gplugin/gplugin-native-private.h>
    -
    -#include <gplugin/gplugin-loader.h>
    -#include <gplugin/gplugin-manager.h>
    +#include <glib/gi18n-lib.h>
    #include <gplugin/gplugin-core.h>
    -
    -#include <glib/gi18n.h>
    -
    -/**
    - * SECTION:gplugin-native-plugin
    - * @Title: Native Plugin API
    - * @Short_description: API for native plugins
    - *
    - * API for use by native plugins. That is plugins written in a compiled
    - * language.
    - */
    +#include <gplugin/gplugin-loader.h>
    +#include <gplugin/gplugin-manager.h>
    +#include <gplugin/gplugin-native-plugin.h>
    +#include <gplugin/gplugin-native-private.h>
    /**
    * GPLUGIN_TYPE_NATIVE_PLUGIN:
    @@ -63,6 +51,7 @@
    GPluginLoader *loader;
    GPluginPluginInfo *info;
    GPluginPluginState state;
    + GError *error;
    };
    /******************************************************************************
    @@ -78,15 +67,19 @@
    PROP_FILENAME = N_PROPERTIES,
    PROP_LOADER,
    PROP_INFO,
    - PROP_STATE
    + PROP_STATE,
    + PROP_ERROR,
    };
    -static GParamSpec *properties[N_PROPERTIES] = {NULL,};
    +static GParamSpec *properties[N_PROPERTIES] = {
    + NULL,
    +};
    /******************************************************************************
    * GPluginPlugin Implementation
    *****************************************************************************/
    static void
    -gplugin_native_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface) {
    +gplugin_native_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface)
    +{
    /* we just override properites from GPluginPlugin */
    }
    @@ -94,12 +87,14 @@
    * GTypeModule Implementation
    *****************************************************************************/
    static gboolean
    -gplugin_native_plugin_load(G_GNUC_UNUSED GTypeModule *module) {
    +gplugin_native_plugin_load(G_GNUC_UNUSED GTypeModule *module)
    +{
    return TRUE;
    }
    static void
    -gplugin_native_plugin_unload(G_GNUC_UNUSED GTypeModule *module) {
    +gplugin_native_plugin_unload(G_GNUC_UNUSED GTypeModule *module)
    +{
    }
    /******************************************************************************
    @@ -109,19 +104,24 @@
    GPluginNativePlugin,
    gplugin_native_plugin,
    G_TYPE_TYPE_MODULE,
    - G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_PLUGIN, gplugin_native_plugin_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE(
    + GPLUGIN_TYPE_PLUGIN,
    + gplugin_native_plugin_iface_init));
    static void
    -gplugin_native_plugin_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *pspec)
    +gplugin_native_plugin_get_property(
    + GObject *obj,
    + guint param_id,
    + GValue *value,
    + GParamSpec *pspec)
    {
    GPluginNativePlugin *plugin = GPLUGIN_NATIVE_PLUGIN(obj);
    switch(param_id) {
    case PROP_MODULE:
    - g_value_set_pointer(value,
    - gplugin_native_plugin_get_module(plugin));
    + g_value_set_pointer(
    + value,
    + gplugin_native_plugin_get_module(plugin));
    break;
    case PROP_LOAD_FUNC:
    g_value_set_pointer(value, plugin->load_func);
    @@ -143,6 +143,10 @@
    case PROP_STATE:
    g_value_set_enum(value, plugin->state);
    break;
    + case PROP_ERROR:
    + g_value_set_boxed(value, plugin->error);
    + break;
    +
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    break;
    @@ -150,8 +154,11 @@
    }
    static void
    -gplugin_native_plugin_set_property(GObject *obj, guint param_id,
    - const GValue *value, GParamSpec *pspec)
    +gplugin_native_plugin_set_property(
    + GObject *obj,
    + guint param_id,
    + const GValue *value,
    + GParamSpec *pspec)
    {
    GPluginNativePlugin *plugin = GPLUGIN_NATIVE_PLUGIN(obj);
    @@ -179,6 +186,9 @@
    case PROP_STATE:
    plugin->state = g_value_get_enum(value);
    break;
    + case PROP_ERROR:
    + plugin->error = g_value_dup_boxed(value);
    + break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    @@ -187,22 +197,28 @@
    }
    static void
    -gplugin_native_plugin_finalize(GObject *obj) {
    +gplugin_native_plugin_finalize(GObject *obj)
    +{
    GPluginNativePlugin *plugin = GPLUGIN_NATIVE_PLUGIN(obj);
    g_clear_pointer(&plugin->filename, g_free);
    g_clear_object(&plugin->loader);
    g_clear_object(&plugin->info);
    + g_clear_error(&plugin->error);
    +
    + g_module_close(plugin->module);
    G_OBJECT_CLASS(gplugin_native_plugin_parent_class)->finalize(obj);
    }
    static void
    -gplugin_native_plugin_init(G_GNUC_UNUSED GPluginNativePlugin *plugin) {
    +gplugin_native_plugin_init(G_GNUC_UNUSED GPluginNativePlugin *plugin)
    +{
    }
    static void
    -gplugin_native_plugin_class_init(GPluginNativePluginClass *klass) {
    +gplugin_native_plugin_class_init(GPluginNativePluginClass *klass)
    +{
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS(klass);
    @@ -219,10 +235,10 @@
    * The GModule instance for this plugin.
    */
    properties[PROP_MODULE] = g_param_spec_pointer(
    - "module", "module handle",
    + "module",
    + "module handle",
    "The GModule instance of the plugin",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginNativePlugin:load-func:
    @@ -230,10 +246,10 @@
    * A function pointer to the load method of the plugin.
    */
    properties[PROP_LOAD_FUNC] = g_param_spec_pointer(
    - "load-func", "load function pointer",
    + "load-func",
    + "load function pointer",
    "address pointer to load function",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginNativePlugin:unload-func:
    @@ -241,10 +257,10 @@
    * A function pointer to the unload method of the plugin.
    */
    properties[PROP_UNLOAD_FUNC] = g_param_spec_pointer(
    - "unload-func", "unload function pointer",
    + "unload-func",
    + "unload function pointer",
    "address pointer to the unload function",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    @@ -253,6 +269,7 @@
    g_object_class_override_property(obj_class, PROP_LOADER, "loader");
    g_object_class_override_property(obj_class, PROP_INFO, "info");
    g_object_class_override_property(obj_class, PROP_STATE, "state");
    + g_object_class_override_property(obj_class, PROP_ERROR, "error");
    }
    /******************************************************************************
    @@ -269,9 +286,9 @@
    * Returns: The %GModule associated with this plugin.
    */
    GModule *
    -gplugin_native_plugin_get_module(GPluginNativePlugin *plugin) {
    +gplugin_native_plugin_get_module(GPluginNativePlugin *plugin)
    +{
    g_return_val_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin), NULL);
    return plugin->module;
    }
    -
    --- a/gplugin/gplugin-native-plugin.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-native-plugin.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -29,11 +29,15 @@
    G_BEGIN_DECLS
    #define GPLUGIN_TYPE_NATIVE_PLUGIN (gplugin_native_plugin_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginNativePlugin, gplugin_native_plugin, GPLUGIN, NATIVE_PLUGIN, GTypeModule)
    +G_DECLARE_FINAL_TYPE(
    + GPluginNativePlugin,
    + gplugin_native_plugin,
    + GPLUGIN,
    + NATIVE_PLUGIN,
    + GTypeModule)
    GModule *gplugin_native_plugin_get_module(GPluginNativePlugin *plugin);
    G_END_DECLS
    #endif /* GPLUGIN_NATIVE_PLUGIN_H */
    -
    --- a/gplugin/gplugin-native-private.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-native-private.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -29,10 +29,12 @@
    G_BEGIN_DECLS
    typedef GPluginPluginInfo *(*GPluginNativePluginQueryFunc)(GError **error);
    -typedef gboolean (*GPluginNativePluginLoadFunc)(GPluginNativePlugin *plugin, GError **error);
    -typedef gboolean (*GPluginNativePluginUnloadFunc)(GPluginNativePlugin *plugin, GError **error);
    +typedef gboolean (
    + *GPluginNativePluginLoadFunc)(GPluginNativePlugin *plugin, GError **error);
    +typedef gboolean (*GPluginNativePluginUnloadFunc)(
    + GPluginNativePlugin *plugin,
    + GError **error);
    G_END_DECLS
    #endif /* GPLUGIN_PRIVATE_H */
    -
    --- a/gplugin/gplugin-native.h.in Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-native.h.in Sat Sep 05 20:05:36 2020 -0500
    @@ -1,18 +1,18 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #ifndef GPLUGIN_NATIVE_H
    #define GPLUGIN_NATIVE_H
    --- a/gplugin/gplugin-options.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-options.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -16,13 +16,12 @@
    */
    #include <glib.h>
    -#include <glib/gi18n.h>
    #include <glib-object.h>
    -
    -#include <gplugin/gplugin-options.h>
    +#include <glib/gi18n-lib.h>
    #include <gplugin/gplugin-core.h>
    #include <gplugin/gplugin-manager.h>
    +#include <gplugin/gplugin-options.h>
    /**
    * SECTION:gplugin-options
    @@ -41,16 +40,18 @@
    static gchar **paths = NULL;
    static gboolean
    -gplugin_options_no_default_paths_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    +gplugin_options_no_default_paths_cb(
    + G_GNUC_UNUSED const gchar *n,
    + G_GNUC_UNUSED const gchar *v,
    + G_GNUC_UNUSED gpointer d,
    + G_GNUC_UNUSED GError **e)
    {
    add_default_paths = FALSE;
    return TRUE;
    }
    +/* clang-format off */
    static GOptionEntry entries[] = {
    {
    "no-default-paths", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    @@ -65,12 +66,14 @@
    NULL, 0, 0, 0, NULL, NULL, NULL
    },
    };
    +/* clang-format on */
    static gboolean
    -gplugin_options_post_parse_cb(G_GNUC_UNUSED GOptionContext *ctx,
    - G_GNUC_UNUSED GOptionGroup *group,
    - G_GNUC_UNUSED gpointer data,
    - G_GNUC_UNUSED GError **error)
    +gplugin_options_post_parse_cb(
    + G_GNUC_UNUSED GOptionContext *ctx,
    + G_GNUC_UNUSED GOptionGroup *group,
    + G_GNUC_UNUSED gpointer data,
    + G_GNUC_UNUSED GError **error)
    {
    gint i = 0;
    @@ -97,23 +100,27 @@
    * g_option_context_add_group(), if you are using g_option_context_parse() to
    * parse your commandline arguments.
    *
    - * GPlugin must be initialized before you call this function.
    + * If gplugin_init() has yet to be called before g_option_context_parse() is
    + * called, gplugin_init() will be called automatically.
    *
    * Return Value: (transfer full): a #GOptionGroup for the commandline arguments
    * recognized by GPlugin.
    */
    GOptionGroup *
    -gplugin_get_option_group(void) {
    +gplugin_get_option_group(void)
    +{
    GOptionGroup *group = NULL;
    - group = g_option_group_new("gplugin", _("GPlugin Options"),
    - _("Show GPlugin Options"), NULL, NULL);
    + group = g_option_group_new(
    + "gplugin",
    + _("GPlugin Options"),
    + _("Show GPlugin Options"),
    + NULL,
    + NULL);
    - g_option_group_set_parse_hooks(group, NULL,
    - gplugin_options_post_parse_cb);
    + g_option_group_set_parse_hooks(group, NULL, gplugin_options_post_parse_cb);
    g_option_group_add_entries(group, entries);
    g_option_group_set_translation_domain(group, GETTEXT_PACKAGE);
    return group;
    }
    -
    --- a/gplugin/gplugin-options.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-options.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -23,7 +23,6 @@
    #define GPLUGIN_OPTIONS_H
    #include <glib.h>
    -#include <glib-object.h>
    G_BEGIN_DECLS
    @@ -32,4 +31,3 @@
    G_END_DECLS
    #endif /* GPLUGIN_OPTIONS_H */
    -
    --- a/gplugin/gplugin-plugin-info.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-plugin-info.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,16 +15,15 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <gplugin/gplugin-core.h>
    +#include <gplugin/gplugin-enums.h>
    #include <gplugin/gplugin-plugin-info.h>
    -#include <gplugin/gplugin-core.h>
    -
    -#include <gplugin/gplugin-enums.h>
    #include <gplugin/gplugin-private.h>
    /**
    * SECTION:gplugin-plugin-info
    * @Title: Plugin Info Objects
    - * @Short_description: information about plugins
    + * @Short_description: information about plugins.
    *
    * #GPluginPluginInfo holds metadata for plugins.
    */
    @@ -57,19 +56,17 @@
    gchar *name;
    gchar *version;
    - GPluginVersionCompareFunc version_func;
    gchar *license_id;
    gchar *license_text;
    gchar *license_url;
    - gchar *icon;
    + gchar *icon_name;
    gchar *summary;
    gchar *description;
    gchar *category;
    gchar **authors;
    - gchar *help;
    gchar *website;
    gchar **dependencies;
    @@ -93,168 +90,185 @@
    PROP_BIND_LOCAL,
    PROP_NAME,
    PROP_VERSION,
    - PROP_VERSION_FUNC,
    PROP_LICENSE_ID,
    PROP_LICENSE_TEXT,
    PROP_LICENSE_URL,
    - PROP_ICON,
    + PROP_ICON_NAME,
    PROP_SUMMARY,
    PROP_DESCRIPTION,
    PROP_CATEGORY,
    PROP_AUTHORS,
    - PROP_HELP,
    PROP_WEBSITE,
    PROP_DEPENDENCIES,
    N_PROPERTIES,
    };
    -static GParamSpec *properties[N_PROPERTIES] = {NULL,};
    +static GParamSpec *properties[N_PROPERTIES] = {
    + NULL,
    +};
    -G_DEFINE_TYPE_WITH_PRIVATE(GPluginPluginInfo, gplugin_plugin_info, G_TYPE_INITIALLY_UNOWNED)
    +G_DEFINE_TYPE_WITH_PRIVATE(
    + GPluginPluginInfo,
    + gplugin_plugin_info,
    + G_TYPE_OBJECT)
    /******************************************************************************
    * Private API
    *****************************************************************************/
    static void
    -gplugin_plugin_info_set_id(GPluginPluginInfo *info, const gchar *id) {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    +gplugin_plugin_info_set_id(GPluginPluginInfo *info, const gchar *id)
    +{
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->id);
    priv->id = g_strdup(id);
    }
    static void
    -gplugin_plugin_info_set_abi_version(GPluginPluginInfo *info,
    - guint32 abi_version)
    +gplugin_plugin_info_set_abi_version(
    + GPluginPluginInfo *info,
    + guint32 abi_version)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    priv->abi_version = abi_version;
    }
    static void
    -gplugin_plugin_info_set_internal(GPluginPluginInfo *info, gboolean internal) {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    +gplugin_plugin_info_set_internal(GPluginPluginInfo *info, gboolean internal)
    +{
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    priv->internal = internal;
    }
    static void
    -gplugin_plugin_info_set_load_on_query(GPluginPluginInfo *info, gboolean loq) {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    +gplugin_plugin_info_set_load_on_query(GPluginPluginInfo *info, gboolean loq)
    +{
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    priv->load_on_query = loq;
    }
    static void
    -gplugin_plugin_info_set_bind_local(GPluginPluginInfo *info,
    - gboolean bind_local)
    +gplugin_plugin_info_set_bind_local(GPluginPluginInfo *info, gboolean bind_local)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    priv->bind_local = bind_local;
    }
    static void
    -gplugin_plugin_info_set_name(GPluginPluginInfo *info, const gchar *name) {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    +gplugin_plugin_info_set_name(GPluginPluginInfo *info, const gchar *name)
    +{
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->name);
    priv->name = g_strdup(name);
    }
    static void
    -gplugin_plugin_info_set_version(GPluginPluginInfo *info,
    - const gchar *version)
    +gplugin_plugin_info_set_version(GPluginPluginInfo *info, const gchar *version)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->version);
    priv->version = g_strdup(version);
    }
    static void
    -gplugin_plugin_info_set_version_func(GPluginPluginInfo *info,
    - GPluginVersionCompareFunc func)
    +gplugin_plugin_info_set_license_id(
    + GPluginPluginInfo *info,
    + const gchar *license_id)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    -
    - priv->version_func = func;
    -}
    -
    -static void
    -gplugin_plugin_info_set_license_id(GPluginPluginInfo *info,
    - const gchar *license_id)
    -{
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->license_id);
    priv->license_id = g_strdup(license_id);
    }
    -
    static void
    -gplugin_plugin_info_set_license_text(GPluginPluginInfo *info,
    - const gchar *license_text)
    +gplugin_plugin_info_set_license_text(
    + GPluginPluginInfo *info,
    + const gchar *license_text)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->license_text);
    priv->license_text = g_strdup(license_text);
    }
    -
    static void
    -gplugin_plugin_info_set_license_url(GPluginPluginInfo *info,
    - const gchar *license_url)
    +gplugin_plugin_info_set_license_url(
    + GPluginPluginInfo *info,
    + const gchar *license_url)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->license_url);
    priv->license_url = g_strdup(license_url);
    }
    static void
    -gplugin_plugin_info_set_icon(GPluginPluginInfo *info, const gchar *icon) {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    +gplugin_plugin_info_set_icon_name(
    + GPluginPluginInfo *info,
    + const gchar *icon_name)
    +{
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    - g_free(priv->icon);
    - priv->icon = g_strdup(icon);
    + g_free(priv->icon_name);
    + priv->icon_name = g_strdup(icon_name);
    }
    static void
    -gplugin_plugin_info_set_summary(GPluginPluginInfo *info,
    - const gchar *summary)
    +gplugin_plugin_info_set_summary(GPluginPluginInfo *info, const gchar *summary)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->summary);
    priv->summary = g_strdup(summary);
    }
    static void
    -gplugin_plugin_info_set_description(GPluginPluginInfo *info,
    - const gchar *description)
    +gplugin_plugin_info_set_description(
    + GPluginPluginInfo *info,
    + const gchar *description)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->description);
    priv->description = g_strdup(description);
    }
    static void
    -gplugin_plugin_info_set_category(GPluginPluginInfo *info,
    - const gchar *category)
    +gplugin_plugin_info_set_category(GPluginPluginInfo *info, const gchar *category)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->category);
    priv->category = g_strdup(category);
    }
    static void
    -gplugin_plugin_info_set_authors(GPluginPluginInfo *info,
    - const gchar * const *authors) {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    +gplugin_plugin_info_set_authors(
    + GPluginPluginInfo *info,
    + const gchar *const *authors)
    +{
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_strfreev(priv->authors);
    @@ -262,29 +276,22 @@
    }
    static void
    -gplugin_plugin_info_set_help(GPluginPluginInfo *info, const gchar *help)
    +gplugin_plugin_info_set_website(GPluginPluginInfo *info, const gchar *website)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    -
    - g_free(priv->help);
    - priv->help = g_strdup(help);
    -}
    -
    -static void
    -gplugin_plugin_info_set_website(GPluginPluginInfo *info,
    - const gchar *website)
    -{
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_free(priv->website);
    priv->website = g_strdup(website);
    }
    static void
    -gplugin_plugin_info_set_dependencies(GPluginPluginInfo *info,
    - const gchar * const *dependencies)
    +gplugin_plugin_info_set_dependencies(
    + GPluginPluginInfo *info,
    + const gchar *const *dependencies)
    {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(info);
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    g_strfreev(priv->dependencies);
    @@ -295,8 +302,11 @@
    * Object Stuff
    *****************************************************************************/
    static void
    -gplugin_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *pspec)
    +gplugin_plugin_info_get_property(
    + GObject *obj,
    + guint param_id,
    + GValue *value,
    + GParamSpec *pspec)
    {
    GPluginPluginInfo *info = GPLUGIN_PLUGIN_INFO(obj);
    @@ -311,12 +321,14 @@
    g_value_set_boolean(value, gplugin_plugin_info_get_internal(info));
    break;
    case PROP_LOQ:
    - g_value_set_boolean(value,
    - gplugin_plugin_info_get_load_on_query(info));
    + g_value_set_boolean(
    + value,
    + gplugin_plugin_info_get_load_on_query(info));
    break;
    case PROP_BIND_LOCAL:
    - g_value_set_boolean(value,
    - gplugin_plugin_info_get_bind_local(info));
    + g_value_set_boolean(
    + value,
    + gplugin_plugin_info_get_bind_local(info));
    break;
    case PROP_NAME:
    g_value_set_string(value, gplugin_plugin_info_get_name(info));
    @@ -324,31 +336,29 @@
    case PROP_VERSION:
    g_value_set_string(value, gplugin_plugin_info_get_version(info));
    break;
    - case PROP_VERSION_FUNC:
    - g_value_set_pointer(value,
    - gplugin_plugin_info_get_version_func(info));
    - break;
    case PROP_LICENSE_ID:
    - g_value_set_string(value,
    - gplugin_plugin_info_get_license_id(info));
    + g_value_set_string(value, gplugin_plugin_info_get_license_id(info));
    break;
    case PROP_LICENSE_TEXT:
    - g_value_set_string(value,
    - gplugin_plugin_info_get_license_text(info));
    + g_value_set_string(
    + value,
    + gplugin_plugin_info_get_license_text(info));
    break;
    case PROP_LICENSE_URL:
    - g_value_set_string(value,
    - gplugin_plugin_info_get_license_url(info));
    + g_value_set_string(
    + value,
    + gplugin_plugin_info_get_license_url(info));
    break;
    - case PROP_ICON:
    - g_value_set_string(value, gplugin_plugin_info_get_icon(info));
    + case PROP_ICON_NAME:
    + g_value_set_string(value, gplugin_plugin_info_get_icon_name(info));
    break;
    case PROP_SUMMARY:
    g_value_set_string(value, gplugin_plugin_info_get_summary(info));
    break;
    case PROP_DESCRIPTION:
    - g_value_set_string(value,
    - gplugin_plugin_info_get_description(info));
    + g_value_set_string(
    + value,
    + gplugin_plugin_info_get_description(info));
    break;
    case PROP_CATEGORY:
    g_value_set_string(value, gplugin_plugin_info_get_category(info));
    @@ -356,15 +366,13 @@
    case PROP_AUTHORS:
    g_value_set_boxed(value, gplugin_plugin_info_get_authors(info));
    break;
    - case PROP_HELP:
    - g_value_set_string(value, gplugin_plugin_info_get_help(info));
    - break;
    case PROP_WEBSITE:
    g_value_set_string(value, gplugin_plugin_info_get_website(info));
    break;
    case PROP_DEPENDENCIES:
    - g_value_set_boxed(value,
    - gplugin_plugin_info_get_dependencies(info));
    + g_value_set_boxed(
    + value,
    + gplugin_plugin_info_get_dependencies(info));
    break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    @@ -373,8 +381,11 @@
    }
    static void
    -gplugin_plugin_info_set_property(GObject *obj, guint param_id,
    - const GValue *value, GParamSpec *pspec)
    +gplugin_plugin_info_set_property(
    + GObject *obj,
    + guint param_id,
    + const GValue *value,
    + GParamSpec *pspec)
    {
    GPluginPluginInfo *info = GPLUGIN_PLUGIN_INFO(obj);
    @@ -389,12 +400,14 @@
    gplugin_plugin_info_set_internal(info, g_value_get_boolean(value));
    break;
    case PROP_LOQ:
    - gplugin_plugin_info_set_load_on_query(info,
    - g_value_get_boolean(value));
    + gplugin_plugin_info_set_load_on_query(
    + info,
    + g_value_get_boolean(value));
    break;
    case PROP_BIND_LOCAL:
    - gplugin_plugin_info_set_bind_local(info,
    - g_value_get_boolean(value));
    + gplugin_plugin_info_set_bind_local(
    + info,
    + g_value_get_boolean(value));
    break;
    case PROP_NAME:
    gplugin_plugin_info_set_name(info, g_value_get_string(value));
    @@ -402,31 +415,29 @@
    case PROP_VERSION:
    gplugin_plugin_info_set_version(info, g_value_get_string(value));
    break;
    - case PROP_VERSION_FUNC:
    - gplugin_plugin_info_set_version_func(info,
    - g_value_get_pointer(value));
    - break;
    case PROP_LICENSE_ID:
    - gplugin_plugin_info_set_license_id(info,
    - g_value_get_string(value));
    + gplugin_plugin_info_set_license_id(info, g_value_get_string(value));
    break;
    case PROP_LICENSE_TEXT:
    - gplugin_plugin_info_set_license_text(info,
    - g_value_get_string(value));
    + gplugin_plugin_info_set_license_text(
    + info,
    + g_value_get_string(value));
    break;
    case PROP_LICENSE_URL:
    - gplugin_plugin_info_set_license_url(info,
    - g_value_get_string(value));
    + gplugin_plugin_info_set_license_url(
    + info,
    + g_value_get_string(value));
    break;
    - case PROP_ICON:
    - gplugin_plugin_info_set_icon(info, g_value_get_string(value));
    + case PROP_ICON_NAME:
    + gplugin_plugin_info_set_icon_name(info, g_value_get_string(value));
    break;
    case PROP_SUMMARY:
    gplugin_plugin_info_set_summary(info, g_value_get_string(value));
    break;
    case PROP_DESCRIPTION:
    - gplugin_plugin_info_set_description(info,
    - g_value_get_string(value));
    + gplugin_plugin_info_set_description(
    + info,
    + g_value_get_string(value));
    break;
    case PROP_CATEGORY:
    gplugin_plugin_info_set_category(info, g_value_get_string(value));
    @@ -434,15 +445,13 @@
    case PROP_AUTHORS:
    gplugin_plugin_info_set_authors(info, g_value_get_boxed(value));
    break;
    - case PROP_HELP:
    - gplugin_plugin_info_set_help(info, g_value_get_string(value));
    - break;
    case PROP_WEBSITE:
    gplugin_plugin_info_set_website(info, g_value_get_string(value));
    break;
    case PROP_DEPENDENCIES:
    - gplugin_plugin_info_set_dependencies(info,
    - g_value_get_boxed(value));
    + gplugin_plugin_info_set_dependencies(
    + info,
    + g_value_get_boxed(value));
    break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    @@ -451,8 +460,10 @@
    }
    static void
    -gplugin_plugin_info_finalize(GObject *obj) {
    - GPluginPluginInfoPrivate *priv = gplugin_plugin_info_get_instance_private(GPLUGIN_PLUGIN_INFO(obj));
    +gplugin_plugin_info_finalize(GObject *obj)
    +{
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(GPLUGIN_PLUGIN_INFO(obj));
    g_clear_pointer(&priv->id, g_free);
    g_clear_pointer(&priv->name, g_free);
    @@ -460,23 +471,25 @@
    g_clear_pointer(&priv->license_id, g_free);
    g_clear_pointer(&priv->license_text, g_free);
    g_clear_pointer(&priv->license_url, g_free);
    - g_clear_pointer(&priv->icon, g_free);
    + g_clear_pointer(&priv->icon_name, g_free);
    g_clear_pointer(&priv->summary, g_free);
    g_clear_pointer(&priv->description, g_free);
    g_clear_pointer(&priv->authors, g_strfreev);
    - g_clear_pointer(&priv->help, g_free);
    g_clear_pointer(&priv->website, g_free);
    g_clear_pointer(&priv->dependencies, g_strfreev);
    + g_clear_pointer(&priv->category, g_free);
    G_OBJECT_CLASS(gplugin_plugin_info_parent_class)->finalize(obj);
    }
    static void
    -gplugin_plugin_info_init(G_GNUC_UNUSED GPluginPluginInfo *info) {
    +gplugin_plugin_info_init(G_GNUC_UNUSED GPluginPluginInfo *info)
    +{
    }
    static void
    -gplugin_plugin_info_class_init(GPluginPluginInfoClass *klass) {
    +gplugin_plugin_info_class_init(GPluginPluginInfoClass *klass)
    +{
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    obj_class->get_property = gplugin_plugin_info_get_property;
    @@ -492,54 +505,57 @@
    * While not required, the recommended convention is to use the following
    * format: &lt;application or library&gt;/&lt;name of the plugin&gt;.
    *
    - * For example, the python loader in GPlugin has an id of
    - * "gplugin/python-plugin-loader".
    + * For example, the Python3 loader in GPlugin has an id of
    + * "gplugin/python3-loader".
    */
    properties[PROP_ID] = g_param_spec_string(
    - "id", "id",
    + "id",
    + "id",
    "The ID of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
    - G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:abi-version:
    *
    * The GPlugin ABI version that the plugin was compiled against.
    *
    - * GPlugin only uses the first byte (0xff000000) of this value. The
    + * GPlugin only uses the first byte (`0xff000000`) of this value. The
    * remaining 3 bytes are available for the application to use.
    *
    * Take the following example from an application:
    *
    + * |[<!-- language="C" -->
    * #define ABI_VERSION (GPLUGIN_NATIVE_ABI_VERSION |
    * (APPLICATION_MAJOR_VERSION << 8) |
    * (APPLICATION_MINOR_VERSION))
    + * ]|
    *
    * The application here uses the thrid and fourth bytes, but could use
    * the second as well.
    */
    properties[PROP_ABI_VERSION] = g_param_spec_uint(
    - "abi-version", "abi_version",
    + "abi-version",
    + "abi_version",
    "The ABI version of the plugin",
    - 0, G_MAXUINT32, 0,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + 0,
    + G_MAXUINT32,
    + 0,
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:internal:
    *
    * Whether or not the plugin is considered an "internal" plugin.
    *
    - * Defaults to FALSE.
    + * Defaults to %FALSE.
    */
    properties[PROP_INTERNAL] = g_param_spec_boolean(
    - "internal", "internal",
    + "internal",
    + "internal",
    "Whether or not the plugin is an internal plugin",
    FALSE,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
    /**
    * GPluginPluginInfo:load-on-query:
    @@ -549,28 +565,28 @@
    * This is used by the loaders and may be useful to your application as
    * well.
    *
    - * Defaults to FALSE.
    + * Defaults to %FALSE.
    */
    properties[PROP_LOQ] = g_param_spec_boolean(
    - "load-on-query", "load-on-query",
    + "load-on-query",
    + "load-on-query",
    "Whether or not the plugin should be loaded when queried",
    FALSE,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
    /**
    * GPluginPluginInfo:bind-local:
    *
    - * Determines whether the plugin should be have it's symbols bound locally.
    + * Determines whether the plugin should be have its symbols bound locally.
    *
    * Note: This should only be used by the native plugin loader.
    */
    properties[PROP_BIND_LOCAL] = g_param_spec_boolean(
    - "bind-local", "bind-local",
    + "bind-local",
    + "bind-local",
    "Whether symbols should be bound locally",
    FALSE,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
    /**
    * GPluginPluginInfo:name:
    @@ -578,34 +594,23 @@
    * The display name of the plugin. This should be a translated string.
    */
    properties[PROP_NAME] = g_param_spec_string(
    - "name", "name",
    + "name",
    + "name",
    "The name of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:version:
    *
    - * The version of the plugin.
    + * The version of the plugin. Preferably a semantic version.
    */
    properties[PROP_VERSION] = g_param_spec_string(
    - "version", "version",
    + "version",
    + "version",
    "The version of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    -
    - /**
    - * GPluginPluginInfo:version-func:
    - *
    - * The function to use to compare versions of this plugin.
    - */
    - properties[PROP_VERSION_FUNC] = g_param_spec_pointer(
    - "version-func", "version-func",
    - "The function that can compare versions of this plugin",
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:license-id:
    @@ -613,58 +618,58 @@
    * The short name of the license.
    *
    * It is recommended to use the identifier of the license from
    - * http://dep.debian.net/deps/dep5/#license-specification and should be
    - * "Other" for licenses that are not mentioned in DEP5.
    + * https://spdx.org/licenses/ and should be "Other" for licenses that are
    + * not listed.
    *
    * If a plugin has multiple license, they should be separated by a pipe
    * (|). In the odd case that you have multiple licenses that are used at
    * the same time, they should be separated by an ampersand (&).
    */
    properties[PROP_LICENSE_ID] = g_param_spec_string(
    - "license-id", "license-id",
    + "license-id",
    + "license-id",
    "The license id of the plugin according to SPDX",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:license-text:
    *
    * The text of the license for this plugin. This should only be used when
    - * the plugin is licensed under a license that is not listed in DEP5.
    + * the plugin is licensed under a license that is not listed at spdx.org.
    */
    properties[PROP_LICENSE_TEXT] = g_param_spec_string(
    - "license-text", "license text",
    + "license-text",
    + "license text",
    "The text of the license for the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:license-url:
    *
    * The url to the text of the license. This should primarily only be used
    - * for licenses not listed in DEP5.
    + * for licenses not listed at spdx.org.
    */
    properties[PROP_LICENSE_URL] = g_param_spec_string(
    - "license-url", "license url",
    + "license-url",
    + "license url",
    "The url to the license of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    - * GPluginPluginInfo:icon:
    + * GPluginPluginInfo:icon-name:
    *
    - * A string representing an icon for the plugin. The actual use of this
    - * is determined by the application/library using GPlugin.
    + * A XDG icon name for the plugin. The actual use of this is determined by
    + * the application/library using GPlugin.
    */
    - properties[PROP_ICON] = g_param_spec_string(
    - "icon", "icon",
    - "The file path of the icon for the plugin",
    + properties[PROP_ICON_NAME] = g_param_spec_string(
    + "icon-name",
    + "icon-name",
    + "The XDG icon name for the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:summary:
    @@ -673,11 +678,11 @@
    * user interface.
    */
    properties[PROP_SUMMARY] = g_param_spec_string(
    - "summary", "summary",
    + "summary",
    + "summary",
    "The summary of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:description:
    @@ -686,11 +691,11 @@
    * information" section in a user interface.
    */
    properties[PROP_DESCRIPTION] = g_param_spec_string(
    - "description", "description",
    + "description",
    + "description",
    "The description of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:category:
    @@ -703,39 +708,26 @@
    * that don't match this category into an "Other" category.
    */
    properties[PROP_CATEGORY] = g_param_spec_string(
    - "category", "category",
    + "category",
    + "category",
    "The category of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:authors:
    *
    - * A gchar ** of the names and email addresses of the authors.
    + * A list of the names and email addresses of the authors.
    *
    * It is recommended to use the RFC 822, 2822 format of:
    - * "First Last <user@domain.com>" with additional authors separated by a
    - * comma.
    + * `"First Last <user@domain.com>"`.
    */
    properties[PROP_AUTHORS] = g_param_spec_boxed(
    - "authors", "authors",
    + "authors",
    + "authors",
    "The authors of the plugin",
    G_TYPE_STRV,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    -
    - /**
    - * GPluginPluginInfo:help:
    - *
    - * The url of the plugin that can be represented in a user interface.
    - */
    - properties[PROP_HELP] = g_param_spec_string(
    - "help", "help",
    - "The help string for the plugin",
    - NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:website:
    @@ -743,11 +735,11 @@
    * The url of the plugin that can be represented in a user interface.
    */
    properties[PROP_WEBSITE] = g_param_spec_string(
    - "website", "website",
    + "website",
    + "website",
    "The website of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    /**
    * GPluginPluginInfo:dependencies:
    @@ -755,11 +747,11 @@
    * A comma separated list of plugin id's that this plugin depends on.
    */
    properties[PROP_DEPENDENCIES] = g_param_spec_boxed(
    - "dependencies", "dependencies",
    + "dependencies",
    + "dependencies",
    "The dependencies of the plugin",
    G_TYPE_STRV,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    }
    @@ -770,25 +762,26 @@
    /**
    * gplugin_plugin_info_new: (skip)
    - * @id: The id of the plugin
    - * @abi_version: The GPlugin ABI version that the plugin uses
    + * @id: The id of the plugin.
    + * @abi_version: The GPlugin ABI version that the plugin uses.
    * @...: name/value pairs of properties to set, followed by %NULL.
    *
    - * Creates a new GPluginPluginInfo instance.
    + * Creates a new #GPluginPluginInfo instance.
    *
    - * Returns: The new GPluginPluginInfo instance.
    + * Returns: (transfer full): The new #GPluginPluginInfo instance.
    */
    /**
    * gplugin_plugin_info_get_id:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the id that the plugin identifies itself as.
    *
    * Returns: The id from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_id(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_id(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -800,7 +793,7 @@
    /**
    * gplugin_plugin_info_get_abi_version:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the ABI or Application Binary Interface version that the plugin
    * is supposed to work against.
    @@ -808,7 +801,8 @@
    * Returns: The abi_version from @info.
    */
    guint32
    -gplugin_plugin_info_get_abi_version(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_abi_version(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), 0);
    @@ -820,16 +814,17 @@
    /**
    * gplugin_plugin_info_get_internal:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns where or not this plugin is is considered an internal plugin. An
    * internal plugin would be something like a plugin loader or another plugin
    * that should not be shown to users.
    *
    - * Returns: TRUE if the plugin is internal, FALSE otherwise.
    + * Returns: %TRUE if the plugin is internal, %FALSE otherwise.
    */
    gboolean
    -gplugin_plugin_info_get_internal(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_internal(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), FALSE);
    @@ -841,17 +836,18 @@
    /**
    * gplugin_plugin_info_get_load_on_query:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns whether or not this plugin should be loaded when queried. This is
    * useful for internal plugins that are adding functionality and should always
    * be turned on. The plugin loaders use this to make sure all plugins can
    * always be loaded.
    *
    - * Returns: TRUE if the plugin should be loaded on query, FALSE otherwise.
    + * Returns: %TRUE if the plugin should be loaded on query, %FALSE otherwise.
    */
    gboolean
    -gplugin_plugin_info_get_load_on_query(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_load_on_query(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), FALSE);
    @@ -863,14 +859,15 @@
    /**
    * gplugin_plugin_info_get_name:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the name of the plugin as specified in @info.
    *
    * Returns: The name from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_name(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_name(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -882,14 +879,15 @@
    /**
    * gplugin_plugin_info_get_version:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the version of the plugin as specified in @info.
    *
    * Returns: The version from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_version(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_version(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -900,36 +898,16 @@
    }
    /**
    - * gplugin_plugin_info_get_version_func: (skip)
    - * @info: #GPluginPluginInfo instance
    - *
    - * Returns the #GPluginVersionCompareFunc used to compare versions of the
    - * plugin.
    - *
    - * Returns: The #GPluginVersionCompareFunc that can compare versions of this
    - * plugins.
    - */
    -GPluginVersionCompareFunc
    -gplugin_plugin_info_get_version_func(GPluginPluginInfo *info) {
    - GPluginPluginInfoPrivate *priv = NULL;
    -
    - g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    -
    - priv = gplugin_plugin_info_get_instance_private(info);
    -
    - return priv->version_func;
    -}
    -
    -/**
    * gplugin_plugin_info_get_license_id:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the liences id for the plugin as specified in @info.
    *
    * Returns: The license-id from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_license_id(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_license_id(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -941,14 +919,15 @@
    /**
    * gplugin_plugin_info_get_license_text:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the license text for the plugin as specified in @info.
    *
    * Returns: The text of the license from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_license_text(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_license_text(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -960,14 +939,15 @@
    /**
    * gplugin_plugin_info_get_license_url:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the url of the license for the plugin as specified in @info
    *
    * Returns: The url of the license from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_license_url(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_license_url(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -978,34 +958,36 @@
    }
    /**
    - * gplugin_plugin_info_get_icon:
    - * @info: #GPluginPluginInfo instance
    + * gplugin_plugin_info_get_icon_name:
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the name of the icon for the plugin as specified in @info.
    *
    - * Returns: The icon from @info.
    + * Returns: The icon name from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_icon(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_icon_name(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    priv = gplugin_plugin_info_get_instance_private(info);
    - return priv->icon;
    + return priv->icon_name;
    }
    /**
    * gplugin_plugin_info_get_summary:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the summery for the plugin as specified in @info.
    *
    * Returns: The summary from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_summary(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_summary(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -1017,14 +999,15 @@
    /**
    * gplugin_plugin_info_get_description:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the description for the plugin as specified in @info.
    *
    * Returns: The description from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_description(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_description(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -1036,14 +1019,15 @@
    /**
    * gplugin_plugin_info_get_category:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the category of the plugin as specified in @info.
    *
    * Returns: The category from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_category(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_category(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -1055,52 +1039,35 @@
    /**
    * gplugin_plugin_info_get_authors:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the authors of the plugin as specified in @info.
    *
    * Returns: (array zero-terminated=1) (transfer none): The authors from @info.
    */
    -const gchar * const *
    -gplugin_plugin_info_get_authors(GPluginPluginInfo *info) {
    +const gchar *const *
    +gplugin_plugin_info_get_authors(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    priv = gplugin_plugin_info_get_instance_private(info);
    - return (const gchar * const *)priv->authors;
    -}
    -
    -/**
    - * gplugin_plugin_info_get_help:
    - * @info: #GPluginPluginInfo instance
    - *
    - * Returns the help text for the plugin as specified in @info.
    - *
    - * Returns: The help from @info.
    - */
    -const gchar *
    -gplugin_plugin_info_get_help(GPluginPluginInfo *info) {
    - GPluginPluginInfoPrivate *priv = NULL;
    -
    - g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    -
    - priv = gplugin_plugin_info_get_instance_private(info);
    -
    - return priv->help;
    + return (const gchar *const *)priv->authors;
    }
    /**
    * gplugin_plugin_info_get_website:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the website for the plugin as specified in @info.
    *
    * Returns: The website from @info.
    */
    const gchar *
    -gplugin_plugin_info_get_website(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_website(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    @@ -1112,35 +1079,37 @@
    /**
    * gplugin_plugin_info_get_dependencies:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * Returns the dependencies of the plugins as specified in @info.
    *
    * Returns: (array zero-terminated=1) (transfer none): The list of
    * dependencies from @info.
    */
    -const gchar * const *
    -gplugin_plugin_info_get_dependencies(GPluginPluginInfo *info) {
    +const gchar *const *
    +gplugin_plugin_info_get_dependencies(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), NULL);
    priv = gplugin_plugin_info_get_instance_private(info);
    - return (const gchar * const *)priv->dependencies;
    + return (const gchar *const *)priv->dependencies;
    }
    /**
    * gplugin_plugin_info_get_bind_local:
    - * @info: #GPluginPluginInfo instance
    + * @info: The #GPluginPluginInfo instance.
    *
    * This function is only used by the native plugin loader.
    *
    - * Returns: TRUE if the plugin has requested to be loaded with it's symbols
    - * bound locally, FALSE if they should bind be bound globally.
    + * Returns: %TRUE if the plugin has requested to be loaded with its symbols
    + * bound locally, %FALSE if they should be bound globally.
    */
    gboolean
    -gplugin_plugin_info_get_bind_local(GPluginPluginInfo *info) {
    +gplugin_plugin_info_get_bind_local(GPluginPluginInfo *info)
    +{
    GPluginPluginInfoPrivate *priv = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), FALSE);
    @@ -1149,4 +1118,3 @@
    return priv->bind_local;
    }
    -
    --- a/gplugin/gplugin-plugin-info.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-plugin-info.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -28,25 +28,31 @@
    G_BEGIN_DECLS
    #define GPLUGIN_TYPE_PLUGIN_INFO (gplugin_plugin_info_get_type())
    -G_DECLARE_DERIVABLE_TYPE(GPluginPluginInfo, gplugin_plugin_info, GPLUGIN, PLUGIN_INFO, GInitiallyUnowned)
    +G_DECLARE_DERIVABLE_TYPE(
    + GPluginPluginInfo,
    + gplugin_plugin_info,
    + GPLUGIN,
    + PLUGIN_INFO,
    + GObject)
    #include <gplugin/gplugin-loader.h>
    #include <gplugin/gplugin-version.h>
    struct _GPluginPluginInfoClass {
    /*< private >*/
    - GInitiallyUnownedClass gparent;
    + GObjectClass parent;
    gpointer reserved[4];
    };
    +/* clang-format off */
    #define gplugin_plugin_info_new(id, abi_version, ...) \
    - GPLUGIN_PLUGIN_INFO( \
    - g_object_new(GPLUGIN_TYPE_PLUGIN_INFO, \
    - "id", (id), \
    - "abi-version", (abi_version), \
    - __VA_ARGS__) \
    - )
    + GPLUGIN_PLUGIN_INFO(g_object_new( \
    + GPLUGIN_TYPE_PLUGIN_INFO, \
    + "id", (id), \
    + "abi-version", (abi_version), \
    + __VA_ARGS__))
    +/* clang-format on */
    const gchar *gplugin_plugin_info_get_id(GPluginPluginInfo *info);
    guint32 gplugin_plugin_info_get_abi_version(GPluginPluginInfo *info);
    @@ -57,17 +63,15 @@
    const gchar *gplugin_plugin_info_get_license_id(GPluginPluginInfo *info);
    const gchar *gplugin_plugin_info_get_license_text(GPluginPluginInfo *info);
    const gchar *gplugin_plugin_info_get_license_url(GPluginPluginInfo *info);
    -const gchar *gplugin_plugin_info_get_icon(GPluginPluginInfo *info);
    +const gchar *gplugin_plugin_info_get_icon_name(GPluginPluginInfo *info);
    const gchar *gplugin_plugin_info_get_summary(GPluginPluginInfo *info);
    const gchar *gplugin_plugin_info_get_description(GPluginPluginInfo *info);
    const gchar *gplugin_plugin_info_get_category(GPluginPluginInfo *info);
    -const gchar * const *gplugin_plugin_info_get_authors(GPluginPluginInfo *info);
    +const gchar *const *gplugin_plugin_info_get_authors(GPluginPluginInfo *info);
    const gchar *gplugin_plugin_info_get_website(GPluginPluginInfo *info);
    -const gchar * const *gplugin_plugin_info_get_dependencies(GPluginPluginInfo *info);
    -GPluginVersionCompareFunc gplugin_plugin_info_get_version_func(GPluginPluginInfo *info);
    -const gchar *gplugin_plugin_info_get_help(GPluginPluginInfo *info);
    +const gchar *const *gplugin_plugin_info_get_dependencies(
    + GPluginPluginInfo *info);
    G_END_DECLS
    #endif /* GPLUGIN_PLUGIN_INFO_H */
    -
    --- a/gplugin/gplugin-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,32 +15,32 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <gplugin/gplugin-enums.h>
    #include <gplugin/gplugin-plugin.h>
    -
    -#include <gplugin/gplugin-enums.h>
    #include <gplugin/gplugin-private.h>
    /**
    * SECTION:gplugin-plugin
    - * @Title: Plugin Objects
    - * @Short_description: abstract plugin implementation
    + * @Title: Plugin Interface
    + * @Short_description: The plugin interface that all plugins must implement.
    *
    - * #GPluginPlugin is an abstract class that tracks the state of a plugin. It
    - * is subclassed by each loader for them to add additional data for their
    + * #GPluginPlugin is an interface that defines the behavior of plugins. It
    + * is implemented by each loader which add additional data for their
    * implementation.
    */
    /**
    * GPluginPluginState:
    - * @GPLUGIN_PLUGIN_STATE_UNKNOWN: The state of the plugin is unknown
    + * @GPLUGIN_PLUGIN_STATE_UNKNOWN: The state of the plugin is unknown.
    * @GPLUGIN_PLUGIN_STATE_ERROR: There was an error loading or unloading the
    - * plugin
    - * @GPLUGIN_PLUGIN_STATE_QUERIED: The plugin has been queried but not loaded
    - * @GPLUGIN_PLUGIN_STATE_REQUERY: The plugin should be requeried
    - * @GPLUGIN_PLUGIN_STATE_LOADED: The plugin is loaded
    - * @GPLUGIN_PLUGIN_STATE_LOAD_FAILED: The plugin failed to load
    + * plugin.
    + * @GPLUGIN_PLUGIN_STATE_QUERIED: The plugin has been queried but not loaded.
    + * @GPLUGIN_PLUGIN_STATE_REQUERY: The plugin should be requeried.
    + * @GPLUGIN_PLUGIN_STATE_LOADED: The plugin is loaded.
    + * @GPLUGIN_PLUGIN_STATE_LOAD_FAILED: The plugin failed to load.
    + * @GPLUGIN_PLUGIN_STATE_UNLOAD_FAILED: The plugin failed to unload.
    *
    - * The expected states of a plugin.
    + * The known states of a plugin.
    */
    /**
    @@ -57,7 +57,8 @@
    /**
    * GPluginPluginInterface:
    - * @state_changed: The class closure for the #GPluginPlugin::state-changed signal.
    + * @state_changed: The class closure for the #GPluginPlugin::state-changed
    + * signal.
    *
    * The interface that defines the behavior of plugins, including properties and
    * signals.
    @@ -70,7 +71,9 @@
    SIG_STATE_CHANGED,
    SIG_LAST,
    };
    -static guint signals[SIG_LAST] = {0, };
    +static guint signals[SIG_LAST] = {
    + 0,
    +};
    G_DEFINE_INTERFACE(GPluginPlugin, gplugin_plugin, G_TYPE_INVALID);
    @@ -78,7 +81,8 @@
    * Object Stuff
    *****************************************************************************/
    static void
    -gplugin_plugin_default_init(GPluginPluginInterface *iface) {
    +gplugin_plugin_default_init(GPluginPluginInterface *iface)
    +{
    GParamSpec *pspec = NULL;
    /**
    @@ -87,11 +91,11 @@
    * The absolute path to the plugin on disk.
    */
    pspec = g_param_spec_string(
    - "filename", "filename",
    + "filename",
    + "filename",
    "The filename of the plugin",
    NULL,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    g_object_interface_install_property(iface, pspec);
    /**
    @@ -100,11 +104,11 @@
    * The #GPluginLoader that loaded this plugin.
    */
    pspec = g_param_spec_object(
    - "loader", "loader",
    + "loader",
    + "loader",
    "The loader for this plugin",
    GPLUGIN_TYPE_LOADER,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    g_object_interface_install_property(iface, pspec);
    /**
    @@ -113,11 +117,11 @@
    * The #GPluginPluginInfo from this plugin.
    */
    pspec = g_param_spec_object(
    - "info", "info",
    + "info",
    + "info",
    "The information for the plugin",
    GPLUGIN_TYPE_PLUGIN_INFO,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    g_object_interface_install_property(iface, pspec);
    /**
    @@ -126,12 +130,25 @@
    * The #GPluginPluginState that this plugin is in.
    */
    pspec = g_param_spec_enum(
    - "state", "state",
    + "state",
    + "state",
    "The state of the plugin",
    GPLUGIN_TYPE_PLUGIN_STATE,
    GPLUGIN_PLUGIN_STATE_UNKNOWN,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
    + g_object_interface_install_property(iface, pspec);
    +
    + /**
    + * GPluginPlugin:error:
    + *
    + * A #GError returned the plugin if it failed to load or unload.
    + */
    + pspec = g_param_spec_boxed(
    + "error",
    + "error",
    + "A GError returned from the load or unload function",
    + G_TYPE_ERROR,
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
    g_object_interface_install_property(iface, pspec);
    /**
    @@ -142,18 +159,18 @@
    *
    * Emitted when @plugin changes state.
    */
    - signals[SIG_STATE_CHANGED] =
    - g_signal_new("state-changed",
    - GPLUGIN_TYPE_PLUGIN,
    - G_SIGNAL_RUN_LAST,
    - G_STRUCT_OFFSET(GPluginPluginInterface, state_changed),
    - NULL,
    - NULL,
    - NULL,
    - G_TYPE_NONE,
    - 2,
    - GPLUGIN_TYPE_PLUGIN_STATE,
    - GPLUGIN_TYPE_PLUGIN_STATE);
    + signals[SIG_STATE_CHANGED] = g_signal_new(
    + "state-changed",
    + GPLUGIN_TYPE_PLUGIN,
    + G_SIGNAL_RUN_LAST,
    + G_STRUCT_OFFSET(GPluginPluginInterface, state_changed),
    + NULL,
    + NULL,
    + NULL,
    + G_TYPE_NONE,
    + 2,
    + GPLUGIN_TYPE_PLUGIN_STATE,
    + GPLUGIN_TYPE_PLUGIN_STATE);
    }
    /******************************************************************************
    @@ -162,14 +179,15 @@
    /**
    * gplugin_plugin_get_filename:
    - * @plugin: #GPluginPlugin instance
    + * @plugin: The #GPluginPlugin instance.
    *
    * Returns the filename that @plugin was loaded from.
    *
    - * Returns: (transfer full): The filename of @plugin
    + * Returns: (transfer full): The filename of @plugin.
    */
    gchar *
    -gplugin_plugin_get_filename(GPluginPlugin *plugin) {
    +gplugin_plugin_get_filename(GPluginPlugin *plugin)
    +{
    gchar *filename = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), NULL);
    @@ -181,14 +199,15 @@
    /**
    * gplugin_plugin_get_loader:
    - * @plugin: #GPluginPlugin instance
    + * @plugin: The #GPluginPlugin instance.
    *
    * Returns the #GPluginLoader that loaded @plugin.
    *
    - * Returns: (transfer full): The #GPluginLoader that loaded @plugin
    + * Returns: (transfer full): The #GPluginLoader that loaded @plugin.
    */
    GPluginLoader *
    -gplugin_plugin_get_loader(GPluginPlugin *plugin) {
    +gplugin_plugin_get_loader(GPluginPlugin *plugin)
    +{
    GPluginLoader *loader = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), NULL);
    @@ -200,14 +219,15 @@
    /**
    * gplugin_plugin_get_info:
    - * @plugin: #GPluginPlugin instance
    + * @plugin: The #GPluginPlugin instance.
    *
    * Returns the #GPluginPluginInfo for @plugin.
    *
    - * Returns: (transfer full): The #GPluginPluginInfo instance for @plugin
    + * Returns: (transfer full): The #GPluginPluginInfo instance for @plugin.
    */
    GPluginPluginInfo *
    -gplugin_plugin_get_info(GPluginPlugin *plugin) {
    +gplugin_plugin_get_info(GPluginPlugin *plugin)
    +{
    GPluginPluginInfo *info = NULL;
    g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), NULL);
    @@ -219,17 +239,20 @@
    /**
    * gplugin_plugin_get_state:
    - * @plugin: #GPluginPlugin instance
    + * @plugin: The #GPluginPlugin instance.
    *
    - * Gets the current state of @plugin
    + * Gets the current state of @plugin.
    *
    - * Returns: (transfer full): The current state of @plugin
    + * Returns: (transfer full): The current state of @plugin.
    */
    GPluginPluginState
    -gplugin_plugin_get_state(GPluginPlugin *plugin) {
    +gplugin_plugin_get_state(GPluginPlugin *plugin)
    +{
    GPluginPluginState state = GPLUGIN_PLUGIN_STATE_UNKNOWN;
    - g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), GPLUGIN_PLUGIN_STATE_UNKNOWN);
    + g_return_val_if_fail(
    + GPLUGIN_IS_PLUGIN(plugin),
    + GPLUGIN_PLUGIN_STATE_UNKNOWN);
    g_object_get(G_OBJECT(plugin), "state", &state, NULL);
    @@ -238,14 +261,15 @@
    /**
    * gplugin_plugin_set_state:
    - * @plugin: #GPluginPlugin instance
    - * @state: new #GPluginPluginState for @plugin
    + * @plugin: The #GPluginPlugin instance.
    + * @state: A new #GPluginPluginState for @plugin.
    *
    * Changes the state of @plugin to @state. This function should only be called
    * by loaders.
    */
    void
    -gplugin_plugin_set_state(GPluginPlugin *plugin, GPluginPluginState state) {
    +gplugin_plugin_set_state(GPluginPlugin *plugin, GPluginPluginState state)
    +{
    GPluginPluginState oldstate = GPLUGIN_PLUGIN_STATE_UNKNOWN;
    g_return_if_fail(GPLUGIN_IS_PLUGIN(plugin));
    @@ -254,6 +278,26 @@
    g_object_set(G_OBJECT(plugin), "state", state, NULL);
    - g_signal_emit(plugin, signals[SIG_STATE_CHANGED], 0,
    - oldstate, state);
    + g_signal_emit(plugin, signals[SIG_STATE_CHANGED], 0, oldstate, state);
    }
    +
    +/**
    + * gplugin_plugin_get_error:
    + * @plugin: The #GPluginPlugin instance.
    + *
    + * Gets the #GError, if any, that the plugin returned during load or unload.
    + *
    + * Returns: (transfer full): The #GError the plugin returned during load or
    + * unload, or %NULL if no error occurred.
    + */
    +GError *
    +gplugin_plugin_get_error(GPluginPlugin *plugin)
    +{
    + GError *error = NULL;
    +
    + g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), NULL);
    +
    + g_object_get(G_OBJECT(plugin), "error", &error, NULL);
    +
    + return error;
    +}
    --- a/gplugin/gplugin-plugin.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-plugin.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -25,6 +25,7 @@
    #include <glib.h>
    #include <glib-object.h>
    +/* clang-format off */
    typedef enum /*< prefix=GPLUGIN_PLUGIN_STATE,underscore_name=GPLUGIN_PLUGIN_STATE >*/ {
    GPLUGIN_PLUGIN_STATE_UNKNOWN = -1,
    GPLUGIN_PLUGIN_STATE_ERROR = 0,
    @@ -32,10 +33,12 @@
    GPLUGIN_PLUGIN_STATE_REQUERY,
    GPLUGIN_PLUGIN_STATE_LOADED,
    GPLUGIN_PLUGIN_STATE_LOAD_FAILED,
    + GPLUGIN_PLUGIN_STATE_UNLOAD_FAILED,
    /*< private >*/
    GPLUGIN_PLUGIN_STATES, /*< skip >*/
    } GPluginPluginState;
    +/* clang-format on */
    G_BEGIN_DECLS
    @@ -43,15 +46,18 @@
    G_DECLARE_INTERFACE(GPluginPlugin, gplugin_plugin, GPLUGIN, PLUGIN, GObject)
    /* circular dependencies suck... */
    +#include <gplugin/gplugin-loader.h>
    #include <gplugin/gplugin-plugin-info.h>
    -#include <gplugin/gplugin-loader.h>
    struct _GPluginPluginInterface {
    /*< private >*/
    GTypeInterface parent;
    /*< public >*/
    - void (*state_changed)(GPluginPlugin *plugin, GPluginPluginState oldstate, GPluginPluginState newstate);
    + void (*state_changed)(
    + GPluginPlugin *plugin,
    + GPluginPluginState oldstate,
    + GPluginPluginState newstate);
    /*< private >*/
    gpointer reserved[8];
    @@ -64,7 +70,8 @@
    GPluginPluginState gplugin_plugin_get_state(GPluginPlugin *plugin);
    void gplugin_plugin_set_state(GPluginPlugin *plugin, GPluginPluginState state);
    +GError *gplugin_plugin_get_error(GPluginPlugin *plugin);
    +
    G_END_DECLS
    #endif /* GPLUGIN_PLUGIN_H */
    -
    --- a/gplugin/gplugin-private.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-private.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,10 +19,11 @@
    /* A GSignalAccumulator that stops emission if a handler returns FALSE */
    gboolean
    -gplugin_boolean_accumulator(G_GNUC_UNUSED GSignalInvocationHint *hint,
    - GValue *return_accu,
    - const GValue *handler_return,
    - G_GNUC_UNUSED gpointer data)
    +gplugin_boolean_accumulator(
    + G_GNUC_UNUSED GSignalInvocationHint *hint,
    + GValue *return_accu,
    + const GValue *handler_return,
    + G_GNUC_UNUSED gpointer data)
    {
    gboolean continue_emission;
    gboolean handler_returned;
    @@ -33,4 +34,3 @@
    return continue_emission;
    }
    -
    --- a/gplugin/gplugin-private.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-private.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -14,30 +14,33 @@
    * You should have received a copy of the GNU Lesser General Public
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -
    -#if !defined(GPLUGIN_GLOBAL_HEADER_INSIDE) && !defined(GPLUGIN_COMPILATION)
    -#error "only <gplugin.h> may be included directly"
    -#endif
    -
    #ifndef GPLUGIN_PRIVATE_H
    #define GPLUGIN_PRIVATE_H
    #include <glib.h>
    #include <glib-object.h>
    +/* this gets included by some tests so we need to trick the headers to accept
    + * it.
    + */
    +#define GPLUGIN_GLOBAL_HEADER_INSIDE
    +#include <gplugin/gplugin-plugin-info.h>
    #include <gplugin/gplugin-plugin.h>
    -#include <gplugin/gplugin-plugin-info.h>
    +#undef GPLUGIN_GLOBAL_HEADER_INSIDE
    G_BEGIN_DECLS
    void gplugin_manager_private_init(void);
    void gplugin_manager_private_uninit(void);
    -gboolean gplugin_boolean_accumulator(GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer data);
    +gboolean gplugin_boolean_accumulator(
    + GSignalInvocationHint *hint,
    + GValue *return_accu,
    + const GValue *handler_return,
    + gpointer data);
    gboolean gplugin_plugin_info_get_bind_local(GPluginPluginInfo *info);
    G_END_DECLS
    #endif /* GPLUGIN_PRIVATE_H */
    -
    --- a/gplugin/gplugin-query.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,350 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <glib.h>
    -#include <glib/gi18n.h>
    -
    -#include <stdio.h>
    -#include <stdlib.h>
    -#include <string.h>
    -
    -#include <gplugin.h>
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -static gint verbosity = 0;
    -static gboolean show_internal = FALSE;
    -static gboolean output_paths = FALSE;
    -static gboolean exit_early = FALSE;
    -
    -/******************************************************************************
    - * Helpers
    - *****************************************************************************/
    -static gboolean
    -verbosity_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    -{
    - verbosity++;
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -full_verbosity_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    -{
    - verbosity = 1 << 11;
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -internal_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    -{
    - show_internal = TRUE;
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -version_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    -{
    - printf("gplugin-query %s\n", GPLUGIN_VERSION);
    -
    - exit_early = TRUE;
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -list_cb(G_GNUC_UNUSED const gchar *n,
    - G_GNUC_UNUSED const gchar *v,
    - G_GNUC_UNUSED gpointer d,
    - G_GNUC_UNUSED GError **e)
    -{
    - output_paths = TRUE;
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -output_plugin(const gchar *id) {
    - GSList *plugins = NULL, *l = NULL;
    - gboolean first = TRUE, header_output = FALSE;
    -
    - #define FORMAT "%-13s"
    - #define MAIN_FORMAT_NEL " " FORMAT ": "
    - #define MAIN_FORMAT MAIN_FORMAT_NEL "%s\n"
    - #define STR_OR_EMPTY(str) ((str) ? (str) : "")
    -
    - plugins = gplugin_manager_find_plugins(id);
    - if(plugins == NULL) {
    - printf("%s not found\n", id);
    -
    - return FALSE;
    - }
    -
    - for(l = plugins; l; l = l->next) {
    - GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data);
    - GPluginPluginInfo *info = gplugin_plugin_get_info(plugin);
    - gboolean internal, loq;
    - guint32 abi_version;
    - gchar *name, *version;
    - gchar *license_id, *license_text, *license_url;
    - gchar *icon, *summary, *description, *category, *website;
    - gchar **authors, **dependencies;
    - gint i = 0;
    -
    - internal = gplugin_plugin_info_get_internal(info);
    -
    - if(!show_internal && internal)
    - continue;
    -
    - if(!header_output) {
    - printf("%s:\n", id);
    - header_output = TRUE;
    - }
    -
    - g_object_get(G_OBJECT(info),
    - "abi-version", &abi_version,
    - "load-on-query", &loq,
    - "name", &name,
    - "version", &version,
    - "license-id", &license_id,
    - "license-text", &license_text,
    - "license-url", &license_url,
    - "icon", &icon,
    - "summary", &summary,
    - "description", &description,
    - "category", &category,
    - "authors", &authors,
    - "website", &website,
    - "dependencies", &dependencies,
    - NULL);
    -
    - if(!first)
    - printf("\n");
    -
    - printf(MAIN_FORMAT, "name", STR_OR_EMPTY(name));
    - if(verbosity > 0)
    - printf(MAIN_FORMAT, "category", STR_OR_EMPTY(category));
    - printf(MAIN_FORMAT, "version", STR_OR_EMPTY(version));
    - if(verbosity > 0) {
    - printf(MAIN_FORMAT, "license", STR_OR_EMPTY(license_id));
    - printf(MAIN_FORMAT, "license url", STR_OR_EMPTY(license_url));
    - }
    - printf(MAIN_FORMAT, "summary", STR_OR_EMPTY(summary));
    - if(verbosity > 0) {
    - printf(MAIN_FORMAT_NEL, "authors");
    - if(authors) {
    - for(i = 0; authors[i]; i++) {
    - if(i > 0)
    - printf(" ");
    - printf("%s\n", STR_OR_EMPTY(authors[i]));
    - }
    - } else {
    - printf("\n");
    - }
    - printf(MAIN_FORMAT, "website", STR_OR_EMPTY(website));
    - }
    - if(verbosity > 1) {
    - printf(MAIN_FORMAT, "filename",
    - STR_OR_EMPTY(gplugin_plugin_get_filename(plugin)));
    - }
    - if(verbosity > 2) {
    - GPluginLoader *loader = gplugin_plugin_get_loader(plugin);
    -
    - printf(MAIN_FORMAT_NEL "%08x\n", "abi version", abi_version);
    - printf(MAIN_FORMAT, "internal", (internal) ? "yes" : "no");
    - printf(MAIN_FORMAT, "load on query", (loq) ? "yes" : "no");
    - printf(MAIN_FORMAT, "loader", G_OBJECT_TYPE_NAME(loader));
    -
    - g_object_unref(G_OBJECT(loader));
    - }
    - if(verbosity > 0)
    - printf(MAIN_FORMAT, "description", STR_OR_EMPTY(description));
    - if(verbosity > 2) {
    - printf(MAIN_FORMAT_NEL, "dependencies");
    - if(dependencies) {
    - for(i = 0; dependencies[i]; i++) {
    - if(i > 0)
    - printf(" ");
    - printf("%s\n", STR_OR_EMPTY(dependencies[i]));
    - }
    - } else {
    - printf("\n");
    - }
    - }
    -
    - g_free(name);
    - g_free(version);
    - g_free(license_id);
    - g_free(license_text);
    - g_free(license_url);
    - g_free(icon);
    - g_free(summary);
    - g_free(description);
    - g_free(category);
    - g_strfreev(authors);
    - g_free(website);
    - g_strfreev(dependencies);
    - g_object_unref(G_OBJECT(info));
    -
    - if(first)
    - first = FALSE;
    - }
    -
    - gplugin_manager_free_plugin_list(plugins);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -output_plugins(GList *plugins) {
    - GList *l = NULL;
    - gboolean ret = TRUE, first = TRUE;
    -
    - for(l = plugins; l; l = l->next) {
    - if(!first)
    - printf("\n");
    -
    - if(!output_plugin(l->data))
    - ret = FALSE;
    -
    - if(first)
    - first = FALSE;
    - }
    -
    - return ret;
    -}
    -
    -/******************************************************************************
    - * Main Stuff
    - *****************************************************************************/
    -static GOptionEntry entries[] = {
    - {
    - "internal", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    - internal_cb, N_("Show internal plugins"),
    - NULL,
    - }, {
    - "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    - verbosity_cb, N_("Increase verbosity"),
    - NULL,
    - }, {
    - "full-verbose", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    - full_verbosity_cb, N_("Increase verbosity to eleven"),
    - NULL,
    - }, {
    - "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    - version_cb, N_("Display the version and exit"),
    - NULL,
    - }, {
    - "list", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
    - list_cb, N_("Display all search paths and exit"),
    - NULL,
    - }, {
    - NULL, 0, 0, 0, NULL, NULL, NULL,
    - }
    -};
    -
    -gint
    -main(gint argc, gchar **argv) {
    - GError *error = NULL;
    - GOptionContext *ctx = NULL;
    - GOptionGroup *group = NULL;
    - gint i = 0, ret = 0;
    -
    - gplugin_init();
    -
    - ctx = g_option_context_new("PLUGIN-ID...");
    - g_option_context_set_summary(ctx, _("Query installed plugins"));
    - g_option_context_set_translation_domain(ctx, GETTEXT_PACKAGE);
    - g_option_context_add_main_entries(ctx, entries, NULL);
    -
    - group = gplugin_get_option_group();
    - g_option_context_add_group(ctx, group);
    -
    - g_option_context_parse(ctx, &argc, &argv, &error);
    - g_option_context_free(ctx);
    -
    - if(error) {
    - fprintf(stderr, "%s\n", error->message);
    -
    - g_error_free(error);
    -
    - gplugin_uninit();
    -
    - return EXIT_FAILURE;
    - }
    -
    - if(output_paths) {
    - GList *path = NULL;
    -
    - for(path = gplugin_manager_get_paths(); path; path = path->next) {
    - printf("%s\n", (gchar *)path->data);
    - }
    -
    - exit_early = TRUE;
    - }
    -
    - if(exit_early) {
    - gplugin_uninit();
    -
    - return 0;
    - }
    -
    - gplugin_manager_refresh();
    -
    - /* check if the user gave us atleast one plugin, and output them */
    - if(argc > 1) {
    - GQueue *plugins = g_queue_new();
    -
    - for(i = 1; i < argc; i++) {
    - if(!argv[i])
    - continue;
    -
    - if(strlen(argv[i]) == 0)
    - continue;
    -
    - g_queue_push_tail(plugins, argv[i]);
    - }
    -
    - if(!output_plugins(plugins->head))
    - ret = EXIT_FAILURE;
    - } else {
    - GList *plugins = gplugin_manager_list_plugins();
    -
    - if(!output_plugins(plugins))
    - ret = EXIT_FAILURE;
    - }
    -
    - gplugin_uninit();
    -
    - return ret;
    -}
    -
    --- a/gplugin/gplugin-version.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-version.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -18,47 +18,47 @@
    /**
    * SECTION:gplugin-version
    * @Title: Version Information
    - * @Short_description: variables and functions to check the GPlugin version
    + * @Short_description: variables and functions to check the version of GPlugin.
    *
    - * GPlugin provides version information, primarily useful in configure
    - * checks for builds that have a configure script. Applications will
    - * not typically use the features described here.
    + * GPlugin provided version information, primarily useful when building against
    + * GPlugin. Applications will not typically use the features described here
    + * unless checking for new versions during builds.
    */
    +#include <stdlib.h>
    +
    +#include <glib/gi18n-lib.h>
    +
    #include <gplugin/gplugin-core.h>
    #include <gplugin/gplugin-version.h>
    -#include <stdlib.h>
    -
    -#include <glib/gi18n.h>
    -
    /******************************************************************************
    * Globals
    *****************************************************************************/
    GRegex *regex = NULL;
    -static const gchar *version_pattern = "^(?P<major>\\d+)(\\." \
    - "(?P<minor>\\d+)" \
    - "(\\.(?P<micro>\\d+)(?P<extra>.*))?)?$";
    -
    +static const gchar *version_pattern = "^(?P<major>\\d+)(\\."
    + "(?P<minor>\\d+)"
    + "(\\.(?P<micro>\\d+)(?P<extra>.*))?)?$";
    /******************************************************************************
    * Helpers
    *****************************************************************************/
    static void
    -gplugin_version_lazy_init(void) {
    +gplugin_version_lazy_init(void)
    +{
    static volatile gsize init_volatile = 0;
    if(g_once_init_enter(&init_volatile)) {
    GError *error = NULL;
    - regex = g_regex_new(version_pattern, G_REGEX_NO_AUTO_CAPTURE,
    - 0, &error);
    + regex =
    + g_regex_new(version_pattern, G_REGEX_NO_AUTO_CAPTURE, 0, &error);
    if(error) {
    - g_warning(_("Failed to initialize the version regex: %s"),
    - (error->message) ? error->message :
    - _("unknown"));
    + g_warning(
    + _("Failed to initialize the version regex: %s"),
    + (error->message) ? error->message : _("unknown"));
    g_error_free(error);
    }
    @@ -66,20 +66,57 @@
    }
    }
    -static gboolean
    -gplugin_version_parser(const gchar *v, gint *major, gint *minor, gint *micro,
    - gchar **extra, GError **error)
    +/*< private >
    + * gplugin_version_parser:
    + * @version: The string version to parse.
    + * @major: (out) (nullable): A return gint pointer for the major version.
    + * @minor: (out) (nullable): A return gint pointer for the minor version.
    + * @micro: (out) (nullable): A return gint pointer for the micro version.
    + * @extra: (out) (nullable): A return gchar * pointer for any extra version
    + * info.
    + *
    + * Attempts to parse a version string into its @major, @minor, @micro, and
    + * @extra parts. If @version doesn't match a semantic version string, @major
    + * will be set to %-1, @minor will be set to %0, and @micro will be set to %0.
    + */
    +static void
    +gplugin_version_parser(
    + const gchar *version,
    + gint *major,
    + gint *minor,
    + gint *micro,
    + gchar **extra)
    {
    GMatchInfo *info = NULL;
    gboolean matches = FALSE;
    gchar *temp = NULL;
    - matches = g_regex_match(regex, v, 0, &info);
    + /* initialize everything to our failed state */
    + if(major != NULL) {
    + *major = -1;
    + }
    +
    + if(minor != NULL) {
    + *minor = 0;
    + }
    +
    + if(micro != NULL) {
    + *micro = 0;
    + }
    +
    + if(version == NULL) {
    + /* if a version was not provided, return our failed values */
    + return;
    + }
    +
    + matches = g_regex_match(regex, version, 0, &info);
    if(!matches) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0,
    - _("%s does not match the version regex"), v);
    + /* If we failed to match the regex, free info and return our failed
    + * values.
    + */
    + g_match_info_unref(info);
    - return FALSE;
    + return;
    }
    /* grab the major version */
    @@ -104,12 +141,11 @@
    }
    /* grab the extra version */
    - if(extra)
    + if(extra) {
    *extra = g_match_info_fetch_named(info, "extra");
    + }
    g_match_info_unref(info);
    -
    - return TRUE;
    }
    /******************************************************************************
    @@ -148,72 +184,97 @@
    /**
    * GPLUGIN_VERSION_CHECK:
    - * @major: the major version to compare for
    - * @minor: the minor version to compare for
    - * @micro: the micro version to compare for
    + * @major: The major version to compare for.
    + * @minor: The minor version to compare for.
    + * @micro: The micro version to compare for.
    *
    * Checks the version of the GPlugin library that is being compiled
    - * against. See gplugin_version_compare() for a runtime check.
    + * against.
    *
    * Returns: %TRUE if the version of the GPlugin header files
    * is the same as or newer than the passed-in version.
    */
    /**
    - * GPluginVersionCompareFunc:
    - * @v1: The first version to compare
    - * @v2: The second version to compare
    - * @error: A #GError return address if there are any errors.
    + * gplugin_version_check:
    + * @major: The required major version.
    + * @minor: The required minor version.
    + * @micro: The required micro version.
    + *
    + * Checks that the GPlugin library in use is compatible with the given version.
    + * Generally you would pass in the constants #GPLUGIN_MAJOR_VERSION,
    + * #GPLUGIN_MINOR_VERSION, #GPLUGIN_MICRO_VERSION as the three arguments to
    + * this function; that produces a check that the library in use is compatible
    + * with the version of GPlugin the application or module was compiled against.
    + *
    + * Compatibility is defined by two things: first the version of the running
    + * library is newer than the version @major.@minor.@micro. Second the running
    + * library must be binary compatible with the version @major.@minor.@micro
    + * (same major version).
    *
    - * #GPluginVersionCompareFunc is used to compare two versions of a plugin. It
    - * should return -1 if @v1 is greater than @v2, 0 if @v1 is equal to @v2, and
    - * 1 if @v1 is less than @v2.
    - *
    - * Returns: -1 if @v1 is greater than @v2, 0 if @v1 is equal to @v1, and 1 if
    - * @v1 is less than @v2.
    + * Returns: %NULL if the GPlugin library is compatible with the given version,
    + * or a string describing the version mismatch. The returned string
    + * is owned by GPlugin and must not be modified or freed.
    */
    +const gchar *
    +gplugin_version_check(guint major, guint minor, guint micro)
    +{
    + if(major > GPLUGIN_MAJOR_VERSION) {
    + return "gplugin version too old (major mismatch)";
    + }
    +
    + /* disable the pvs-studio suppression after 1.0.0 is released */
    + if(major < GPLUGIN_MAJOR_VERSION) { //-V547
    + return "gplugin version too new (major mismatch)";
    + }
    +
    + if(minor > GPLUGIN_MINOR_VERSION) {
    + return "gplugin version too old (minor mismatch)";
    + }
    +
    + if(minor == GPLUGIN_MINOR_VERSION && micro > GPLUGIN_MICRO_VERSION) {
    + return "gplugin version too old (micro mismatch)";
    + }
    +
    + return NULL;
    +}
    /**
    * gplugin_version_compare:
    - * @v1: The first version to compare
    - * @v2: The second version to compare
    - * @error: A #GError return address if there are any errors.
    + * @v1: The first version to compare.
    + * @v2: The second version to compare.
    *
    - * The default #GPluginVersionCompareFunc. It handles the typical
    - * MAJOR.MINOR.MICRO format and ignore any characters after the micro version.
    + * A semantic version checker which ignores any characters after the micro
    + * version.
    *
    - * Returns: -1 if @v1 is greater than @v2, 0 if @v1 is equal to @v1, and 1 if
    - * @v1 is less than @v2.
    + * Returns: less than 0 if @v1 is less than @v2, 0 if @v1 is equal to @v1, and
    + * greater than 0 if @v1 is greater than @v2.
    */
    gint
    -gplugin_version_compare(const gchar *v1, const gchar *v2, GError **error) {
    +gplugin_version_compare(const gchar *v1, const gchar *v2)
    +{
    gint v1_maj = 0, v1_min = 0, v1_mic = 0;
    gint v2_maj = 0, v2_min = 0, v2_mic = 0;
    gint t = 0;
    - g_return_val_if_fail(v1 != NULL, 1);
    - g_return_val_if_fail(v2 != NULL, 2);
    -
    - if(regex == NULL)
    + if(regex == NULL) {
    gplugin_version_lazy_init();
    + }
    - /* make sure v1 matches the regex */
    - if(!gplugin_version_parser(v1, &v1_maj, &v1_min, &v1_mic, NULL, error))
    - return 1;
    + /* parse v1 */
    + gplugin_version_parser(v1, &v1_maj, &v1_min, &v1_mic, NULL);
    - /* make sure v2 matches the regex */
    - if(!gplugin_version_parser(v2, &v2_maj, &v2_min, &v2_mic, NULL, error))
    - return -1;
    + /* parse v2 */
    + gplugin_version_parser(v2, &v2_maj, &v2_min, &v2_mic, NULL);
    /* now figure out if they match */
    - t = v2_maj - v1_maj;
    + t = v1_maj - v2_maj;
    if(t != 0)
    return t;
    - t = v2_min - v1_min;
    + t = v1_min - v2_min;
    if(t != 0)
    return t;
    - return v2_mic - v1_mic;
    + return v1_mic - v2_mic;
    }
    -
    --- a/gplugin/gplugin-version.h.in Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin-version.h.in Sat Sep 05 20:05:36 2020 -0500
    @@ -1,18 +1,18 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #if !defined(GPLUGIN_GLOBAL_HEADER_INSIDE) && !defined(GPLUGIN_COMPILATION)
    @@ -38,11 +38,10 @@
    G_BEGIN_DECLS
    -typedef gint (*GPluginVersionCompareFunc)(const gchar *v1, const gchar *v2, GError **error);
    +const gchar *gplugin_version_check(guint major, guint minor, guint micro);
    -gint gplugin_version_compare(const gchar *v1, const gchar *v2, GError **error);
    +gint gplugin_version_compare(const gchar *v1, const gchar *v2);
    G_END_DECLS
    #endif /* GPLUGIN_VERSION_H */
    -
    --- a/gplugin/gplugin.h.in Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/gplugin.h.in Sat Sep 05 20:05:36 2020 -0500
    @@ -1,18 +1,18 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #ifndef GPLUGIN_H
    #define GPLUGIN_H
    @@ -26,5 +26,3 @@
    #undef GPLUGIN_GLOBAL_HEADER_INSIDE
    #endif /* GPLUGIN_H */
    -
    -
    --- a/gplugin/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -69,7 +69,7 @@
    'gplugin-enums',
    sources : ENUM_HEADERS,
    install_header : true,
    - install_dir : join_paths(get_option('includedir'), 'gplugin-1.0', 'gplugin'))
    + install_dir : get_option('includedir') / 'gplugin-1.0' / 'gplugin')
    enums_c = enums[0]
    enums_h = enums[1]
    @@ -95,7 +95,7 @@
    output : 'gplugin-version.h',
    configuration : version_conf,
    install : true,
    - install_dir : join_paths(get_option('includedir'), 'gplugin-1.0', 'gplugin')
    + install_dir : get_option('includedir') / 'gplugin-1.0' / 'gplugin'
    )
    GPLUGIN_PUBLIC_BUILT_HEADERS += [
    @@ -126,7 +126,7 @@
    output : 'gplugin.h',
    configuration : conf,
    install : true,
    - install_dir : join_paths(get_option('includedir'), 'gplugin-1.0'))
    + install_dir : get_option('includedir') / 'gplugin-1.0')
    # Build gplugin-native.h
    GPLUGIN_NATIVE_H_INCLUDES = ''
    @@ -143,7 +143,7 @@
    output : 'gplugin-native.h',
    configuration : conf,
    install : true,
    - install_dir : join_paths(get_option('includedir'), 'gplugin-1.0'))
    + install_dir : get_option('includedir') / 'gplugin-1.0')
    ###############################################################################
    # Library target
    @@ -175,39 +175,18 @@
    )
    pkgconfig.generate(
    + gplugin,
    name : 'libgplugin',
    description : 'A fully featured GModule based plugin library',
    - version : meson.project_version(),
    filebase : 'gplugin',
    subdirs : 'gplugin-1.0',
    - libraries : gplugin,
    - requires : ['glib-2.0', 'gobject-2.0', 'gmodule-2.0'],
    + requires : [GLIB, GOBJECT, GMODULE],
    variables : [
    'plugindir=${libdir}',
    ],
    )
    ###############################################################################
    -# gplugin-query executable
    -###############################################################################
    -gplugin_query = executable('gplugin-query',
    - 'gplugin-query.c',
    - dependencies : [gplugin_dep, GLIB, GOBJECT],
    - install : true)
    -
    -if get_option('help2man')
    - custom_target('gplugin-query.1',
    - command : [help2man,
    - '--name=Query installed plugins', '--section=1',
    - '--help-option=--help-all', '--no-info',
    - '--output', '@OUTPUT@',
    - gplugin_query],
    - output : 'gplugin-query.1',
    - install : true,
    - install_dir : join_paths(get_option('mandir'), 'man1'))
    -endif
    -
    -###############################################################################
    # loader-tests static library
    ###############################################################################
    gplugin_loader_tests = static_library('gplugin-loader-tests',
    @@ -220,7 +199,7 @@
    ###############################################################################
    # GObject Introspection
    ###############################################################################
    -if get_option('gobject-introspection')
    +if get_option('introspection')
    gplugin_gir = gnome.generate_gir(gplugin,
    sources : GPLUGIN_SOURCES + GPLUGIN_HEADERS +
    GPLUGIN_PUBLIC_BUILT_SOURCES +
    @@ -229,7 +208,7 @@
    header : 'gplugin.h',
    namespace : 'GPlugin',
    symbol_prefix : 'gplugin',
    - nsversion : '@0@.0'.format(GPLUGIN_MAJOR_VERSION),
    + nsversion : '1.0',
    install : true,
    export_packages : ['gplugin'],
    extra_args : ['--quiet', '-DGPLUGIN_COMPILATION'])
    @@ -248,6 +227,7 @@
    ###############################################################################
    # subdirectories
    ###############################################################################
    +subdir('share')
    subdir('tests')
    if ENABLE_DOC
    --- a/gplugin/reference/embedding.xml Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/reference/embedding.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -13,8 +13,8 @@
    </para>
    <para>
    - Using GPlugin is pretty simple and I'd like to think straight forward
    - since that's the way I designed it.
    + GPlugin was designed to be simple to implement and use. Initialization
    + and teardown examples can be found below.
    </para>
    </simplesect>
    @@ -30,14 +30,13 @@
    gplugin_manager_add_default_paths();
    /* Optionally tell GPlugin to look for plugins in application specific
    - * paths.
    + * paths. This will add `$PREFIX/lib/application`.
    */
    gplugin_manager_add_app_paths(PREFIX, "application");
    /* Once you're ready to find/load plugins call g_plugin_manager_refresh.
    */
    gplugin_manager_refresh();
    -
    </programlisting></informalexample>
    </para>
    </simplesect>
    @@ -51,4 +50,4 @@
    </programlisting></informalexample>
    </para>
    </simplesect>
    -</chapter>
    \ No newline at end of file
    +</chapter>
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/reference/genie.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,70 @@
    +<?xml version='1.0' encoding="UTF-8"?>
    +<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
    + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
    +]>
    +<chapter id="chapter-genie">
    + <title>Genie Plugins</title>
    +
    + <warning>
    + <para>
    + You <emphasis role="strong">MUST</emphasis> have the Vala bindings
    + installed on your system for this to work. They are built by the
    + default GPlugin build.
    + </para>
    + </warning>
    +
    + <simplesect>
    + <title>Example Genie Plugin</title>
    +
    + <para>
    + Like all plugins in GPlugin, Genie plugins must also implement
    + the <code>gplugin_query</code>, <code>gplugin_load</code>, and
    + <code>gplugin_unload</code> functions.
    + </para>
    +
    + <para>
    + Due to the way <code>GPlugin.PluginInfo</code> info works, you must
    + subclass it and set your values in the new constructor.
    + </para>
    +
    + <para>
    + The following is a basic Genie plugin.
    + </para>
    +
    + <informalexample><programlisting>
    + uses GPlugin
    +
    + class BasicPluginInfo : GPlugin.PluginInfo
    + construct()
    + authors : array of string = {"author1"}
    +
    + Object(
    + id: "gplugin/genie-basic-plugin",
    + abi_version: 0x01020304,
    + name: "basic plugin",
    + authors: authors,
    + category: "test",
    + version: "version",
    + license_id: "license",
    + summary: "summary",
    + website: "website",
    + description: "description"
    + )
    +
    + def gplugin_query(out error : Error) : GPlugin.PluginInfo
    + error = null
    +
    + return new BasicPluginInfo()
    +
    + def gplugin_load(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    +
    + def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    + </programlisting></informalexample>
    + </simplesect>
    +</chapter>
    --- a/gplugin/reference/gplugin-docs.xml Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/reference/gplugin-docs.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -11,10 +11,13 @@
    <abstract>
    <title>GPlugin &version;</title>
    <para>
    - GPlugin is a GObject based library that implements a reusable plugin system that
    - supports loading plugins in other languages
    - via loaders. GPlugin also implements
    - dependencies among the plugins.
    + GPlugin is a GObject based library that implements a reusable plugin
    + system that supports loading plugins in other languages via loaders.
    + GPlugin also implements dependencies among the plugins.
    + </para>
    + <para>
    + The latest version of this documentation can be found on-line at
    + <ulink role="online-location" url="https://docs.pidgin.im/gplugin/latest">https://docs.pidgin.im/gplugin/latest/</ulink>.
    </para>
    </abstract>
    </bookinfo>
    @@ -23,8 +26,10 @@
    <title>Tutorials</title>
    <xi:include href="embedding.xml"/>
    + <xi:include href="genie.xml"/>
    + <xi:include href="lua.xml"/>
    <xi:include href="native-plugins.xml"/>
    - <xi:include href="lua.xml"/>
    + <xi:include href="perl5.xml"/>
    <xi:include href="python.xml"/>
    <xi:include href="vala.xml"/>
    </part>
    @@ -38,23 +43,13 @@
    <part id="API">
    <title>API Reference</title>
    - <chapter id="coreapi">
    - <title>Core API</title>
    - <xi:include href="xml/gplugin-core.xml"/>
    - <xi:include href="xml/gplugin-loader.xml"/>
    - <xi:include href="xml/gplugin-manager.xml"/>
    - <xi:include href="xml/gplugin-options.xml"/>
    - <xi:include href="xml/gplugin-plugin-info.xml"/>
    - <xi:include href="xml/gplugin-plugin.xml"/>
    - <xi:include href="xml/gplugin-version.xml"/>
    - </chapter>
    -
    - <chapter id="nativeapi">
    - <title>Native API</title>
    -
    - <xi:include href="xml/gplugin-native-plugin.xml"/>
    - <xi:include href="xml/gplugin-native-loader.xml"/>
    - </chapter>
    + <xi:include href="xml/gplugin-core.xml"/>
    + <xi:include href="xml/gplugin-loader.xml"/>
    + <xi:include href="xml/gplugin-manager.xml"/>
    + <xi:include href="xml/gplugin-options.xml"/>
    + <xi:include href="xml/gplugin-plugin-info.xml"/>
    + <xi:include href="xml/gplugin-plugin.xml"/>
    + <xi:include href="xml/gplugin-version.xml"/>
    </part>
    <index id="api-index-full">
    --- a/gplugin/reference/lua.xml Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/reference/lua.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -24,33 +24,34 @@
    <para>
    The following is a basic Lua plugin.
    - <informalexample><programlisting>
    - local lgi = require 'lgi'
    - local GPlugin = lgi.GPlugin
    + </para>
    +
    + <informalexample><programlisting>
    + local lgi = require "lgi"
    + local GPlugin = lgi.GPlugin
    - function gplugin_query()
    - return GPlugin.PluginInfo {
    - id = "gplugin-lua/basic-plugin",
    - abi_version = 0x01020304,
    - name = "basic plugin",
    - category = "test",
    - version = "0.0.10",
    - license_id = "license-id",
    - summary = "basic lua plugin",
    - description = "description of the basic lua plugin",
    - authors = { "Gary Kramlich &lt;grim@reaperworld.com&gt;" },
    - website = "https://bitbucket.org/gplugin/gplugin/"
    - }
    - end
    + function gplugin_query()
    + return GPlugin.PluginInfo {
    + id = "gplugin-lua/basic-plugin",
    + abi_version = 0x01020304,
    + name = "basic plugin",
    + category = "test",
    + version = "0.0.10",
    + license_id = "license-id",
    + summary = "basic lua plugin",
    + description = "description of the basic lua plugin",
    + authors = { "Gary Kramlich &lt;grim@reaperworld.com&gt;" },
    + website = "https://bitbucket.org/gplugin/gplugin/"
    + }
    + end
    - function gplugin_load(plugin)
    - return true
    - end
    + function gplugin_load(plugin)
    + return true
    + end
    - function gplugin_unload(plugin)
    - return true
    - end
    - </programlisting></informalexample>
    - </para>
    + function gplugin_unload(plugin)
    + return true
    + end
    + </programlisting></informalexample>
    </simplesect>
    </chapter>
    --- a/gplugin/reference/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/reference/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -7,6 +7,8 @@
    'gplugin-loader-tests.h',
    'gplugin-native-private.h',
    'gplugin-native.h',
    + 'gplugin-native-loader.h',
    + 'gplugin-native-plugin.h',
    'gplugin-private.h',
    'gplugin.h',
    ]
    @@ -26,13 +28,17 @@
    content_files = [
    'embedding.xml',
    + 'genie.xml',
    'lua.xml',
    'native-plugins.xml',
    + 'perl5.xml',
    'python.xml',
    + 'vala.xml',
    ]
    gnome.gtkdoc(DOC_MODULE,
    main_xml : DOC_MODULE + '-docs.xml',
    + namespace : 'gplugin',
    src_dir : gplugin_inc,
    dependencies : gplugin_dep,
    ignore_headers : ignore_hfiles,
    --- a/gplugin/reference/native-plugins.xml Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/reference/native-plugins.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -7,7 +7,7 @@
    <simplesect id="intro">
    <para>
    - Writing Native plugins is very simple, but since it's C/C++ it's a bit
    + Writing Native plugins is pretty simple, but since it's C/C++ it's a bit
    more complicated.
    </para>
    @@ -25,17 +25,17 @@
    /* gplugin_plugin_query is called by the native loader to determine if
    * the plugin is loadable. It must have this signature and should
    - * return a valid GPluginPluginInfo if everything is fine. If something
    - * went wrong, error should be set to a valid GError and NULL should be
    - * returned.
    + * return a valid GPluginPluginInfo instance if everything is fine. If
    + * something went wrong, error should be set to a valid GError and NULL
    + * should be returned.
    */
    G_MODULE_EXPORT GPluginPluginInfo *
    gplugin_plugin_query(GError **error) {
    - /* Authors is a list of authors of the plugin. Generally these are
    - * in the "Name Surname &lt;user@domain.com&gt;" format.
    + /* Authors is a list of authors who worked on the plugin. Generally
    + * these are in the "Name Surname &lt;user@domain.com&gt;" format.
    */
    const gchar * const authors[] = {
    - "author",
    + "Author O &lt;author@example.com&gt;",
    NULL
    };
    @@ -51,8 +51,7 @@
    "description", "description",
    "authors", authors,
    "website", "website",
    - NULL
    - );
    + NULL);
    }
    /* gplugin_plugin_load is called by the loader when the plugin should
    @@ -77,4 +76,4 @@
    </programlisting></informalexample>
    </para>
    </simplesect>
    -</chapter>
    \ No newline at end of file
    +</chapter>
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/reference/perl5.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,60 @@
    +<?xml version='1.0' encoding="UTF-8"?>
    +<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
    + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
    +]>
    +<chapter id="chapter-perl5">
    + <title>Perl5 Plugins</title>
    +
    + <warning>
    + <para>
    + You <emphasis role="strong">MUST</emphasis> have the Perl5 loader
    + plugin installed and working as well as the gobject-introspection
    + package for GPlugin installed to use Perl5 plugins.
    + </para>
    + </warning>
    +
    + <simplesect>
    + <title>Example Perl5 Plugin</title>
    +
    + <para>
    + Like all plugins in GPlugin, Perl5 plugins must also implement
    + the <code>gplugin_query</code>, <code>gplugin_load</code>, and
    + <code>gplugin_unload</code> functions.
    + </para>
    +
    + <para>
    + The following is a basic Perl5 plugin.
    + </para>
    +
    + <informalexample><programlisting>
    + use strict;
    +
    + use Glib::Object::Introspection;
    +
    + Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package=> "GPlugin");
    +
    + sub gplugin_query {
    + return GPlugin::PluginInfo->new(
    + id => "gplugin/perl5-basic-plugin",
    + abi_version => 0x01020304,
    + name => "basic plugin",
    + authors => ("author1"),
    + category => "test",
    + version => "version",
    + license_id => "license",
    + summary => "summary",
    + website => "website",
    + description => "description",
    + );
    + }
    +
    + sub gplugin_load {
    + return 0;
    + }
    +
    + sub gplugin_unload {
    + return 0;
    + }
    + </programlisting></informalexample>
    + </simplesect>
    +</chapter>
    --- a/gplugin/reference/python.xml Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/reference/python.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -24,33 +24,33 @@
    <para>
    The following is a basic Python plugin.
    - <informalexample><programlisting>
    - import gi
    + </para>
    - gi.require_version('GPlugin', '0.0')
    - from gi.repository import GPlugin
    + <informalexample><programlisting>
    + import gi
    +
    + gi.require_version("GPlugin", "0.0")
    + from gi.repository import GPlugin
    - def gplugin_plugin_query():
    - return GPlugin.PluginInfo(
    - id='gplugin-python/basic-plugin',
    - abi_version=0x01020304,
    - name='basic plugin',
    - authors=['author1'],
    - category='test',
    - version='version',
    - license_id='license',
    - summary='summary',
    - website='website',
    - description='description',
    - )
    + def gplugin_plugin_query():
    + return GPlugin.PluginInfo(
    + id="gplugin-python/basic-plugin",
    + abi_version=0x01020304,
    + name="basic plugin",
    + authors=["author1"],
    + category="test",
    + version="version",
    + license_id="license",
    + summary="summary",
    + website="website",
    + description="description",
    + )
    - def gplugin_plugin_load(plugin):
    - return True
    -
    + def gplugin_plugin_load(plugin):
    + return True
    - def gplugin_plugin_unload(plugin):
    - return True
    - </programlisting></informalexample>
    - </para>
    + def gplugin_plugin_unload(plugin):
    + return True
    + </programlisting></informalexample>
    </simplesect>
    </chapter>
    --- a/gplugin/reference/vala.xml Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/reference/vala.xml Sat Sep 05 20:05:36 2020 -0500
    @@ -8,8 +8,8 @@
    <warning>
    <para>
    You <emphasis role="strong">MUST</emphasis> have the Vala bindings
    - installed on your system for this to work. They are built by the
    - default GPlugin build.
    + installed on your system for this to work. They are built by
    + default.
    </para>
    </warning>
    @@ -29,46 +29,47 @@
    <para>
    The following is a basic Vala plugin.
    - <informalexample><programlisting>
    - using GPlugin;
    + </para>
    - public class BasicPluginInfo : GPlugin.PluginInfo {
    - public BasicPluginInfo() {
    - string[] authors = {"author1"};
    + <informalexample><programlisting>
    + using GPlugin;
    +
    + public class BasicPluginInfo : GPlugin.PluginInfo {
    + public BasicPluginInfo() {
    + string[] authors = {"author1"};
    - Object(
    - id: "gplugin/vala-basic-plugin",
    - abi_version: 0x01020304,
    - name: "basic plugin",
    - authors: authors,
    - category: "test",
    - version: "version",
    - license_id: "license",
    - summary: "summary",
    - website: "website",
    - description: "description"
    - );
    - }
    + Object(
    + id: "gplugin/vala-basic-plugin",
    + abi_version: 0x01020304,
    + name: "basic plugin",
    + authors: authors,
    + category: "test",
    + version: "version",
    + license_id: "license",
    + summary: "summary",
    + website: "website",
    + description: "description"
    + );
    }
    -
    - public GPlugin.PluginInfo gplugin_query(out Error error) {
    - error = null;
    + }
    - return new BasicPluginInfo();
    - }
    + public GPlugin.PluginInfo gplugin_query(out Error error) {
    + error = null;
    - public bool gplugin_load(GPlugin.Plugin plugin, out Error error) {
    - error = null;
    + return new BasicPluginInfo();
    + }
    - return true;
    - }
    + public bool gplugin_load(GPlugin.Plugin plugin, out Error error) {
    + error = null;
    +
    + return true;
    + }
    - public bool gplugin_unload(GPlugin.Plugin plugin, out Error error) {
    - error = null;
    + public bool gplugin_unload(GPlugin.Plugin plugin, out Error error) {
    + error = null;
    - return true;
    - }
    - </programlisting></informalexample>
    - </para>
    + return true;
    + }
    + </programlisting></informalexample>
    </simplesect>
    </chapter>
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/share/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,2 @@
    +subdir('valgrind')
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/share/valgrind/gplugin.supp Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,40 @@
    +# GPlugin Valgrind suppressions file
    +#
    +# This file provides a list of suppressions for all of GPlugin (including
    +# GPluginGTK), for all Valgrind tools (memcheck, drd, helgrind, etc.) for the
    +# false positives and deliberate one-time leaks which GPlugin causes to be
    +# reported when running under Valgrind. Some of these are actually from Glib,
    +# but we handle them here because it takes time for them to filter into
    +# distribution versions of glib.
    +#
    +# When running an application which links to GPlugin under Valgrind, you can
    +# pass this suppression file to Valgrind using
    +# --suppressions=/path/to/gplugin.supp.
    +#
    +# http://valgrind.org/docs/manual/manual-core.html#manual-core.suppress
    +#
    +# Note that there is currently no way for Valgrind to load this automatically
    +# (https://bugs.kde.org/show_bug.cgi?id=160905), so the best GPlugin can
    +# currently do is to install this file as part of its development package.
    +#
    +# This file should be updated if GPlugin introduces a new deliberate one-time
    +# leak, or another false positive in Valgrind: please file bugs at:
    +# https://issues.imfreedom.org/issues/GPLUGIN
    +
    +{
    + g-test-add
    + Memcheck:Leak
    + match-leak-kinds: reachable
    + ...
    + fun:g_test_add_vtable
    +}
    +
    +
    +{
    + gplugin-register-plugin-state
    + Memcheck:Leak
    + match-leak-kinds: possible
    + ...
    + fun:gplugin_plugin_state_get_type
    +}
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/share/valgrind/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,2 @@
    +install_data('gplugin.supp',
    + install_dir : join_paths(get_option('datadir'), 'gplugin', 'valgrind'))
    --- a/gplugin/tests/bad-plugins/query-error.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/bad-plugins/query-error.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,23 +19,25 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    g_set_error(error, GPLUGIN_DOMAIN, 0, "expected error");
    return NULL;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/bind-local/bind-local.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/bind-local/bind-local.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/bind-local",
    0x04030201,
    "bind-local", TRUE,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/dynamic-type/dynamic-test.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/dynamic-type/dynamic-test.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,4 +1,5 @@
    /*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    * Copyright (C) 2013 Ankit Vani <a@nevitus.org>
    *
    * This library is free software; you can redistribute it and/or
    @@ -18,15 +19,20 @@
    #ifndef DYNAMIC_TEST_H
    #define DYNAMIC_TEST_H
    -#define DYNAMIC_TYPE_TEST (dynamic_test_get_type())
    -#define DYNAMIC_TEST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), DYNAMIC_TYPE_TEST, DynamicTest))
    -#define DYNAMIC_TEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), DYNAMIC_TYPE_TEST, DynamicTestClass))
    -#define DYNAMIC_IS_TEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), DYNAMIC_TYPE_TEST))
    -#define DYNAMIC_IS_TEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), DYNAMIC_TYPE_TEST))
    -#define DYNAMIC_TEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), DYNAMIC_TYPE_TEST, DynamicTestClass))
    +#define DYNAMIC_TYPE_TEST (dynamic_test_get_type())
    +#define DYNAMIC_TEST(obj) \
    + (G_TYPE_CHECK_INSTANCE_CAST((obj), DYNAMIC_TYPE_TEST, DynamicTest))
    +#define DYNAMIC_TEST_CLASS(klass) \
    + (G_TYPE_CHECK_CLASS_CAST((klass), DYNAMIC_TYPE_TEST, DynamicTestClass))
    +#define DYNAMIC_IS_TEST(obj) \
    + (G_TYPE_CHECK_INSTANCE_TYPE((obj), DYNAMIC_TYPE_TEST))
    +#define DYNAMIC_IS_TEST_CLASS(klass) \
    + (G_TYPE_CHECK_CLASS_CAST((klass), DYNAMIC_TYPE_TEST))
    +#define DYNAMIC_TEST_GET_CLASS(obj) \
    + (G_TYPE_INSTANCE_GET_CLASS((obj), DYNAMIC_TYPE_TEST, DynamicTestClass))
    -typedef struct _DynamicTest DynamicTest;
    -typedef struct _DynamicTestClass DynamicTestClass;
    +typedef struct _DynamicTest DynamicTest;
    +typedef struct _DynamicTestClass DynamicTestClass;
    #include <glib.h>
    #include <glib-object.h>
    --- a/gplugin/tests/dynamic-type/dynamic-type-provider.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/dynamic-type/dynamic-type-provider.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,4 +1,5 @@
    /*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    * Copyright (C) 2013 Ankit Vani <a@nevitus.org>
    *
    * This library is free software; you can redistribute it and/or
    @@ -23,39 +24,41 @@
    G_DEFINE_DYNAMIC_TYPE(DynamicTest, dynamic_test, G_TYPE_OBJECT);
    static void
    -dynamic_test_init(G_GNUC_UNUSED DynamicTest *inst) {
    - g_message("instance created");
    +dynamic_test_init(G_GNUC_UNUSED DynamicTest *inst)
    +{
    }
    static void
    -dynamic_test_class_finalize(G_GNUC_UNUSED DynamicTestClass *klass) {
    +dynamic_test_class_finalize(G_GNUC_UNUSED DynamicTestClass *klass)
    +{
    }
    static void
    -dynamic_test_class_init(G_GNUC_UNUSED DynamicTestClass *klass) {
    - g_message("class created");
    +dynamic_test_class_init(G_GNUC_UNUSED DynamicTestClass *klass)
    +{
    }
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    return gplugin_plugin_info_new(
    "gplugin/dynamic-type-provider",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - NULL
    - );
    + NULL);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(GPluginNativePlugin *plugin, G_GNUC_UNUSED GError **error) {
    +gplugin_load(GPluginNativePlugin *plugin, G_GNUC_UNUSED GError **error)
    +{
    dynamic_test_register_type(G_TYPE_MODULE(plugin));
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/dynamic-type/dynamic-type-user.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/dynamic-type/dynamic-type-user.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,4 +1,5 @@
    /*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    * Copyright (C) 2013 Ankit Vani <a@nevitus.org>
    *
    * This library is free software; you can redistribute it and/or
    @@ -23,27 +24,30 @@
    static DynamicTest *test_object = NULL;
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const dependencies[] = {
    - "gplugin/dynamic-type-provider",
    - NULL
    - };
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + const gchar *const dependencies[] = {"gplugin/dynamic-type-provider", NULL};
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/dynamic-type-user",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "dependencies", dependencies,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error) {
    +gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +{
    test_object = g_object_new(DYNAMIC_TYPE_TEST, NULL);
    - if (test_object == NULL) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0,
    - "could not create an instance of DynamicTest");
    + if(test_object == NULL) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "could not create an instance of DynamicTest");
    return FALSE;
    }
    @@ -58,12 +62,10 @@
    g_object_unref(test_object);
    - if (DYNAMIC_IS_TEST(weak_test_object)) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0,
    - "test_object is still valid");
    + if(DYNAMIC_IS_TEST(weak_test_object)) {
    + g_set_error(error, GPLUGIN_DOMAIN, 0, "test_object is still valid");
    return FALSE;
    }
    return TRUE;
    }
    -
    --- a/gplugin/tests/id-collision/id-collision1.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/id-collision/id-collision1.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/id-collision",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "name", "first",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/id-collision/id-collision2.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/id-collision/id-collision2.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/id-collision",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "name", "second",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/load-on-query-fail/load-on-query-fail.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/load-on-query-fail/load-on-query-fail.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,17 +19,20 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/load-on-query-fail",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "load-on-query", TRUE,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error) {
    +gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +{
    static int count = 1;
    g_set_error(error, GPLUGIN_DOMAIN, 0, "called %d times", count++);
    @@ -38,9 +41,9 @@
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/load-on-query-pass/load-on-query-pass.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/load-on-query-pass/load-on-query-pass.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/load-on-query",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "load-on-query", TRUE,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -1,15 +1,16 @@
    ###############################################################################
    # Subdirectories
    ###############################################################################
    -subdir('plugins')
    subdir('bad-plugins')
    +subdir('bind-local')
    subdir('dynamic-type')
    subdir('id-collision')
    +subdir('load-on-query-fail')
    subdir('load-on-query-pass')
    -subdir('load-on-query-fail')
    +subdir('newest-version')
    +subdir('plugins')
    +subdir('unresolved-symbol')
    subdir('versioned-dependencies')
    -subdir('bind-local')
    -subdir('unresolved-symbol')
    ###############################################################################
    # Tests
    @@ -28,6 +29,20 @@
    dependencies : [gplugin_dep, GLIB, GOBJECT])
    test('Core', e)
    +e = executable('test-find-plugins', 'test-find-plugins.c',
    + c_args : ['-DTEST_DIR="@0@/plugins/"'.format(meson.current_build_dir())],
    + dependencies : [gplugin_dep, GLIB, GOBJECT])
    +test('Find Plugins', e)
    +
    +e = executable('test-loader-registration', 'test-loader-registration.c',
    + dependencies : [gplugin_dep, GLIB, GOBJECT])
    +test('Loader Registration', e)
    +
    +e = executable('test-newest-version', 'test-newest-version.c',
    + c_args : ['-DTEST_DIR="@0@/newest-version/"'.format(meson.current_build_dir())],
    + dependencies : [gplugin_dep, GLIB, GOBJECT])
    +test('Find Plugins Newest Version', e)
    +
    e = executable('test-option-group', 'test-option-group.c',
    dependencies : [gplugin_dep, GLIB, GOBJECT])
    test('Option Group', e)
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,35 @@
    +shared_module('multiple-semantic-1.0.0', 'multiple-semantic-1.0.0.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    +
    +shared_module('multiple-semantic-1.1.0', 'multiple-semantic-1.1.0.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    +
    +shared_module('no-version-and-semantic-no-version',
    + 'no-version-and-semantic-no-version.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    +
    +shared_module('no-version-and-semantic-semantic',
    + 'no-version-and-semantic-semantic.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    +
    +shared_module('non-semantic-and-semantic-non-semantic',
    + 'non-semantic-and-semantic-non-semantic.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    +
    +shared_module('non-semantic-and-semantic-semantic',
    + 'non-semantic-and-semantic-semantic.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    +
    +shared_module('solo-no-version', 'solo-no-version.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    +
    +shared_module('solo-non-semantic', 'solo-non-semantic.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/multiple-semantic-1.0.0.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,47 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/test-newest-version/multiple-semantic",
    + 0x04030201,
    + "version", "1.0.0",
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/multiple-semantic-1.1.0.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,47 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/test-newest-version/multiple-semantic",
    + 0x04030201,
    + "version", "1.1.0",
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/no-version-and-semantic-no-version.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,46 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/test-newest-version/no-version-and-semantic",
    + 0x04030201,
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/no-version-and-semantic-semantic.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,47 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/test-newest-version/no-version-and-semantic",
    + 0x04030201,
    + "version", "1.0.0",
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,47 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/test-newest-version/non-semantic-and-semantic",
    + 0x04030201,
    + "version", "abc123",
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,47 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/test-newest-version/non-semantic-and-semantic",
    + 0x04030201,
    + "version", "1.0.0",
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/solo-no-version.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,46 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/test-newest-version/solo-no-version",
    + 0x04030201,
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/newest-version/solo-non-semantic.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,47 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/test-newest-version/solo-non-semantic",
    + 0x04030201,
    + "version", "abc123",
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    --- a/gplugin/tests/plugins/basic-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/plugins/basic-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,12 +19,11 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "author1",
    - NULL
    - };
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + const gchar *const authors[] = {"author1", NULL};
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/native-basic-plugin",
    0x01020304,
    @@ -36,21 +35,22 @@
    "description", "description",
    "authors", authors,
    "website", "website",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/plugins/broken-dependent-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/plugins/broken-dependent-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,31 +19,31 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const dependencies[] = {
    - "gplugin/does-not-exist",
    - NULL
    - };
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + const gchar *const dependencies[] = {"gplugin/does-not-exist", NULL};
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/broken-dependent-native-plugin",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "dependencies", dependencies,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/plugins/dependent-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/plugins/dependent-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,32 +19,31 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const dependencies[] = {
    - "dependency1",
    - "dependency2",
    - NULL
    - };
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + const gchar *const dependencies[] = {"dependency1", "dependency2", NULL};
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/native-dependent-plugin",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "dependencies", dependencies,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/plugins/load-exception.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/plugins/load-exception.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,25 +19,26 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    return gplugin_plugin_info_new(
    "gplugin/native-load-exception",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - NULL
    - );
    + NULL);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return FALSE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/plugins/load-failed.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/plugins/load-failed.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,25 +19,26 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    return gplugin_plugin_info_new(
    "gplugin/native-load-failed",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - NULL
    - );
    + NULL);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error) {
    +gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +{
    g_set_error(error, GPLUGIN_DOMAIN, 0, "expected error");
    return FALSE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/plugins/unload-failed.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/plugins/unload-failed.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,25 +19,26 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    return gplugin_plugin_info_new(
    "gplugin/native-unload-failed",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - NULL
    - );
    + NULL);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error) {
    +gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +{
    g_set_error(error, GPLUGIN_DOMAIN, 0, "expected error");
    return FALSE;
    }
    -
    --- a/gplugin/tests/test-bind-local.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-bind-local.c Sat Sep 05 20:05:36 2020 -0500
    @@ -17,21 +17,22 @@
    #include <stdlib.h>
    +#include <glib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    -#include <glib.h>
    -
    /******************************************************************************
    * Tests
    *****************************************************************************/
    /* This test kind of sucks because theres no way for us to lookup whether or
    - * not a module handle has had it's symbols bound locally. Therefore, right
    + * not a module handle has had its symbols bound locally. Therefore, right
    * now we have to settle to see if it was loaded correctly.
    */
    static void
    -test_bind_local(void) {
    +test_bind_local(void)
    +{
    GPluginPlugin *plugin = NULL;
    gplugin_manager_remove_paths();
    @@ -48,16 +49,15 @@
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    /* test the load on query flag */
    - g_test_add_func("/loaders/native/bind-local",
    - test_bind_local);
    + g_test_add_func("/loaders/native/bind-local", test_bind_local);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-core.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-core.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,35 +15,72 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <glib.h>
    +
    #include <gplugin.h>
    -#include <glib.h>
    +/******************************************************************************
    + * Helpers
    + *****************************************************************************/
    +static void
    +test_gplugin_count_ids_and_plugins(gint *n_ids, gint *n_plugins)
    +{
    + GList *l_ids = NULL, *l_id = NULL;
    + gint ids = 0, plugins = 0;
    +
    + l_ids = gplugin_manager_list_plugins();
    + for(l_id = l_ids; l_id; l_id = l_id->next) {
    + GSList *l_plugins = NULL, *l_plugin = NULL;
    + const gchar *id = (const gchar *)l_id->data;
    +
    + ids += 1;
    +
    + l_plugins = gplugin_manager_find_plugins(id);
    + for(l_plugin = l_plugins; l_plugin; l_plugin = l_plugin->next) {
    + plugins += 1;
    + }
    + g_slist_free_full(l_plugins, g_object_unref);
    + }
    + g_list_free(l_ids);
    +
    + if(n_ids) {
    + *n_ids = ids;
    + }
    +
    + if(n_plugins) {
    + *n_plugins = plugins;
    + }
    +}
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_gplugin_init_uninit(void) {
    +test_gplugin_init_uninit(void)
    +{
    gplugin_init();
    gplugin_uninit();
    }
    static void
    -test_gplugin_init_init_uninit(void) {
    +test_gplugin_init_init_uninit(void)
    +{
    gplugin_init();
    gplugin_init();
    gplugin_uninit();
    }
    static void
    -test_gplugin_init_uninit_with_refresh(void) {
    +test_gplugin_init_uninit_with_refresh(void)
    +{
    gplugin_init();
    gplugin_manager_refresh();
    gplugin_uninit();
    }
    static void
    -test_gplugin_init_uninit_with_refresh_plugins(void) {
    +test_gplugin_init_uninit_with_refresh_plugins(void)
    +{
    gplugin_init();
    gplugin_manager_append_path(TEST_DIR);
    gplugin_manager_refresh();
    @@ -51,9 +88,8 @@
    }
    static void
    -test_gplugin_init_uninit_with_double_refresh_plugins(void) {
    - GList *l = NULL;
    - GSList *sl = NULL;
    +test_gplugin_init_uninit_with_double_refresh_plugins(void)
    +{
    gint f_ids = 0, s_ids = 0, f_plugins = 0, s_plugins = 0;
    gplugin_init();
    @@ -62,25 +98,11 @@
    /* run the first refresh and count everything */
    gplugin_manager_refresh();
    - for(l = gplugin_manager_list_plugins(); l; l = l->next) {
    - const gchar *id = (const gchar *)l->data;
    -
    - f_ids += 1;
    -
    - for(sl = gplugin_manager_find_plugins(id); sl; sl = sl->next)
    - f_plugins += 1;
    - }
    + test_gplugin_count_ids_and_plugins(&f_ids, &f_plugins);
    /* now run the second refresh and make sure the counts match */
    gplugin_manager_refresh();
    - for(l = gplugin_manager_list_plugins(); l; l = l->next) {
    - const gchar *id = (const gchar *)l->data;
    -
    - s_ids += 1;
    -
    - for(sl = gplugin_manager_find_plugins(id); sl; sl = sl->next)
    - s_plugins += 1;
    - }
    + test_gplugin_count_ids_and_plugins(&s_ids, &s_plugins);
    g_assert_cmpint(f_ids, >, 0);
    g_assert_cmpint(f_plugins, >, 0);
    @@ -95,23 +117,21 @@
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    -
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    - gplugin_init();
    -
    - g_test_add_func("/core/init_uninit",
    - test_gplugin_init_uninit);
    - g_test_add_func("/core/init_init_uninit",
    - test_gplugin_init_init_uninit);
    - g_test_add_func("/core/init_uninit_with_refresh",
    - test_gplugin_init_uninit_with_refresh);
    - g_test_add_func("/core/init_uninit_with_refresh_plugins",
    - test_gplugin_init_uninit_with_refresh_plugins);
    - g_test_add_func("/core/init_uninit_with_double_refresh_plugins",
    - test_gplugin_init_uninit_with_double_refresh_plugins);
    + g_test_add_func("/core/init_uninit", test_gplugin_init_uninit);
    + g_test_add_func("/core/init_init_uninit", test_gplugin_init_init_uninit);
    + g_test_add_func(
    + "/core/init_uninit_with_refresh",
    + test_gplugin_init_uninit_with_refresh);
    + g_test_add_func(
    + "/core/init_uninit_with_refresh_plugins",
    + test_gplugin_init_uninit_with_refresh_plugins);
    + g_test_add_func(
    + "/core/init_uninit_with_double_refresh_plugins",
    + test_gplugin_init_uninit_with_double_refresh_plugins);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-dynamic-type.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-dynamic-type.c Sat Sep 05 20:05:36 2020 -0500
    @@ -17,18 +17,18 @@
    #include <stdlib.h>
    +#include <glib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    -
    -#include <glib.h>
    -
    #include <gplugin/gplugin-loader-tests.h>
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_dynamic_type(void) {
    +test_dynamic_type(void)
    +{
    GPluginPlugin *provider = NULL, *user = NULL;
    GPluginPluginState state;
    GError *error = NULL;
    @@ -72,16 +72,15 @@
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    /* test dynamic types */
    - g_test_add_func("/loaders/native/dynamic-type",
    - test_dynamic_type);
    + g_test_add_func("/loaders/native/dynamic-type", test_dynamic_type);
    return g_test_run();
    }
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/test-find-plugins.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,142 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <glib.h>
    +
    +#include <gplugin.h>
    +
    +/******************************************************************************
    + * Helpers
    + *****************************************************************************/
    +static void
    +test_gplugin_manager_foreach_load_plugins(
    + G_GNUC_UNUSED const gchar *id,
    + GSList *plugins,
    + gpointer data)
    +{
    + GSList *l = NULL;
    +
    + for(l = plugins; l != NULL; l = l->next) {
    + gplugin_manager_load_plugin(GPLUGIN_PLUGIN(l->data), NULL);
    + }
    +}
    +
    +static void
    +test_gplugin_manager_foreach_unload_plugins(
    + G_GNUC_UNUSED const gchar *id,
    + GSList *plugins,
    + gpointer data)
    +{
    + GSList *l = NULL;
    +
    + for(l = plugins; l != NULL; l = l->next) {
    + gplugin_manager_unload_plugin(GPLUGIN_PLUGIN(l->data), NULL);
    + }
    +}
    +
    +/******************************************************************************
    + * Tests
    + *****************************************************************************/
    +/*< private >
    + * test_gplugin_manager_find_plugins_with_state:
    + *
    + * Runs through the normal plugins in the plugins directory, trying to progress
    + * their state each iteration while checking that
    + * gplugin_manager_find_plugins_with_state() returns the correct values.
    + *
    + * If any changes are made to the plugins directory, this test will need to be
    + * updated.
    + */
    +static void
    +test_gplugin_manager_find_plugins_with_state(void)
    +{
    + GSList *plugins = NULL;
    +
    + /* this is the list of the current plugins and the furthest state they can
    + * make it in the plugin lifecycle:
    + *
    + * basic-plugin.c: unloaded
    + * broken-dependent-plugin.c: queried
    + * dependent-plugin.c: queried
    + * load-exception: load failed
    + * load-failed: load failed
    + * unload-failed: unload failed
    + */
    + gint QUERIED = 6;
    + gint LOADED = 2;
    + gint LOAD_FAILED = 2;
    + gint UNLOADED = 3; /* unloaded plugins go back to the queried state */
    + gint UNLOAD_FAILED = 1;
    +
    + gplugin_init();
    +
    + gplugin_manager_append_path(TEST_DIR);
    + gplugin_manager_refresh();
    +
    + /* make sure that all the plugins are queried */
    + plugins =
    + gplugin_manager_find_plugins_with_state(GPLUGIN_PLUGIN_STATE_QUERIED);
    + g_assert_cmpint(g_slist_length(plugins), ==, QUERIED);
    + g_slist_free_full(plugins, g_object_unref);
    +
    + /* now load all of the plugins */
    + gplugin_manager_foreach(test_gplugin_manager_foreach_load_plugins, NULL);
    +
    + /* make sure we have the proper number loaded */
    + plugins =
    + gplugin_manager_find_plugins_with_state(GPLUGIN_PLUGIN_STATE_LOADED);
    + g_assert_cmpint(g_slist_length(plugins), ==, LOADED);
    + g_slist_free_full(plugins, g_object_unref);
    +
    + /* make sure we have the proper number of load failed */
    + plugins = gplugin_manager_find_plugins_with_state(
    + GPLUGIN_PLUGIN_STATE_LOAD_FAILED);
    + g_assert_cmpint(g_slist_length(plugins), ==, LOAD_FAILED);
    + g_slist_free_full(plugins, g_object_unref);
    +
    + /* unload all of the plugins */
    + gplugin_manager_foreach(test_gplugin_manager_foreach_unload_plugins, NULL);
    +
    + /* make sure we have the proper number unloaded */
    + plugins =
    + gplugin_manager_find_plugins_with_state(GPLUGIN_PLUGIN_STATE_QUERIED);
    + g_assert_cmpint(g_slist_length(plugins), ==, UNLOADED);
    + g_slist_free_full(plugins, g_object_unref);
    +
    + /* make sure we have the proper number of unload failed */
    + plugins = gplugin_manager_find_plugins_with_state(
    + GPLUGIN_PLUGIN_STATE_UNLOAD_FAILED);
    + g_assert_cmpint(g_slist_length(plugins), ==, UNLOAD_FAILED);
    + g_slist_free_full(plugins, g_object_unref);
    +
    + gplugin_uninit();
    +}
    +
    +/******************************************************************************
    + * Main
    + *****************************************************************************/
    +gint
    +main(gint argc, gchar **argv)
    +{
    + g_test_init(&argc, &argv, NULL);
    +
    + g_test_add_func(
    + "/manager/find_plugins/with_state",
    + test_gplugin_manager_find_plugins_with_state);
    +
    + return g_test_run();
    +}
    --- a/gplugin/tests/test-id-collision.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-id-collision.c Sat Sep 05 20:05:36 2020 -0500
    @@ -17,18 +17,18 @@
    #include <stdlib.h>
    +#include <glib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    -
    -#include <glib.h>
    -
    #include <gplugin/gplugin-loader-tests.h>
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_id_collision(void) {
    +test_id_collision(void)
    +{
    GSList *plugins = NULL;
    gplugin_manager_append_path(TEST_ID_DIR);
    @@ -39,22 +39,21 @@
    g_assert_cmpuint(g_slist_length(plugins), ==, 2);
    - gplugin_manager_free_plugin_list(plugins);
    + g_slist_free_full(plugins, g_object_unref);
    }
    /******************************************************************************
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    - g_test_add_func("/loaders/native/id-collision",
    - test_id_collision);
    + g_test_add_func("/loaders/native/id-collision", test_id_collision);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-load-on-query.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-load-on-query.c Sat Sep 05 20:05:36 2020 -0500
    @@ -17,17 +17,18 @@
    #include <stdlib.h>
    +#include <glib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    -#include <glib.h>
    -
    /******************************************************************************
    * Tests
    *****************************************************************************/
    /* load on query */
    static void
    -test_load_on_query(void) {
    +test_load_on_query(void)
    +{
    GPluginPlugin *plugin = NULL;
    gplugin_manager_remove_paths();
    @@ -38,12 +39,15 @@
    g_assert_nonnull(plugin);
    g_assert_true(GPLUGIN_IS_PLUGIN(plugin));
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_LOADED);
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_LOADED);
    }
    static void
    -test_load_on_query_fail_subprocess(void) {
    +test_load_on_query_fail_subprocess(void)
    +{
    /* this test is very simple since we can't get the exact error condition
    * that we want.
    *
    @@ -60,8 +64,12 @@
    }
    static void
    -test_load_on_query_fail(void) {
    - g_test_trap_subprocess("/loaders/native/load-on-query/fail/subprocess", 0, 0);
    +test_load_on_query_fail(void)
    +{
    + g_test_trap_subprocess(
    + "/loaders/native/load-on-query/fail/subprocess",
    + 0,
    + 0);
    g_test_trap_assert_stderr("*failed to load*during query*");
    }
    @@ -70,20 +78,21 @@
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    /* test the load on query flag */
    - g_test_add_func("/loaders/native/load-on-query/pass",
    - test_load_on_query);
    - g_test_add_func("/loaders/native/load-on-query/fail",
    - test_load_on_query_fail);
    - g_test_add_func("/loaders/native/load-on-query/fail/subprocess",
    - test_load_on_query_fail_subprocess);
    + g_test_add_func("/loaders/native/load-on-query/pass", test_load_on_query);
    + g_test_add_func(
    + "/loaders/native/load-on-query/fail",
    + test_load_on_query_fail);
    + g_test_add_func(
    + "/loaders/native/load-on-query/fail/subprocess",
    + test_load_on_query_fail_subprocess);
    return g_test_run();
    }
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/test-loader-registration.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,188 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +
    +#include "../gplugin-private.h"
    +
    +#include <glib.h>
    +
    +/******************************************************************************
    + * GObject Implementation
    + *****************************************************************************/
    +#define TEST_GPLUGIN_TYPE_LOADER (test_gplugin_loader_get_type())
    +G_DECLARE_FINAL_TYPE(
    + TestGPluginLoader,
    + test_gplugin_loader,
    + TEST_GPLUGIN,
    + LOADER,
    + GPluginLoader)
    +
    +struct _TestGPluginLoader {
    + GPluginLoader parent;
    +};
    +
    +static GSList *
    +test_gplugin_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *loader)
    +{
    + return NULL;
    +}
    +
    +static GPluginPlugin *
    +test_gplugin_loader_query(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + G_GNUC_UNUSED const gchar *filename,
    + G_GNUC_UNUSED GError **error)
    +{
    + return NULL;
    +}
    +
    +static gboolean
    +test_gplugin_loader_load(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return FALSE;
    +}
    +
    +static gboolean
    +test_gplugin_loader_unload(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return FALSE;
    +}
    +
    +G_DEFINE_TYPE(TestGPluginLoader, test_gplugin_loader, GPLUGIN_TYPE_LOADER)
    +
    +static void
    +test_gplugin_loader_init(TestGPluginLoader *loader)
    +{
    +}
    +
    +static void
    +test_gplugin_loader_class_init(TestGPluginLoaderClass *klass)
    +{
    + GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
    +
    + loader_class->supported_extensions =
    + test_gplugin_loader_supported_extensions;
    + loader_class->query = test_gplugin_loader_query;
    + loader_class->load = test_gplugin_loader_load;
    + loader_class->unload = test_gplugin_loader_unload;
    +}
    +
    +/******************************************************************************
    + * Tests
    + *****************************************************************************/
    +static void
    +test_gplugin_manager_loader_register_unregister(void)
    +{
    + GError *error = NULL;
    + gboolean ret;
    +
    + gplugin_manager_private_uninit();
    + gplugin_manager_private_init();
    +
    + ret = gplugin_manager_register_loader(TEST_GPLUGIN_TYPE_LOADER, &error);
    +
    + g_assert_true(ret);
    + g_assert_no_error(error);
    +
    + ret = gplugin_manager_unregister_loader(TEST_GPLUGIN_TYPE_LOADER, &error);
    +
    + g_assert_true(ret);
    + g_assert_no_error(error);
    +}
    +
    +static void
    +test_gplugin_manager_loader_register_twice(void)
    +{
    + GError *error = NULL;
    + gboolean ret;
    +
    + gplugin_manager_private_uninit();
    + gplugin_manager_private_init();
    +
    + ret = gplugin_manager_register_loader(TEST_GPLUGIN_TYPE_LOADER, &error);
    + g_assert_true(ret);
    + g_assert_no_error(error);
    +
    + ret = gplugin_manager_register_loader(TEST_GPLUGIN_TYPE_LOADER, &error);
    + g_assert_false(ret);
    + g_assert_error(error, GPLUGIN_DOMAIN, 0);
    + g_clear_error(&error);
    +
    + ret = gplugin_manager_unregister_loader(TEST_GPLUGIN_TYPE_LOADER, &error);
    +
    + g_assert_true(ret);
    + g_assert_no_error(error);
    +}
    +
    +static void
    +test_gplugin_manager_loader_unregister_twice(void)
    +{
    + GError *error = NULL;
    + gboolean ret;
    +
    + gplugin_manager_private_uninit();
    + gplugin_manager_private_init();
    +
    + ret = gplugin_manager_register_loader(TEST_GPLUGIN_TYPE_LOADER, &error);
    + g_assert_true(ret);
    + g_assert_no_error(error);
    +
    + ret = gplugin_manager_unregister_loader(TEST_GPLUGIN_TYPE_LOADER, &error);
    + g_assert_true(ret);
    + g_assert_no_error(error);
    +
    + ret = gplugin_manager_unregister_loader(TEST_GPLUGIN_TYPE_LOADER, &error);
    + g_assert_false(ret);
    + g_assert_error(error, GPLUGIN_DOMAIN, 0);
    + g_clear_error(&error);
    +}
    +
    +/******************************************************************************
    + * Main
    + *****************************************************************************/
    +gint
    +main(gint argc, gchar **argv)
    +{
    + gint r = 0;
    +
    + g_test_init(&argc, &argv, NULL);
    +
    + gplugin_init();
    +
    + g_test_add_func(
    + "/manager/loader/register_unregister",
    + test_gplugin_manager_loader_register_unregister);
    + g_test_add_func(
    + "/manager/loader/register-twice",
    + test_gplugin_manager_loader_register_twice);
    + g_test_add_func(
    + "/manager/loader/unregister-twice",
    + test_gplugin_manager_loader_unregister_twice);
    +
    + r = g_test_run();
    +
    + gplugin_uninit();
    +
    + return r;
    +}
    --- a/gplugin/tests/test-native-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-native-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -17,18 +17,18 @@
    #include <stdlib.h>
    +#include <glib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    -
    -#include <glib.h>
    -
    #include <gplugin/gplugin-loader-tests.h>
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_broken_depend_plugin_load(void) {
    +test_broken_depend_plugin_load(void)
    +{
    GPluginPlugin *plugin = NULL;
    GPluginPluginState state;
    GError *error = NULL;
    @@ -49,13 +49,16 @@
    /* now attempt to load the dependent plugin, it's supposed to fail */
    g_assert_false(gplugin_manager_load_plugin(plugin, &error));
    + g_assert_error(error, GPLUGIN_DOMAIN, 0);
    + g_error_free(error);
    }
    /******************************************************************************
    * Test bad plugins
    *****************************************************************************/
    static void
    -test_query_error_subprocess(void) {
    +test_query_error_subprocess(void)
    +{
    GPluginPlugin *plugin = NULL;
    /* add the test directory to the plugin manager's search paths */
    @@ -70,7 +73,8 @@
    }
    static void
    -test_query_error(void) {
    +test_query_error(void)
    +{
    g_test_trap_subprocess("/loaders/native/error/query/subprocess", 0, 0);
    g_test_trap_assert_failed();
    @@ -80,7 +84,8 @@
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    @@ -88,15 +93,15 @@
    gplugin_loader_tests_main(NULL, TEST_DIR, "native");
    - g_test_add_func("/loaders/native/load/broken_dependent",
    - test_broken_depend_plugin_load);
    + g_test_add_func(
    + "/loaders/native/load/broken_dependent",
    + test_broken_depend_plugin_load);
    /* bad plugin tests */
    - g_test_add_func("/loaders/native/error/query",
    - test_query_error);
    - g_test_add_func("/loaders/native/error/query/subprocess",
    - test_query_error_subprocess);
    + g_test_add_func("/loaders/native/error/query", test_query_error);
    + g_test_add_func(
    + "/loaders/native/error/query/subprocess",
    + test_query_error_subprocess);
    return g_test_run();
    }
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/test-newest-version.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,117 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <glib.h>
    +
    +#include <gplugin.h>
    +
    +/******************************************************************************
    + * Helpers
    + *****************************************************************************/
    +static void
    +test_newest_version(const gchar *id, const gchar *version)
    +{
    + GPluginPlugin *plugin = NULL;
    + GPluginPluginInfo *info = NULL;
    +
    + gplugin_manager_append_path(TEST_DIR);
    + gplugin_manager_refresh();
    +
    + plugin = gplugin_manager_find_plugin_with_newest_version(id);
    + g_assert_nonnull(plugin);
    + g_assert_true(GPLUGIN_IS_PLUGIN(plugin));
    +
    + info = gplugin_plugin_get_info(plugin);
    + g_assert_nonnull(info);
    + g_assert_true(GPLUGIN_IS_PLUGIN_INFO(info));
    +
    + g_assert_cmpstr(gplugin_plugin_info_get_version(info), ==, version);
    +
    + g_clear_object(&info);
    + g_clear_object(&plugin);
    +}
    +
    +/******************************************************************************
    + * Tests
    + *****************************************************************************/
    +static void
    +test_newest_version_multiple_semantic(void)
    +{
    + test_newest_version(
    + "gplugin/test-newest-version/multiple-semantic",
    + "1.1.0");
    +}
    +
    +static void
    +test_newest_version_solo_non_semantic(void)
    +{
    + test_newest_version(
    + "gplugin/test-newest-version/solo-non-semantic",
    + "abc123");
    +}
    +
    +static void
    +test_newest_version_non_semantic_and_semantic(void)
    +{
    + test_newest_version(
    + "gplugin/test-newest-version/non-semantic-and-semantic",
    + "1.0.0");
    +}
    +
    +static void
    +test_newest_version_solo_no_version(void)
    +{
    + test_newest_version("gplugin/test-newest-version/solo-no-version", NULL);
    +}
    +
    +static void
    +test_newest_version_no_version_and_semantic(void)
    +{
    + test_newest_version(
    + "gplugin/test-newest-version/no-version-and-semantic",
    + "1.0.0");
    +}
    +
    +/******************************************************************************
    + * Main
    + *****************************************************************************/
    +gint
    +main(gint argc, gchar **argv)
    +{
    +
    + g_test_init(&argc, &argv, NULL);
    +
    + gplugin_init();
    +
    + g_test_add_func(
    + "/manager/find_plugin_newest_version/multiple-semantic",
    + test_newest_version_multiple_semantic);
    + g_test_add_func(
    + "/manager/find_plugin_newest_version/no-version-and-semantic",
    + test_newest_version_no_version_and_semantic);
    + g_test_add_func(
    + "/manager/find_plugin_newest_version/non-semantic-and-semantic",
    + test_newest_version_non_semantic_and_semantic);
    + g_test_add_func(
    + "/manager/find_plugin_newest_version/solo-no-version",
    + test_newest_version_solo_no_version);
    + g_test_add_func(
    + "/manager/find_plugin_newest_version/solo-non-semantic",
    + test_newest_version_solo_non_semantic);
    +
    + return g_test_run();
    +}
    --- a/gplugin/tests/test-option-group.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-option-group.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,15 +15,16 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#include <gplugin.h>
    +#include <glib.h>
    -#include <glib.h>
    +#include <gplugin.h>
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_gplugin_option_group_paths(const gchar *args, GList *expected) {
    +test_gplugin_option_group_paths(const gchar *args, GList *expected)
    +{
    GError *error = NULL;
    GOptionContext *ctx = NULL;
    GOptionGroup *group = NULL;
    @@ -63,21 +64,27 @@
    }
    static GList *
    -test_gplugin_manager_default_paths(void) {
    +test_gplugin_manager_default_paths(void)
    +{
    GList *paths = NULL;
    gchar *path = NULL;
    - path = g_build_filename(PREFIX, LIBDIR, "gplugin", NULL);
    + path = g_build_filename(PREFIX, LIBDIR, "gplugin", G_DIR_SEPARATOR_S, NULL);
    paths = g_list_prepend(paths, path);
    - path = g_build_filename(g_get_user_config_dir(), "gplugin", NULL);
    + path = g_build_filename(
    + g_get_user_config_dir(),
    + "gplugin",
    + G_DIR_SEPARATOR_S,
    + NULL);
    paths = g_list_prepend(paths, path);
    return paths;
    }
    static void
    -test_gplugin_option_group_default_paths(void) {
    +test_gplugin_option_group_default_paths(void)
    +{
    GList *expected = NULL;
    gchar *args = "test-option-group";
    @@ -87,76 +94,84 @@
    }
    static void
    -test_gplugin_option_group_default_paths_with_single(void) {
    +test_gplugin_option_group_default_paths_with_single(void)
    +{
    GList *expected = NULL;
    gchar *args = "test-option-group,-p,foo";
    expected = test_gplugin_manager_default_paths();
    - expected = g_list_prepend(expected, g_strdup("foo"));
    + expected = g_list_prepend(expected, g_strdup("foo/"));
    test_gplugin_option_group_paths(args, expected);
    }
    static void
    -test_gplugin_option_group_default_paths_with_multiple(void) {
    +test_gplugin_option_group_default_paths_with_multiple(void)
    +{
    GList *expected = NULL;
    gchar *args = "test-option-group,-p,foo,-p,bar,-p,baz";
    expected = test_gplugin_manager_default_paths();
    - expected = g_list_prepend(expected, g_strdup("foo"));
    - expected = g_list_prepend(expected, g_strdup("bar"));
    - expected = g_list_prepend(expected, g_strdup("baz"));
    + expected = g_list_prepend(expected, g_strdup("foo/"));
    + expected = g_list_prepend(expected, g_strdup("bar/"));
    + expected = g_list_prepend(expected, g_strdup("baz/"));
    test_gplugin_option_group_paths(args, expected);
    }
    static void
    -test_gplugin_option_group_no_default_paths_short(void) {
    +test_gplugin_option_group_no_default_paths_short(void)
    +{
    gchar *args = "test-option-group,-D";
    test_gplugin_option_group_paths(args, NULL);
    }
    static void
    -test_gplugin_option_group_no_default_paths_long(void) {
    +test_gplugin_option_group_no_default_paths_long(void)
    +{
    gchar *args = "test-option-group,--no-default-paths";
    test_gplugin_option_group_paths(args, NULL);
    }
    static void
    -test_gplugin_option_group_no_default_paths_with_single(void) {
    +test_gplugin_option_group_no_default_paths_with_single(void)
    +{
    GList *expected = NULL;
    gchar *args = "test-option-group,-D,-p,foo";
    - expected = g_list_prepend(expected, g_strdup("foo"));
    + expected = g_list_prepend(expected, g_strdup("foo/"));
    test_gplugin_option_group_paths(args, expected);
    }
    static void
    -test_gplugin_option_group_no_default_paths_with_multiple(void) {
    +test_gplugin_option_group_no_default_paths_with_multiple(void)
    +{
    GList *expected = NULL;
    gchar *args = "test-option-group,-D,-p,foo,-p,bar,-p,baz";
    - expected = g_list_prepend(expected, g_strdup("foo"));
    - expected = g_list_prepend(expected, g_strdup("bar"));
    - expected = g_list_prepend(expected, g_strdup("baz"));
    + expected = g_list_prepend(expected, g_strdup("foo/"));
    + expected = g_list_prepend(expected, g_strdup("bar/"));
    + expected = g_list_prepend(expected, g_strdup("baz/"));
    test_gplugin_option_group_paths(args, expected);
    }
    static void
    -test_gplugin_option_group_add_path_short(void) {
    - GList *expected = g_list_prepend(NULL, g_strdup("foo"));
    +test_gplugin_option_group_add_path_short(void)
    +{
    + GList *expected = g_list_prepend(NULL, g_strdup("foo/"));
    gchar *args = "test-option-group,-D,-p,foo";
    test_gplugin_option_group_paths(args, expected);
    }
    static void
    -test_gplugin_option_group_add_path_long(void) {
    - GList *expected = g_list_prepend(NULL, g_strdup("foo"));
    +test_gplugin_option_group_add_path_long(void)
    +{
    + GList *expected = g_list_prepend(NULL, g_strdup("foo/"));
    gchar *args = "test-option-group,-D,--path,foo";
    test_gplugin_option_group_paths(args, expected);
    @@ -166,30 +181,39 @@
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    - g_test_add_func("/option-group/default-paths",
    - test_gplugin_option_group_default_paths);
    - g_test_add_func("/option-group/default-paths/single",
    - test_gplugin_option_group_default_paths_with_single);
    - g_test_add_func("/option-group/default-paths/multiple",
    - test_gplugin_option_group_default_paths_with_multiple);
    + g_test_add_func(
    + "/option-group/default-paths",
    + test_gplugin_option_group_default_paths);
    + g_test_add_func(
    + "/option-group/default-paths/single",
    + test_gplugin_option_group_default_paths_with_single);
    + g_test_add_func(
    + "/option-group/default-paths/multiple",
    + test_gplugin_option_group_default_paths_with_multiple);
    - g_test_add_func("/option-group/no-default-paths/short",
    - test_gplugin_option_group_no_default_paths_short);
    - g_test_add_func("/option-group/no-default-paths/long",
    - test_gplugin_option_group_no_default_paths_long);
    - g_test_add_func("/option-group/no-default-paths/single",
    - test_gplugin_option_group_no_default_paths_with_single);
    - g_test_add_func("/option-group/no-default-paths/multiple",
    - test_gplugin_option_group_no_default_paths_with_multiple);
    + g_test_add_func(
    + "/option-group/no-default-paths/short",
    + test_gplugin_option_group_no_default_paths_short);
    + g_test_add_func(
    + "/option-group/no-default-paths/long",
    + test_gplugin_option_group_no_default_paths_long);
    + g_test_add_func(
    + "/option-group/no-default-paths/single",
    + test_gplugin_option_group_no_default_paths_with_single);
    + g_test_add_func(
    + "/option-group/no-default-paths/multiple",
    + test_gplugin_option_group_no_default_paths_with_multiple);
    - g_test_add_func("/option-group/add-path/short",
    - test_gplugin_option_group_add_path_short);
    - g_test_add_func("/option-group/add-path/long",
    - test_gplugin_option_group_add_path_long);
    + g_test_add_func(
    + "/option-group/add-path/short",
    + test_gplugin_option_group_add_path_short);
    + g_test_add_func(
    + "/option-group/add-path/long",
    + test_gplugin_option_group_add_path_long);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-plugin-info.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-plugin-info.c Sat Sep 05 20:05:36 2020 -0500
    @@ -15,37 +15,52 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <glib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    -#include <glib.h>
    -
    -#define test_string(var, value) G_STMT_START { \
    - g_assert_cmpstr((var), ==, (value)); \
    - g_assert_cmpstr((var), ==, gplugin_plugin_info_get_##var(info)); \
    - g_free((var)); \
    -} G_STMT_END
    +#define test_string(var, value) \
    + G_STMT_START \
    + { \
    + g_assert_cmpstr((var), ==, (value)); \
    + g_assert_cmpstr((var), ==, gplugin_plugin_info_get_##var(info)); \
    + g_free((var)); \
    + } \
    + G_STMT_END
    -#define test_uint(var, value) G_STMT_START { \
    - g_assert_cmpuint((var), ==, (value)); \
    - g_assert_cmpuint((var), ==, gplugin_plugin_info_get_##var(info)); \
    -} G_STMT_END
    +#define test_uint(var, value) \
    + G_STMT_START \
    + { \
    + g_assert_cmpuint((var), ==, (value)); \
    + g_assert_cmpuint((var), ==, gplugin_plugin_info_get_##var(info)); \
    + } \
    + G_STMT_END
    -#define test_true(var) G_STMT_START { \
    - g_assert_true((var)); \
    - g_assert_true(gplugin_plugin_info_get_##var(info)); \
    -} G_STMT_END
    +#define test_true(var) \
    + G_STMT_START \
    + { \
    + g_assert_true((var)); \
    + g_assert_true(gplugin_plugin_info_get_##var(info)); \
    + } \
    + G_STMT_END
    -#define test_false(var) G_STMT_START { \
    - g_assert_false((var)); \
    - g_assert_false(gplugin_plugin_info_get_##var(info)); \
    -} G_STMT_END
    +#define test_false(var) \
    + G_STMT_START \
    + { \
    + g_assert_false((var)); \
    + g_assert_false(gplugin_plugin_info_get_##var(info)); \
    + } \
    + G_STMT_END
    typedef gchar **(*TestStringVFunc)(GPluginPluginInfo *info);
    static void
    -test_stringv(gchar **got, const gchar * const * const expected,
    - TestStringVFunc func, GPluginPluginInfo *info)
    +test_stringv(
    + gchar **got,
    + const gchar *const *const expected,
    + TestStringVFunc func,
    + GPluginPluginInfo *info)
    {
    gint i = 0;
    gchar **tmp = NULL;
    @@ -67,26 +82,28 @@
    g_assert_cmpstr(tmp[i], ==, expected[i]);
    g_strfreev(got);
    - g_strfreev(tmp);
    }
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_gplugin_plugin_info_construction(void) {
    +test_gplugin_plugin_info_construction(void)
    +{
    GPluginPluginInfo *info = NULL;
    - gchar *id = NULL, *name = NULL, *version = NULL;
    + gchar *id = NULL, *name = NULL, *version = NULL, *icon_name = NULL;
    gchar *license_id = NULL, *license_text = NULL, *license_url = NULL;
    - gchar *icon = NULL, *summary = NULL, *description = NULL, *category = NULL;
    - gchar *help = NULL, *website = NULL;
    + gchar *summary = NULL, *description = NULL, *category = NULL;
    + gchar *website = NULL;
    gchar **authors = NULL, **dependencies = NULL;
    guint abi_version = 0;
    gboolean internal = FALSE, load_on_query = FALSE;
    - const gchar * const r_authors[] = { "author", NULL };
    - const gchar * const r_dependencies[] = { "dependency", NULL };
    + const gchar *const r_authors[] = {"author", NULL};
    + const gchar *const r_dependencies[] = {"dependency", NULL};
    - info = g_object_new(GPLUGIN_TYPE_PLUGIN_INFO,
    + /* clang-format off */
    + info = g_object_new(
    + GPLUGIN_TYPE_PLUGIN_INFO,
    "id", "gplugin-test/plugin-info-test",
    "abi_version", GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "internal", TRUE,
    @@ -96,20 +113,21 @@
    "license-id", "license-id",
    "license-text", "license-text",
    "license-url", "license-url",
    - "icon", "icon",
    + "icon-name", "icon-name",
    "summary", "summary",
    "description", "description",
    "category", "category",
    "authors", r_authors,
    - "help", "help",
    "website", "website",
    "dependencies", r_dependencies,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    g_assert_true(GPLUGIN_IS_PLUGIN_INFO(info));
    - g_object_get(G_OBJECT(info),
    + /* clang-format off */
    + g_object_get(
    + G_OBJECT(info),
    "id", &id,
    "abi_version", &abi_version,
    "internal", &internal,
    @@ -119,115 +137,15 @@
    "license-id", &license_id,
    "license-text", &license_text,
    "license-url", &license_url,
    - "icon", &icon,
    + "icon-name", &icon_name,
    "summary", &summary,
    "description", &description,
    "category", &category,
    "authors", &authors,
    - "help", &help,
    "website", &website,
    "dependencies", &dependencies,
    - NULL
    - );
    -
    - test_string(id, "gplugin-test/plugin-info-test");
    - test_uint(abi_version, GPLUGIN_NATIVE_PLUGIN_ABI_VERSION);
    - test_true(internal);
    - test_true(load_on_query);
    - test_string(name, "name");
    - test_string(version, "version");
    - test_string(license_id, "license-id");
    - test_string(license_text, "license-text");
    - test_string(license_url, "license-url");
    - test_string(icon, "icon");
    - test_string(summary, "summary");
    - test_string(description, "description");
    - test_string(category, "category");
    - test_stringv(authors, r_authors,
    - (TestStringVFunc)gplugin_plugin_info_get_authors, info);
    - test_string(help, "help");
    - test_string(website, "website");
    - test_stringv(dependencies, r_dependencies,
    - (TestStringVFunc)gplugin_plugin_info_get_dependencies, info);
    -}
    -
    -static void
    -test_gplugin_plugin_info_new_empty(void) {
    - GPluginPluginInfo *info = NULL;
    - gchar *id = NULL;
    - guint32 abi_version = 0;
    -
    - info = gplugin_plugin_info_new("empty", 1, NULL);
    -
    - g_assert_true(GPLUGIN_IS_PLUGIN_INFO(info));
    -
    - g_object_get(G_OBJECT(info),
    - "id", &id,
    - "abi-version", &abi_version,
    - NULL);
    -
    - test_string(id, "empty");
    - test_uint(abi_version, 1);
    -
    - g_object_unref(G_OBJECT(info));
    -}
    -
    -static void
    -test_gplugin_plugin_info_new_full(void) {
    - GPluginPluginInfo *info = NULL;
    - gchar *id = NULL, *name = NULL, *version = NULL;
    - gchar *license_id = NULL, *license_text = NULL, *license_url = NULL;
    - gchar *icon = NULL, *summary = NULL, *description = NULL, *category = NULL;
    - gchar *website = NULL, *help = NULL;
    - gchar **authors = NULL, **dependencies = NULL;
    - guint abi_version = 0;
    - gboolean internal = FALSE, load_on_query = FALSE;
    - const gchar * const r_authors[] = { "author", NULL };
    - const gchar * const r_dependencies[] = { "dependency", NULL };
    -
    - info = gplugin_plugin_info_new(
    - "gplugin-test/plugin-info-test",
    - GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - "internal", TRUE,
    - "load-on-query", TRUE,
    - "name", "name",
    - "version", "version",
    - "license-id", "license-id",
    - "license-text", "license-text",
    - "license-url", "license-url",
    - "icon", "icon",
    - "summary", "summary",
    - "description", "description",
    - "category", "category",
    - "authors", r_authors,
    - "help", "help",
    - "website", "website",
    - "dependencies", r_dependencies,
    - NULL
    - );
    -
    - g_assert_true(GPLUGIN_IS_PLUGIN_INFO(info));
    -
    - g_object_get(G_OBJECT(info),
    - "id", &id,
    - "abi_version", &abi_version,
    - "internal", &internal,
    - "load-on-query", &load_on_query,
    - "name", &name,
    - "version", &version,
    - "license-id", &license_id,
    - "license-text", &license_text,
    - "license-url", &license_url,
    - "icon", &icon,
    - "summary", &summary,
    - "description", &description,
    - "category", &category,
    - "authors", &authors,
    - "help", &help,
    - "website", &website,
    - "dependencies", &dependencies,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    test_string(id, "gplugin-test/plugin-info-test");
    test_uint(abi_version, GPLUGIN_NATIVE_PLUGIN_ABI_VERSION);
    @@ -238,30 +156,152 @@
    test_string(license_id, "license-id");
    test_string(license_text, "license-text");
    test_string(license_url, "license-url");
    - test_string(icon, "icon");
    + test_string(icon_name, "icon-name");
    test_string(summary, "summary");
    test_string(description, "description");
    test_string(category, "category");
    - test_stringv(authors, r_authors,
    - (TestStringVFunc)gplugin_plugin_info_get_authors, info);
    - test_string(help, "help");
    + test_stringv(
    + authors,
    + r_authors,
    + (TestStringVFunc)gplugin_plugin_info_get_authors,
    + info);
    test_string(website, "website");
    - test_stringv(dependencies, r_dependencies,
    - (TestStringVFunc)gplugin_plugin_info_get_dependencies, info);
    + test_stringv(
    + dependencies,
    + r_dependencies,
    + (TestStringVFunc)gplugin_plugin_info_get_dependencies,
    + info);
    +
    + g_object_unref(G_OBJECT(info));
    +}
    +
    +static void
    +test_gplugin_plugin_info_new_empty(void)
    +{
    + GPluginPluginInfo *info = NULL;
    + gchar *id = NULL;
    + guint32 abi_version = 0;
    +
    + info = gplugin_plugin_info_new("empty", 1, NULL);
    +
    + g_assert_true(GPLUGIN_IS_PLUGIN_INFO(info));
    +
    + /* clang-format off */
    + g_object_get(
    + G_OBJECT(info),
    + "id", &id,
    + "abi-version", &abi_version,
    + NULL);
    + /* clang-format on */
    +
    + test_string(id, "empty");
    + test_uint(abi_version, 1);
    +
    + g_object_unref(G_OBJECT(info));
    }
    static void
    -test_gplugin_plugin_info_authors_single(void) {
    +test_gplugin_plugin_info_new_full(void)
    +{
    GPluginPluginInfo *info = NULL;
    - const gchar * const authors[] = { "author", NULL };
    - const gchar * const *g_authors = NULL;
    + gchar *id = NULL, *name = NULL, *version = NULL, *icon_name = NULL;
    + gchar *license_id = NULL, *license_text = NULL, *license_url = NULL;
    + gchar *summary = NULL, *description = NULL, *category = NULL;
    + gchar *website = NULL;
    + gchar **authors = NULL, **dependencies = NULL;
    + guint abi_version = 0;
    + gboolean internal = FALSE, load_on_query = FALSE;
    + const gchar *const r_authors[] = {"author", NULL};
    + const gchar *const r_dependencies[] = {"dependency", NULL};
    +
    + /* clang-format off */
    + info = gplugin_plugin_info_new(
    + "gplugin-test/plugin-info-test",
    + GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    + "internal", TRUE,
    + "load-on-query", TRUE,
    + "name", "name",
    + "version", "version",
    + "license-id", "license-id",
    + "license-text", "license-text",
    + "license-url", "license-url",
    + "icon-name", "icon-name",
    + "summary", "summary",
    + "description", "description",
    + "category", "category",
    + "authors", r_authors,
    + "website", "website",
    + "dependencies", r_dependencies,
    + NULL);
    + /* clang-format on */
    +
    + g_assert_true(GPLUGIN_IS_PLUGIN_INFO(info));
    +
    + /* clang-format off */
    + g_object_get(
    + G_OBJECT(info),
    + "id", &id,
    + "abi_version", &abi_version,
    + "internal", &internal,
    + "load-on-query", &load_on_query,
    + "name", &name,
    + "version", &version,
    + "license-id", &license_id,
    + "license-text", &license_text,
    + "license-url", &license_url,
    + "icon-name", &icon_name,
    + "summary", &summary,
    + "description", &description,
    + "category", &category,
    + "authors", &authors,
    + "website", &website,
    + "dependencies", &dependencies,
    + NULL);
    + /* clang-format on */
    +
    + test_string(id, "gplugin-test/plugin-info-test");
    + test_uint(abi_version, GPLUGIN_NATIVE_PLUGIN_ABI_VERSION);
    + test_true(internal);
    + test_true(load_on_query);
    + test_string(name, "name");
    + test_string(version, "version");
    + test_string(license_id, "license-id");
    + test_string(license_text, "license-text");
    + test_string(license_url, "license-url");
    + test_string(icon_name, "icon-name");
    + test_string(summary, "summary");
    + test_string(description, "description");
    + test_string(category, "category");
    + test_stringv(
    + authors,
    + r_authors,
    + (TestStringVFunc)gplugin_plugin_info_get_authors,
    + info);
    + test_string(website, "website");
    + test_stringv(
    + dependencies,
    + r_dependencies,
    + (TestStringVFunc)gplugin_plugin_info_get_dependencies,
    + info);
    +
    + g_object_unref(G_OBJECT(info));
    +}
    +
    +static void
    +test_gplugin_plugin_info_authors_single(void)
    +{
    + GPluginPluginInfo *info = NULL;
    + const gchar *const authors[] = {"author", NULL};
    + const gchar *const *g_authors = NULL;
    gint i;
    + /* clang-format off */
    info = gplugin_plugin_info_new(
    "test/single-author",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "authors", authors,
    NULL);
    + /* clang-format on */
    g_authors = gplugin_plugin_info_get_authors(info);
    @@ -272,17 +312,20 @@
    }
    static void
    -test_gplugin_plugin_info_authors_multiple(void) {
    +test_gplugin_plugin_info_authors_multiple(void)
    +{
    GPluginPluginInfo *info = NULL;
    - const gchar * const authors[] = { "author1", "author2", NULL };
    - const gchar * const *g_authors = NULL;
    + const gchar *const authors[] = {"author1", "author2", NULL};
    + const gchar *const *g_authors = NULL;
    gint i;
    + /* clang-format off */
    info = gplugin_plugin_info_new(
    "test/single-author",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "authors", authors,
    NULL);
    + /* clang-format on */
    g_authors = gplugin_plugin_info_get_authors(info);
    @@ -292,19 +335,21 @@
    g_object_unref(G_OBJECT(info));
    }
    -
    static void
    -test_gplugin_plugin_info_dependencies_single(void) {
    +test_gplugin_plugin_info_dependencies_single(void)
    +{
    GPluginPluginInfo *info = NULL;
    - gchar *dependencies[] = { "dependency1", NULL };
    - const gchar * const *g_dependencies = NULL;
    + gchar *dependencies[] = {"dependency1", NULL};
    + const gchar *const *g_dependencies = NULL;
    gint i;
    + /* clang-format off */
    info = gplugin_plugin_info_new(
    "test/single-dependency",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "dependencies", dependencies,
    NULL);
    + /* clang-format on */
    g_dependencies = gplugin_plugin_info_get_dependencies(info);
    @@ -315,18 +360,20 @@
    }
    static void
    -test_gplugin_plugin_info_dependencies_multiple(void) {
    +test_gplugin_plugin_info_dependencies_multiple(void)
    +{
    GPluginPluginInfo *info = NULL;
    - gchar *dependencies[] = { "dependencie1", "dependencie2", NULL };
    - const gchar * const *g_dependencies = NULL;
    + gchar *dependencies[] = {"dependencie1", "dependencie2", NULL};
    + const gchar *const *g_dependencies = NULL;
    gint i;
    -
    + /* clang-format off */
    info = gplugin_plugin_info_new(
    "test/single-dependencie",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "dependencies", dependencies,
    NULL);
    + /* clang-format on */
    g_dependencies = gplugin_plugin_info_get_dependencies(info);
    @@ -337,84 +384,37 @@
    }
    /******************************************************************************
    - * version_func tests
    - *****************************************************************************/
    -static void
    -test_gplugin_plugin_info_version_func(GPluginVersionCompareFunc func) {
    - GPluginPluginInfo *info = NULL;
    - GPluginVersionCompareFunc got = NULL;
    -
    - info = gplugin_plugin_info_new(
    - "test/version-func",
    - GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - "version-func", func,
    - NULL);
    -
    - got = gplugin_plugin_info_get_version_func(info);
    - g_assert_true(func == got);
    -}
    -
    -static gint
    -test_gplugin_version_compare(G_GNUC_UNUSED const gchar *v1,
    - G_GNUC_UNUSED const gchar *v2,
    - G_GNUC_UNUSED GError *error)
    -{
    - return 0;
    -}
    -
    -static void
    -test_gplugin_plugin_info_version_func_null(void) {
    - test_gplugin_plugin_info_version_func(NULL);
    -}
    -
    -static void
    -test_gplugin_plugin_info_version_func_default(void) {
    - test_gplugin_plugin_info_version_func(
    - (GPluginVersionCompareFunc)gplugin_version_compare
    - );
    -}
    -
    -static void
    -test_gplugin_plugin_info_version_func_custom(void) {
    - test_gplugin_plugin_info_version_func(
    - (GPluginVersionCompareFunc)test_gplugin_version_compare
    - );
    -}
    -
    -/******************************************************************************
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    - g_test_add_func("/plugin-info/construction",
    - test_gplugin_plugin_info_construction);
    + g_test_add_func(
    + "/plugin-info/construction",
    + test_gplugin_plugin_info_construction);
    - g_test_add_func("/plugin-info/new/empty",
    - test_gplugin_plugin_info_new_empty);
    - g_test_add_func("/plugin-info/new/full",
    - test_gplugin_plugin_info_new_full);
    + g_test_add_func(
    + "/plugin-info/new/empty",
    + test_gplugin_plugin_info_new_empty);
    + g_test_add_func("/plugin-info/new/full", test_gplugin_plugin_info_new_full);
    - g_test_add_func("/plugin-info/authors/single",
    - test_gplugin_plugin_info_authors_single);
    - g_test_add_func("/plugin-info/authors/multiple",
    - test_gplugin_plugin_info_authors_multiple);
    + g_test_add_func(
    + "/plugin-info/authors/single",
    + test_gplugin_plugin_info_authors_single);
    + g_test_add_func(
    + "/plugin-info/authors/multiple",
    + test_gplugin_plugin_info_authors_multiple);
    - g_test_add_func("/plugin-info/dependencies/single",
    - test_gplugin_plugin_info_dependencies_single);
    - g_test_add_func("/plugin-info/dependencies/multiple",
    - test_gplugin_plugin_info_dependencies_multiple);
    -
    - g_test_add_func("/plugin-info/version-func/null",
    - test_gplugin_plugin_info_version_func_null);
    - g_test_add_func("/plugin-info/version-func/default",
    - test_gplugin_plugin_info_version_func_default);
    - g_test_add_func("/plugin-info/version-func/custom",
    - test_gplugin_plugin_info_version_func_custom);
    + g_test_add_func(
    + "/plugin-info/dependencies/single",
    + test_gplugin_plugin_info_dependencies_single);
    + g_test_add_func(
    + "/plugin-info/dependencies/multiple",
    + test_gplugin_plugin_info_dependencies_multiple);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-plugin-manager-paths.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-plugin-manager-paths.c Sat Sep 05 20:05:36 2020 -0500
    @@ -15,20 +15,24 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#include <gplugin.h>
    -
    #include <glib.h>
    -#define test_path_count(e) G_STMT_START { \
    - GList *paths = gplugin_manager_get_paths(); \
    - g_assert_cmpint(g_list_length(paths), ==, (e)); \
    -} G_STMT_END
    +#include <gplugin.h>
    +
    +#define test_path_count(e) \
    + G_STMT_START \
    + { \
    + GList *paths = gplugin_manager_get_paths(); \
    + g_assert_cmpint(g_list_length(paths), ==, (e)); \
    + } \
    + G_STMT_END
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_gplugin_manager_paths_single(void) {
    +test_gplugin_manager_paths_single(void)
    +{
    gplugin_manager_append_path("foo");
    test_path_count(1);
    @@ -37,7 +41,8 @@
    }
    static void
    -test_gplugin_manager_paths_duplicate(void) {
    +test_gplugin_manager_paths_duplicate(void)
    +{
    gplugin_manager_append_path("foo");
    gplugin_manager_append_path("foo");
    @@ -48,7 +53,8 @@
    }
    static void
    -test_gplugin_manager_paths_multiple_fifo(void) {
    +test_gplugin_manager_paths_multiple_fifo(void)
    +{
    /* add */
    gplugin_manager_append_path("foo");
    test_path_count(1);
    @@ -65,7 +71,8 @@
    }
    static void
    -test_gplugin_manager_paths_multiple_filo(void) {
    +test_gplugin_manager_paths_multiple_filo(void)
    +{
    /* add */
    gplugin_manager_append_path("foo");
    test_path_count(1);
    @@ -82,7 +89,44 @@
    }
    static void
    -test_gplugin_manager_add_default_paths(void) {
    +test_gplugin_manager_paths_unicode(void)
    +{
    + test_path_count(0);
    +
    + gplugin_manager_append_path("/home/🐦/.plugins");
    + test_path_count(1);
    +
    + gplugin_manager_append_path("/home/user/.plugins");
    + test_path_count(2);
    +
    + gplugin_manager_remove_path("/home/🐦/.plugins");
    + test_path_count(1);
    +
    + gplugin_manager_remove_path("/home/user/.plugins");
    + test_path_count(0);
    +}
    +
    +static void
    +test_gplugin_manager_add_multiple_mixed_trailing_slashes(void)
    +{
    + test_path_count(0);
    +
    + gplugin_manager_append_path("/home/user1/.plugins");
    + test_path_count(1);
    +
    + gplugin_manager_append_path("/home/user2/.plugins/");
    + test_path_count(2);
    +
    + gplugin_manager_remove_path("/home/user1/.plugins/");
    + test_path_count(1);
    +
    + gplugin_manager_remove_path("/home/user2/.plugins");
    + test_path_count(0);
    +}
    +
    +static void
    +test_gplugin_manager_add_default_paths(void)
    +{
    GHashTable *req = NULL;
    GList *paths = NULL, *l = NULL;
    gchar *path = NULL;
    @@ -92,10 +136,14 @@
    req = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    /* create and add the paths we are expecting to the table */
    - path = g_build_filename(PREFIX, LIBDIR, "gplugin", NULL);
    + path = g_build_filename(PREFIX, LIBDIR, "gplugin", G_DIR_SEPARATOR_S, NULL);
    g_hash_table_insert(req, path, GINT_TO_POINTER(FALSE));
    - path = g_build_filename(g_get_user_config_dir(), "gplugin", NULL);
    + path = g_build_filename(
    + g_get_user_config_dir(),
    + "gplugin",
    + G_DIR_SEPARATOR_S,
    + NULL);
    g_hash_table_insert(req, path, GINT_TO_POINTER(FALSE));
    /* now tell the plugin manager to add the default paths */
    @@ -115,7 +163,8 @@
    }
    static void
    -test_gplugin_manager_add_app_paths(void) {
    +test_gplugin_manager_add_app_paths(void)
    +{
    GHashTable *req = NULL;
    GList *paths = NULL, *l = NULL;
    const gchar *prefix = "/usr/local/";
    @@ -126,10 +175,15 @@
    /* build our table of required paths */
    req = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    - path = g_build_filename(prefix, LIBDIR, appname, NULL);
    + path = g_build_filename(prefix, LIBDIR, appname, G_DIR_SEPARATOR_S, NULL);
    g_hash_table_insert(req, path, GINT_TO_POINTER(FALSE));
    - path = g_build_filename(g_get_user_config_dir(), appname, "plugins", NULL);
    + path = g_build_filename(
    + g_get_user_config_dir(),
    + appname,
    + "plugins",
    + G_DIR_SEPARATOR_S,
    + NULL);
    g_hash_table_insert(req, path, GINT_TO_POINTER(FALSE));
    /* now add the app paths */
    @@ -153,29 +207,43 @@
    }
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    - g_test_add_func("/plugins/paths/add_remove_single",
    - test_gplugin_manager_paths_single);
    + g_test_add_func(
    + "/plugins/paths/add_remove_single",
    + test_gplugin_manager_paths_single);
    - g_test_add_func("/plugins/paths/add_remove_duplicate",
    - test_gplugin_manager_paths_duplicate);
    + g_test_add_func(
    + "/plugins/paths/add_remove_duplicate",
    + test_gplugin_manager_paths_duplicate);
    +
    + g_test_add_func(
    + "/plugins/paths/add_remove_multiple_fifo",
    + test_gplugin_manager_paths_multiple_fifo);
    - g_test_add_func("/plugins/paths/add_remove_multiple_fifo",
    - test_gplugin_manager_paths_multiple_fifo);
    + g_test_add_func(
    + "/plugins/paths/add_remove_multiple_filo",
    + test_gplugin_manager_paths_multiple_filo);
    +
    + g_test_add_func(
    + "/plugins/paths/add_multiple_unicode",
    + test_gplugin_manager_paths_unicode);
    - g_test_add_func("/plugins/paths/add_remove_multiple_filo",
    - test_gplugin_manager_paths_multiple_filo);
    + g_test_add_func(
    + "/plugins/paths/add_multiple_mixed_trailing_slashes",
    + test_gplugin_manager_add_multiple_mixed_trailing_slashes);
    - g_test_add_func("/plugins/paths/add_default_paths",
    - test_gplugin_manager_add_default_paths);
    - g_test_add_func("/plugins/paths/add_app_paths",
    - test_gplugin_manager_add_app_paths);
    + g_test_add_func(
    + "/plugins/paths/add_default_paths",
    + test_gplugin_manager_add_default_paths);
    + g_test_add_func(
    + "/plugins/paths/add_app_paths",
    + test_gplugin_manager_add_app_paths);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-signals.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-signals.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,9 +15,9 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#include <gplugin.h>
    +#include <glib.h>
    -#include <glib.h>
    +#include <gplugin.h>
    typedef struct {
    gboolean loading;
    @@ -25,16 +25,18 @@
    gboolean unloading;
    gboolean unloaded;
    gboolean load_failed;
    + gboolean unload_failed;
    } TestGPluginManagerSignalsData;
    /******************************************************************************
    * Callbacks
    *****************************************************************************/
    static gboolean
    -test_gplugin_manager_signals_normal_loading(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED GError **error,
    - gpointer d)
    +test_gplugin_manager_signals_normal_loading(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED GError **error,
    + gpointer d)
    {
    TestGPluginManagerSignalsData *data = (TestGPluginManagerSignalsData *)d;
    @@ -44,9 +46,10 @@
    }
    static void
    -test_gplugin_manager_signals_normal_loaded(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - gpointer d)
    +test_gplugin_manager_signals_normal_loaded(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + gpointer d)
    {
    TestGPluginManagerSignalsData *data = (TestGPluginManagerSignalsData *)d;
    @@ -54,10 +57,11 @@
    }
    static gboolean
    -test_gplugin_manager_signals_normal_unloading(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED GError **error,
    - gpointer d)
    +test_gplugin_manager_signals_normal_unloading(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED GError **error,
    + gpointer d)
    {
    TestGPluginManagerSignalsData *data = (TestGPluginManagerSignalsData *)d;
    @@ -67,9 +71,10 @@
    }
    static void
    -test_gplugin_manager_signals_normal_unloaded(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - gpointer d)
    +test_gplugin_manager_signals_normal_unloaded(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + gpointer d)
    {
    TestGPluginManagerSignalsData *data = (TestGPluginManagerSignalsData *)d;
    @@ -77,10 +82,11 @@
    }
    static gboolean
    -test_gplugin_manager_signals_stop_loading(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - GError **error,
    - gpointer d)
    +test_gplugin_manager_signals_stop_loading(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + GError **error,
    + gpointer d)
    {
    TestGPluginManagerSignalsData *data = (TestGPluginManagerSignalsData *)d;
    @@ -92,10 +98,11 @@
    }
    static gboolean
    -test_gplugin_manager_signals_stop_unloading(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - GError **error,
    - gpointer d)
    +test_gplugin_manager_signals_stop_unloading(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + GError **error,
    + gpointer d)
    {
    TestGPluginManagerSignalsData *data = (TestGPluginManagerSignalsData *)d;
    @@ -107,42 +114,64 @@
    }
    static void
    -test_gplugin_manager_signals_load_failed(G_GNUC_UNUSED GObject *manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - gpointer d)
    +test_gplugin_manager_signals_load_failed(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + gpointer d)
    {
    TestGPluginManagerSignalsData *data = (TestGPluginManagerSignalsData *)d;
    data->load_failed = TRUE;
    }
    +static void
    +test_gplugin_manager_signals_unload_failed(
    + G_GNUC_UNUSED GObject *manager,
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + gpointer d)
    +{
    + TestGPluginManagerSignalsData *data = (TestGPluginManagerSignalsData *)d;
    +
    + data->unload_failed = TRUE;
    +}
    +
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_gplugin_manager_signals_normal(void) {
    +test_gplugin_manager_signals_normal(void)
    +{
    GPluginPlugin *plugin = NULL;
    GObject *manager = gplugin_manager_get_instance();
    GError *error = NULL;
    - TestGPluginManagerSignalsData data = { FALSE, FALSE, FALSE, FALSE, FALSE };
    - gulong signals[] = { 0, 0, 0, 0};
    + TestGPluginManagerSignalsData data = {
    + .loading = FALSE,
    + .loaded = FALSE,
    + .unloading = FALSE,
    + .unloaded = FALSE,
    + };
    + gulong signals[] = {0, 0, 0, 0};
    - signals[0] =
    - g_signal_connect(manager, "loading-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_loading),
    - &data);
    - signals[1] =
    - g_signal_connect(manager, "loaded-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_loaded),
    - &data);
    - signals[2] =
    - g_signal_connect(manager, "unloading-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_unloading),
    - &data);
    - signals[3] =
    - g_signal_connect(manager, "unloaded-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_unloaded),
    - &data);
    + signals[0] = g_signal_connect(
    + manager,
    + "loading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_loading),
    + &data);
    + signals[1] = g_signal_connect(
    + manager,
    + "loaded-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_loaded),
    + &data);
    + signals[2] = g_signal_connect(
    + manager,
    + "unloading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_unloading),
    + &data);
    + signals[3] = g_signal_connect(
    + manager,
    + "unloaded-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_unloaded),
    + &data);
    gplugin_manager_append_path(TEST_DIR);
    gplugin_manager_refresh();
    @@ -165,29 +194,39 @@
    }
    static void
    -test_gplugin_manager_signals_loading_stopped(void) {
    +test_gplugin_manager_signals_loading_stopped(void)
    +{
    GPluginPlugin *plugin = NULL;
    GObject *manager = gplugin_manager_get_instance();
    GError *error = NULL;
    - TestGPluginManagerSignalsData data = { FALSE, FALSE, FALSE, FALSE, FALSE };
    - gulong signals[] = { 0, 0, 0, 0};
    + TestGPluginManagerSignalsData data = {
    + .loading = FALSE,
    + .loaded = FALSE,
    + .unloading = FALSE,
    + .unloaded = FALSE,
    + };
    + gulong signals[] = {0, 0, 0, 0};
    - signals[0] =
    - g_signal_connect(manager, "loading-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_stop_loading),
    - &data);
    - signals[1] =
    - g_signal_connect(manager, "loaded-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_loaded),
    - &data);
    - signals[2] =
    - g_signal_connect(manager, "unloading-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_unloading),
    - &data);
    - signals[3] =
    - g_signal_connect(manager, "unloaded-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_unloaded),
    - &data);
    + signals[0] = g_signal_connect(
    + manager,
    + "loading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_stop_loading),
    + &data);
    + signals[1] = g_signal_connect(
    + manager,
    + "loaded-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_loaded),
    + &data);
    + signals[2] = g_signal_connect(
    + manager,
    + "unloading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_unloading),
    + &data);
    + signals[3] = g_signal_connect(
    + manager,
    + "unloaded-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_unloaded),
    + &data);
    gplugin_manager_append_path(TEST_DIR);
    gplugin_manager_refresh();
    @@ -207,29 +246,39 @@
    }
    static void
    -test_gplugin_manager_signals_unloading_stopped(void) {
    +test_gplugin_manager_signals_unloading_stopped(void)
    +{
    GPluginPlugin *plugin = NULL;
    GObject *manager = gplugin_manager_get_instance();
    GError *error = NULL;
    - TestGPluginManagerSignalsData data = { FALSE, FALSE, FALSE, FALSE, FALSE };
    - gulong signals[] = { 0, 0, 0, 0};
    + TestGPluginManagerSignalsData data = {
    + .loading = FALSE,
    + .loaded = FALSE,
    + .unloading = FALSE,
    + .unloaded = FALSE,
    + };
    + gulong signals[] = {0, 0, 0, 0};
    - signals[0] =
    - g_signal_connect(manager, "loading-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_loading),
    - &data);
    - signals[1] =
    - g_signal_connect(manager, "loaded-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_loaded),
    - &data);
    - signals[2] =
    - g_signal_connect(manager, "unloading-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_stop_unloading),
    - &data);
    - signals[3] =
    - g_signal_connect(manager, "unloaded-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_unloaded),
    - &data);
    + signals[0] = g_signal_connect(
    + manager,
    + "loading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_loading),
    + &data);
    + signals[1] = g_signal_connect(
    + manager,
    + "loaded-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_loaded),
    + &data);
    + signals[2] = g_signal_connect(
    + manager,
    + "unloading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_stop_unloading),
    + &data);
    + signals[3] = g_signal_connect(
    + manager,
    + "unloaded-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_unloaded),
    + &data);
    gplugin_manager_append_path(TEST_DIR);
    gplugin_manager_refresh();
    @@ -252,21 +301,27 @@
    }
    static void
    -test_gplugin_manager_signals_load_failure(void) {
    +test_gplugin_manager_signals_load_failure(void)
    +{
    GPluginPlugin *plugin = NULL;
    GObject *manager = gplugin_manager_get_instance();
    GError *error = NULL;
    - TestGPluginManagerSignalsData data = { FALSE, FALSE, FALSE, FALSE, FALSE };
    - gulong signals[] = { 0, 0, 0, 0, 0};
    + TestGPluginManagerSignalsData data = {
    + .loading = FALSE,
    + .load_failed = FALSE,
    + };
    + gulong signals[] = {0, 0, 0, 0};
    - signals[0] =
    - g_signal_connect(manager, "loading-plugin",
    - G_CALLBACK(test_gplugin_manager_signals_normal_loading),
    - &data);
    - signals[1] =
    - g_signal_connect(manager, "load-failed",
    - G_CALLBACK(test_gplugin_manager_signals_load_failed),
    - &data);
    + signals[0] = g_signal_connect(
    + manager,
    + "loading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_loading),
    + &data);
    + signals[1] = g_signal_connect(
    + manager,
    + "load-plugin-failed",
    + G_CALLBACK(test_gplugin_manager_signals_load_failed),
    + &data);
    gplugin_manager_append_path(TEST_DIR);
    gplugin_manager_refresh();
    @@ -281,25 +336,84 @@
    g_signal_handler_disconnect(manager, signals[1]);
    }
    +static void
    +test_gplugin_manager_signals_unload_failure(void)
    +{
    + GPluginPlugin *plugin = NULL;
    + GObject *manager = gplugin_manager_get_instance();
    + GError *error = NULL;
    + TestGPluginManagerSignalsData data = {
    + .loading = FALSE,
    + .loaded = FALSE,
    + .unloading = FALSE,
    + .unload_failed = FALSE,
    + };
    + gulong signals[] = {0, 0, 0, 0};
    +
    + signals[0] = g_signal_connect(
    + manager,
    + "loading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_loading),
    + &data);
    + signals[1] = g_signal_connect(
    + manager,
    + "loaded-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_loaded),
    + &data);
    + signals[2] = g_signal_connect(
    + manager,
    + "unloading-plugin",
    + G_CALLBACK(test_gplugin_manager_signals_normal_unloading),
    + &data);
    + signals[3] = g_signal_connect(
    + manager,
    + "unload-plugin-failed",
    + G_CALLBACK(test_gplugin_manager_signals_unload_failed),
    + &data);
    +
    + gplugin_manager_append_path(TEST_DIR);
    + gplugin_manager_refresh();
    +
    + plugin = gplugin_manager_find_plugin("gplugin/native-unload-failed");
    + gplugin_manager_load_plugin(plugin, &error);
    + g_assert_no_error(error);
    + g_assert_true(data.loading);
    + g_assert_true(data.loaded);
    +
    + gplugin_manager_unload_plugin(plugin, &error);
    + g_assert_error(error, GPLUGIN_DOMAIN, 0);
    + g_assert_true(data.unloading);
    + g_assert_true(data.unload_failed);
    +
    + g_signal_handler_disconnect(manager, signals[0]);
    + g_signal_handler_disconnect(manager, signals[1]);
    +}
    +
    /******************************************************************************
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    -
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    - g_test_add_func("/manager/signals/normal",
    - test_gplugin_manager_signals_normal);
    - g_test_add_func("/manager/signals/loading-stopped",
    - test_gplugin_manager_signals_loading_stopped);
    - g_test_add_func("/manager/signals/unloading-stopped",
    - test_gplugin_manager_signals_unloading_stopped);
    - g_test_add_func("/manager/signals/load-failed",
    - test_gplugin_manager_signals_load_failure);
    + g_test_add_func(
    + "/manager/signals/normal",
    + test_gplugin_manager_signals_normal);
    + g_test_add_func(
    + "/manager/signals/loading-stopped",
    + test_gplugin_manager_signals_loading_stopped);
    + g_test_add_func(
    + "/manager/signals/unloading-stopped",
    + test_gplugin_manager_signals_unloading_stopped);
    + g_test_add_func(
    + "/manager/signals/load-failed",
    + test_gplugin_manager_signals_load_failure);
    + g_test_add_func(
    + "/manager/signals/unload-failed",
    + test_gplugin_manager_signals_unload_failure);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-unresolved-symbol.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-unresolved-symbol.c Sat Sep 05 20:05:36 2020 -0500
    @@ -17,22 +17,22 @@
    #include <stdlib.h>
    +#include <glib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    -#include <glib.h>
    -
    /******************************************************************************
    * Tests
    *****************************************************************************/
    static void
    -test_unresolved_symbol(void) {
    +test_unresolved_symbol(void)
    +{
    g_test_expect_message(
    G_LOG_DOMAIN,
    G_LOG_LEVEL_WARNING,
    - "*some_unresolved_symbol*"
    - );
    + "*some_unresolved_symbol*");
    gplugin_manager_remove_paths();
    gplugin_manager_append_path(PLUGIN_DIR);
    @@ -45,16 +45,17 @@
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    /* test the load on query flag */
    - g_test_add_func("/loaders/native/unresolved-symbol",
    - test_unresolved_symbol);
    + g_test_add_func(
    + "/loaders/native/unresolved-symbol",
    + test_unresolved_symbol);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-version-compare.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-version-compare.c Sat Sep 05 20:05:36 2020 -0500
    @@ -22,227 +22,199 @@
    *****************************************************************************/
    /* bad versions */
    static void
    -test_gplugin_version_null__null_subprocess(void) {
    - gplugin_version_compare(NULL, NULL, NULL);
    -}
    -
    -static void
    -test_gplugin_version_null__null(void) {
    - g_test_trap_subprocess("/version-check/null__null/subprocess", 0, 0);
    -
    - g_test_trap_assert_failed();
    - g_test_trap_assert_stderr("*gplugin_version_compare*assertion*");
    +test_gplugin_version_null__null(void)
    +{
    + g_assert_cmpint(gplugin_version_compare(NULL, NULL), ==, 0);
    }
    static void
    -test_gplugin_version_null__1_2_3_subprocess(void) {
    - gplugin_version_compare(NULL, "1.2.3", NULL);
    -}
    -
    -static void
    -test_gplugin_version_null__1_2_3(void) {
    - g_test_trap_subprocess("/version-check/null__1_2_3/subprocess", 0, 0);
    -
    - g_test_trap_assert_failed();
    - g_test_trap_assert_stderr("*gplugin_version_compare*assertion*");
    -}
    -
    -static void
    -test_gplugin_version_1_2_3__null_subprocess(void) {
    - gplugin_version_compare("1.2.3", NULL, NULL);
    +test_gplugin_version_null__1_2_3(void)
    +{
    + g_assert_cmpint(gplugin_version_compare(NULL, "1.2.3"), <, 0);
    }
    static void
    -test_gplugin_version_1_2_3__null(void) {
    - g_test_trap_subprocess("/version-check/1_2_3__null/subprocess", 0, 0);
    -
    - g_test_trap_assert_failed();
    - g_test_trap_assert_stderr("*gplugin_version_compare*assertion*");
    -}
    -
    -static void
    -test_gplugin_version_abc__1_2_3_subprocess(void) {
    - GError *error = NULL;
    - gint t = 0;
    -
    - t = gplugin_version_compare("abc", "1.2.3", &error);
    -
    - g_assert_cmpint(t, ==, 1);
    - g_assert_no_error(error);
    +test_gplugin_version_1_2_3__null(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("1.2.3", NULL), >, 0);
    }
    static void
    -test_gplugin_version_abc__1_2_3(void) {
    - g_test_trap_subprocess("/version-check/abc__1_2_3/subprocess", 0, 0);
    -
    - g_test_trap_assert_failed();
    - g_test_trap_assert_stderr("*assertion*");
    +test_gplugin_version_abc__1_2_3(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("abc", "1.2.3"), <, 0);
    }
    static void
    -test_gplugin_version_1_2_3__abc_subprocess(void) {
    - GError *error = NULL;
    - gint t = 0;
    -
    - t = gplugin_version_compare("1.2.3", "abc", &error);
    -
    - g_assert_cmpint(t, ==, -1);
    - g_assert_no_error(error);
    -}
    -
    -static void
    -test_gplugin_version_1_2_3__abc(void) {
    - g_test_trap_subprocess("/version-check/1_2_3__abc/subprocess", 0, 0);
    -
    - g_test_trap_assert_failed();
    - g_test_trap_assert_stderr("*assertion*");
    +test_gplugin_version_1_2_3__abc(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("1.2.3", "abc"), >, 0);
    }
    /* major version tests */
    static void
    -test_gplugin_version_1_0_0__0_0_0(void) {
    - g_assert_cmpint(gplugin_version_compare("1.0.0", "0.0.0", NULL), ==, -1);
    +test_gplugin_version_1_0_0__0_0_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("1.0.0", "0.0.0"), >, 0);
    }
    static void
    -test_gplugin_version_1_0_0__1_0_0(void) {
    - g_assert_cmpint(gplugin_version_compare("1.0.0", "1.0.0", NULL), ==, 0);
    +test_gplugin_version_1_0_0__1_0_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("1.0.0", "1.0.0"), ==, 0);
    }
    static void
    -test_gplugin_version_0_0_0__1_0_0(void) {
    - g_assert_cmpint(gplugin_version_compare("0.0.0", "1.0.0", NULL), ==, 1);
    +test_gplugin_version_0_0_0__1_0_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0.0.0", "1.0.0"), <, 0);
    }
    /* minor version tests */
    static void
    -test_gplugin_version_0_1_0__0_0_0(void) {
    - g_assert_cmpint(gplugin_version_compare("0.1.0", "0.0.0", NULL), ==, -1);
    +test_gplugin_version_0_1_0__0_0_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0.1.0", "0.0.0"), >, 0);
    }
    static void
    -test_gplugin_version_0_1_0__0_1_0(void) {
    - g_assert_cmpint(gplugin_version_compare("0.1.0", "0.1.0", NULL), ==, 0);
    +test_gplugin_version_0_1_0__0_1_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0.1.0", "0.1.0"), ==, 0);
    }
    static void
    -test_gplugin_version_0_0_0__0_1_0(void) {
    - g_assert_cmpint(gplugin_version_compare("0.0.0", "0.1.0", NULL), ==, 1);
    +test_gplugin_version_0_0_0__0_1_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0.0.0", "0.1.0"), <, 0);
    }
    /* micro version tests */
    static void
    -test_gplugin_version_0_0_1__0_0_0(void) {
    - g_assert_cmpint(gplugin_version_compare("0.1.0", "0.0.0", NULL), ==, -1);
    +test_gplugin_version_0_0_1__0_0_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0.0.1", "0.0.0"), >, 0);
    }
    static void
    -test_gplugin_version_0_0_1__0_0_1(void) {
    - g_assert_cmpint(gplugin_version_compare("0.1.0", "0.1.0", NULL), ==, 0);
    +test_gplugin_version_0_0_1__0_0_1(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0.0.1", "0.0.1"), ==, 0);
    }
    static void
    -test_gplugin_version_0_0_0__0_0_1(void) {
    - g_assert_cmpint(gplugin_version_compare("0.0.0", "0.1.0", NULL), ==, 1);
    +test_gplugin_version_0_0_0__0_0_1(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0.0.0", "0.0.1"), <, 0);
    }
    /* major-minor tests */
    -static void test_gplugin_version_1_0__0_1(void) {
    - g_assert_cmpint(gplugin_version_compare("1.0", "0.1", NULL), ==, -1);
    +static void
    +test_gplugin_version_1_0__0_1(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("1.0", "0.1"), >, 0);
    }
    -static void test_gplugin_version_1_0__1_0(void) {
    - g_assert_cmpint(gplugin_version_compare("1.0", "1.0", NULL), ==, 0);
    +static void
    +test_gplugin_version_1_0__1_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("1.0", "1.0"), ==, 0);
    }
    -static void test_gplugin_version_0_1__1_0(void) {
    - g_assert_cmpint(gplugin_version_compare("0.1", "1.0", NULL), ==, 1);
    +static void
    +test_gplugin_version_0_1__1_0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0.1", "1.0"), <, 0);
    }
    /* major tests */
    -static void test_gplugin_version_1__0(void) {
    - g_assert_cmpint(gplugin_version_compare("1", "0", NULL), ==, -1);
    +static void
    +test_gplugin_version_1__0(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("1", "0"), >, 0);
    }
    -static void test_gplugin_version_1__1(void) {
    - g_assert_cmpint(gplugin_version_compare("1", "1", NULL), ==, 0);
    +static void
    +test_gplugin_version_1__1(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("1", "1"), ==, 0);
    }
    -static void test_gplugin_version_0__1(void) {
    - g_assert_cmpint(gplugin_version_compare("0", "1", NULL), ==, 1);
    +static void
    +test_gplugin_version_0__1(void)
    +{
    + g_assert_cmpint(gplugin_version_compare("0", "1"), <, 0);
    }
    /******************************************************************************
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_init();
    /* bad versions */
    - g_test_add_func("/version-check/null__null",
    - test_gplugin_version_null__null);
    - g_test_add_func("/version-check/null__1_2_3",
    - test_gplugin_version_null__1_2_3);
    - g_test_add_func("/version-check/1_2_3__null",
    - test_gplugin_version_1_2_3__null);
    - g_test_add_func("/version-check/abc__1_2_3",
    - test_gplugin_version_abc__1_2_3);
    - g_test_add_func("/version-check/1_2_3__abc",
    - test_gplugin_version_1_2_3__abc);
    - g_test_add_func("/version-check/null__null/subprocess",
    - test_gplugin_version_null__null_subprocess);
    - g_test_add_func("/version-check/null__1_2_3/subprocess",
    - test_gplugin_version_null__1_2_3_subprocess);
    - g_test_add_func("/version-check/1_2_3__null/subprocess",
    - test_gplugin_version_1_2_3__null_subprocess);
    - g_test_add_func("/version-check/abc__1_2_3/subprocess",
    - test_gplugin_version_abc__1_2_3_subprocess);
    - g_test_add_func("/version-check/1_2_3__abc/subprocess",
    - test_gplugin_version_1_2_3__abc_subprocess);
    + g_test_add_func(
    + "/version-compare/null__null",
    + test_gplugin_version_null__null);
    + g_test_add_func(
    + "/version-compare/null__1_2_3",
    + test_gplugin_version_null__1_2_3);
    + g_test_add_func(
    + "/version-compare/1_2_3__null",
    + test_gplugin_version_1_2_3__null);
    + g_test_add_func(
    + "/version-compare/abc__1_2_3",
    + test_gplugin_version_abc__1_2_3);
    + g_test_add_func(
    + "/version-compare/1_2_3__abc",
    + test_gplugin_version_1_2_3__abc);
    /* major version */
    - g_test_add_func("/version-check/1_0_0__0_0_0",
    - test_gplugin_version_1_0_0__0_0_0);
    - g_test_add_func("/version-check/1_0_0__1_0_0",
    - test_gplugin_version_1_0_0__1_0_0);
    - g_test_add_func("/version-check/0_0_0__1_0_0",
    - test_gplugin_version_0_0_0__1_0_0);
    + g_test_add_func(
    + "/version-compare/1_0_0__0_0_0",
    + test_gplugin_version_1_0_0__0_0_0);
    + g_test_add_func(
    + "/version-compare/1_0_0__1_0_0",
    + test_gplugin_version_1_0_0__1_0_0);
    + g_test_add_func(
    + "/version-compare/0_0_0__1_0_0",
    + test_gplugin_version_0_0_0__1_0_0);
    /* minor version */
    - g_test_add_func("/version-check/0_1_0__0_0_0",
    - test_gplugin_version_0_1_0__0_0_0);
    - g_test_add_func("/version-check/0_1_0__0_1_0",
    - test_gplugin_version_0_1_0__0_1_0);
    - g_test_add_func("/version-check/0_0_0__0_1_0",
    - test_gplugin_version_0_0_0__0_1_0);
    + g_test_add_func(
    + "/version-compare/0_1_0__0_0_0",
    + test_gplugin_version_0_1_0__0_0_0);
    + g_test_add_func(
    + "/version-compare/0_1_0__0_1_0",
    + test_gplugin_version_0_1_0__0_1_0);
    + g_test_add_func(
    + "/version-compare/0_0_0__0_1_0",
    + test_gplugin_version_0_0_0__0_1_0);
    /* micro version */
    - g_test_add_func("/version-check/0_0_1__0_0_0",
    - test_gplugin_version_0_0_1__0_0_0);
    - g_test_add_func("/version-check/0_0_1__0_0_1",
    - test_gplugin_version_0_0_1__0_0_1);
    - g_test_add_func("/version-check/0_0_0__0_0_1",
    - test_gplugin_version_0_0_0__0_0_1);
    + g_test_add_func(
    + "/version-compare/0_0_1__0_0_0",
    + test_gplugin_version_0_0_1__0_0_0);
    + g_test_add_func(
    + "/version-compare/0_0_1__0_0_1",
    + test_gplugin_version_0_0_1__0_0_1);
    + g_test_add_func(
    + "/version-compare/0_0_0__0_0_1",
    + test_gplugin_version_0_0_0__0_0_1);
    /* major-minor */
    - g_test_add_func("/version-check/1_0__0_1",
    - test_gplugin_version_1_0__0_1);
    - g_test_add_func("/version-check/1_0__1_0",
    - test_gplugin_version_1_0__1_0);
    - g_test_add_func("/version-check/0_1__1_0",
    - test_gplugin_version_0_1__1_0);
    + g_test_add_func("/version-compare/1_0__0_1", test_gplugin_version_1_0__0_1);
    + g_test_add_func("/version-compare/1_0__1_0", test_gplugin_version_1_0__1_0);
    + g_test_add_func("/version-compare/0_1__1_0", test_gplugin_version_0_1__1_0);
    /* major */
    - g_test_add_func("/version-check/1__0",
    - test_gplugin_version_1__0);
    - g_test_add_func("/version-check/1__1",
    - test_gplugin_version_1__1);
    - g_test_add_func("/version-check/0__1",
    - test_gplugin_version_0__1);
    + g_test_add_func("/version-compare/1__0", test_gplugin_version_1__0);
    + g_test_add_func("/version-compare/1__1", test_gplugin_version_1__1);
    + g_test_add_func("/version-compare/0__1", test_gplugin_version_0__1);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/test-versioned-dependencies.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/test-versioned-dependencies.c Sat Sep 05 20:05:36 2020 -0500
    @@ -15,40 +15,23 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    -#include <stdlib.h>
    +#include <glib.h>
    #include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -#include <glib.h>
    /******************************************************************************
    * Helpers
    *****************************************************************************/
    static void
    -_test_plugin_load_and_has_dependent(GPluginPlugin *dependent,
    - G_GNUC_UNUSED const gchar *id)
    +_test_plugin_loaded(GPluginPlugin *dependent, const gchar *id)
    {
    GPluginPlugin *plugin = NULL;
    - GSList *deps = NULL, *l = NULL;
    - gboolean found = FALSE;
    - plugin = gplugin_manager_find_plugin("gplugin/test-no-version");
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_LOADED);
    -
    - deps = gplugin_manager_get_plugin_dependencies(plugin, NULL);
    - if(deps != NULL) {
    - for(l = deps; l; l = l->next) {
    - if(l->data == dependent) {
    - found = TRUE;
    - }
    - }
    -
    - g_slist_free_full(deps, g_object_unref);
    -
    - g_assert_true(found);
    - }
    + plugin = gplugin_manager_find_plugin(id);
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_LOADED);
    g_object_unref(G_OBJECT(plugin));
    }
    @@ -57,11 +40,14 @@
    * Tests
    *****************************************************************************/
    static void
    -test_load_with_dependencies(void) {
    +test_load_with_dependencies(void)
    +{
    GPluginPlugin *plugin = NULL;
    GError *error = NULL;
    + gboolean ret = FALSE;
    - gplugin_manager_remove_paths();
    + gplugin_init();
    +
    gplugin_manager_append_path(TEST_VERSIONED_DEPENDENCY_DIR);
    gplugin_manager_refresh();
    @@ -69,40 +55,45 @@
    g_assert_nonnull(plugin);
    g_assert_true(GPLUGIN_IS_PLUGIN(plugin));
    - gplugin_manager_load_plugin(plugin, &error);
    + ret = gplugin_manager_load_plugin(plugin, &error);
    +
    + g_assert_true(ret);
    g_assert_no_error(error);
    - g_assert_cmpint(gplugin_plugin_get_state(plugin), ==,
    - GPLUGIN_PLUGIN_STATE_LOADED);
    - g_object_unref(G_OBJECT(plugin));
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_LOADED);
    /* now make sure each dependent plugin that's available was loaded */
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/test-no-version");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/test-exact1");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/test-exact2");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/test-greater");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/test-greater-equal");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/test-less");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/test-less-equal");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/bar");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/baz");
    - _test_plugin_load_and_has_dependent(plugin, "gplugin/fez");
    + _test_plugin_loaded(plugin, "gplugin/test-no-version");
    + _test_plugin_loaded(plugin, "gplugin/test-exact1");
    + _test_plugin_loaded(plugin, "gplugin/test-exact2");
    + _test_plugin_loaded(plugin, "gplugin/test-greater");
    + _test_plugin_loaded(plugin, "gplugin/test-greater-equal");
    + _test_plugin_loaded(plugin, "gplugin/test-less");
    + _test_plugin_loaded(plugin, "gplugin/test-less-equal");
    + _test_plugin_loaded(plugin, "gplugin/bar");
    + _test_plugin_loaded(plugin, "gplugin/baz");
    + _test_plugin_loaded(plugin, "gplugin/fez");
    +
    + g_object_unref(G_OBJECT(plugin));
    +
    + gplugin_uninit();
    }
    /******************************************************************************
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    -
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    - gplugin_init();
    -
    /* test the load on query flag */
    - g_test_add_func("/dependent-versions/super-dependent",
    - test_load_with_dependencies);
    + g_test_add_func(
    + "/dependent-versions/super-dependent",
    + test_load_with_dependencies);
    return g_test_run();
    }
    -
    --- a/gplugin/tests/unresolved-symbol/unresolved-symbol.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/unresolved-symbol/unresolved-symbol.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2016 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -21,23 +21,25 @@
    void some_unresolved_symbol(void);
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    some_unresolved_symbol();
    return NULL;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/bar.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/bar.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/bar",
    0x04030201,
    "version", "2.0",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/baz.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/baz.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/baz",
    0x04030201,
    "version", "2.0",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/exact1.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/exact1.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/test-exact1",
    0x04030201,
    "version", "1.0",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/exact2.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/exact2.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/test-exact2",
    0x04030201,
    "version", "1.0",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/fez.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/fez.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/fez",
    0x04030201,
    "version", "1.8",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/greater-equal.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/greater-equal.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/test-greater-equal",
    0x04030201,
    "version", "2.0",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/greater.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/greater.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/test-greater",
    0x04030201,
    "version", "2.0",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/less-equal.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/less-equal.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/test-less-equal",
    0x04030201,
    "version", "1.0",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/less.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/less.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,26 +19,29 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/test-less",
    0x04030201,
    "version", "0.9",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/no-version.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/no-version.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,25 +19,23 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - return gplugin_plugin_info_new(
    - "gplugin/test-no-version",
    - 0x04030201,
    - NULL
    - );
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + return gplugin_plugin_info_new("gplugin/test-no-version", 0x04030201, NULL);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/gplugin/tests/versioned-dependencies/super-dependent.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/gplugin/tests/versioned-dependencies/super-dependent.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -19,8 +19,11 @@
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const dependencies[] = {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format-11 formats this correctly, so this can be removed then. */
    + /* clang-format off */
    + const gchar *const dependencies[] = {
    "gplugin/test-no-version",
    "gplugin/test-exact1==1.0",
    "gplugin/test-exact2=1.0",
    @@ -31,29 +34,31 @@
    "gplugin/foo|gplugin/bar",
    "gplugin/foo|gplugin/baz>=2.0",
    "gplugin/foo|gplugin/fez<1.9",
    - NULL
    - };
    + NULL};
    + /* clang-format on */
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/super-dependent",
    0x04030201,
    "name", "super dependent",
    "dependencies", dependencies,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/lua/gplugin-lua-core.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/gplugin-lua-core.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -15,22 +15,25 @@
    * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <glib/gi18n-lib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    #include "gplugin-lua-loader.h"
    #include "gplugin-lua-plugin.h"
    -#include <glib/gi18n-lib.h>
    -
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format-11 formats this correctly, so this can be removed then. */
    + /* clang-format off */
    + const gchar *const authors[] = {
    + "Gary Kramlich <grim@reaperworld.com>",
    + NULL};
    + /* clang-format on */
    -G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Gary Kramlich <grim@reaperworld.com>",
    - NULL
    - };
    -
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/lua-loader",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    @@ -38,40 +41,34 @@
    "load-on-query", TRUE,
    "name", "Lua Plugin Loader",
    "version", GPLUGIN_VERSION,
    - "license-id", "GPL3",
    + "license-id", "LGPL-2.0-or-later",
    "summary", "A plugin that can load Lua plugins",
    "description", "This plugin allows the loading of plugins written in "
    "the Lua programming language.",
    "authors", authors,
    "website", GPLUGIN_WEBSITE,
    "category", "loaders",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(GPluginNativePlugin *plugin, GError **error)
    {
    gplugin_lua_loader_register(plugin);
    gplugin_lua_plugin_register(plugin);
    - gplugin_manager_register_loader(GPLUGIN_LUA_TYPE_LOADER);
    -
    - return TRUE;
    + return gplugin_manager_register_loader(GPLUGIN_LUA_TYPE_LOADER, error);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - GError **error)
    +gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    {
    g_set_error_literal(
    error,
    GPLUGIN_DOMAIN,
    0,
    - _("The Lua loader can not be unloaded")
    - );
    + _("The Lua loader can not be unloaded"));
    return FALSE;
    }
    -
    --- a/lua/gplugin-lua-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/gplugin-lua-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -17,25 +17,29 @@
    #include "gplugin-lua-loader.h"
    -#include "gplugin-lua-plugin.h"
    -
    -#include <glib/gi18n.h>
    +#include <glib/gi18n-lib.h>
    +#include <lauxlib.h>
    #include <lua.h>
    -#include <lauxlib.h>
    #include <lualib.h>
    +#include "gplugin-lua-plugin.h"
    +
    struct _GPluginLuaLoader {
    GPluginLoader parent;
    };
    -G_DEFINE_DYNAMIC_TYPE(GPluginLuaLoader, gplugin_lua_loader, GPLUGIN_TYPE_LOADER);
    +G_DEFINE_DYNAMIC_TYPE(
    + GPluginLuaLoader,
    + gplugin_lua_loader,
    + GPLUGIN_TYPE_LOADER);
    /******************************************************************************
    * Helpers
    *****************************************************************************/
    static void
    -_gplugin_lua_error_to_gerror(lua_State *L, GError **error) {
    +_gplugin_lua_error_to_gerror(lua_State *L, GError **error)
    +{
    const gchar *msg = NULL;
    if(error == NULL)
    @@ -47,10 +51,11 @@
    }
    static gboolean
    -_gplugin_lua_loader_load_unload_plugin(G_GNUC_UNUSED GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - const gchar *function,
    - GError **error)
    +_gplugin_lua_loader_load_unload_plugin(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + const gchar *function,
    + GError **error)
    {
    gboolean ret = TRUE;
    lua_State *L = gplugin_lua_plugin_get_state(GPLUGIN_LUA_PLUGIN(plugin));
    @@ -81,59 +86,28 @@
    * GPluginLoaderInterface API
    *****************************************************************************/
    static GSList *
    -gplugin_lua_loader_class_supported_extensions(G_GNUC_UNUSED GPluginLoaderClass *klass) {
    - GSList *exts = NULL;
    -
    - exts = g_slist_append(exts, "lua");
    - exts = g_slist_append(exts, "moon");
    -
    - return exts;
    +gplugin_lua_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *l)
    +{
    + return g_slist_append(NULL, "lua");
    }
    static GPluginPlugin *
    -gplugin_lua_loader_query(GPluginLoader *loader, const gchar *filename,
    - GError **error)
    +gplugin_lua_loader_query(
    + GPluginLoader *loader,
    + const gchar *filename,
    + GError **error)
    {
    GPluginPlugin *plugin = NULL;
    GPluginPluginInfo *info = NULL;
    lua_State *L = NULL;
    - gchar *ext = NULL;
    L = luaL_newstate();
    luaL_openlibs(L);
    - /* check the extension to see if we need to load moonscript */
    - ext = g_utf8_strrchr(filename, -1, g_utf8_get_char("."));
    - if(ext && g_utf8_collate(ext, ".moon") == 0) {
    - lua_getglobal(L, "require");
    - lua_pushstring(L, "moonscript");
    -
    - if(lua_pcall(L, 1, 1, 0) != 0) {
    - _gplugin_lua_error_to_gerror(L, error);
    -
    - return NULL;
    - }
    -
    - if(!lua_istable(L, -1)) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0, "moonscript returned an unexpected value");
    + if(luaL_loadfile(L, filename) != 0) {
    + _gplugin_lua_error_to_gerror(L, error);
    - return NULL;
    - }
    -
    - lua_getfield(L, -1, "loadfile");
    - lua_pushstring(L, filename);
    -
    - if(lua_pcall(L, 1, 1, 0) != 0) {
    - _gplugin_lua_error_to_gerror(L, error);
    -
    - return NULL;
    - }
    - } else {
    - if(luaL_loadfile(L, filename) != 0) {
    - _gplugin_lua_error_to_gerror(L, error);
    -
    - return NULL;
    - }
    + return NULL;
    }
    /* run the script */
    @@ -145,7 +119,11 @@
    lua_getglobal(L, "gplugin_query");
    if(lua_isnil(L, -1)) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0, "no gplugin_query function found");
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "no gplugin_query function found");
    return NULL;
    }
    @@ -165,50 +143,65 @@
    info = lua_touserdata(L, -1);
    lua_pop(L, 1);
    - plugin = g_object_new(GPLUGIN_LUA_TYPE_PLUGIN,
    - "filename", filename,
    - "loader", loader,
    - "lua-state", L,
    - "info", info,
    - NULL);
    + /* clang-format off */
    + plugin = g_object_new(
    + GPLUGIN_LUA_TYPE_PLUGIN,
    + "filename", filename,
    + "loader", loader,
    + "lua-state", L,
    + "info", info,
    + NULL);
    + /* clang-format on */
    return plugin;
    }
    static gboolean
    -gplugin_lua_loader_load(GPluginLoader *loader, GPluginPlugin *plugin,
    - GError **error)
    +gplugin_lua_loader_load(
    + GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    - return _gplugin_lua_loader_load_unload_plugin(loader, plugin,
    - "gplugin_load", error);
    + return _gplugin_lua_loader_load_unload_plugin(
    + loader,
    + plugin,
    + "gplugin_load",
    + error);
    }
    static gboolean
    -gplugin_lua_loader_unload(GPluginLoader *loader, GPluginPlugin *plugin,
    - GError **error)
    +gplugin_lua_loader_unload(
    + GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    - return _gplugin_lua_loader_load_unload_plugin(loader, plugin,
    - "gplugin_unload",
    - error);
    + return _gplugin_lua_loader_load_unload_plugin(
    + loader,
    + plugin,
    + "gplugin_unload",
    + error);
    }
    /******************************************************************************
    * GObject Stuff
    *****************************************************************************/
    static void
    -gplugin_lua_loader_init(G_GNUC_UNUSED GPluginLuaLoader *loader) {
    +gplugin_lua_loader_init(G_GNUC_UNUSED GPluginLuaLoader *loader)
    +{
    }
    static void
    -gplugin_lua_loader_class_finalize(G_GNUC_UNUSED GPluginLuaLoaderClass *klass) {
    +gplugin_lua_loader_class_finalize(G_GNUC_UNUSED GPluginLuaLoaderClass *klass)
    +{
    }
    static void
    -gplugin_lua_loader_class_init(GPluginLuaLoaderClass *klass) {
    +gplugin_lua_loader_class_init(GPluginLuaLoaderClass *klass)
    +{
    GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
    loader_class->supported_extensions =
    - gplugin_lua_loader_class_supported_extensions;
    + gplugin_lua_loader_supported_extensions;
    loader_class->query = gplugin_lua_loader_query;
    loader_class->load = gplugin_lua_loader_load;
    loader_class->unload = gplugin_lua_loader_unload;
    @@ -218,6 +211,7 @@
    * API
    *****************************************************************************/
    void
    -gplugin_lua_loader_register(GPluginNativePlugin *plugin) {
    +gplugin_lua_loader_register(GPluginNativePlugin *plugin)
    +{
    gplugin_lua_loader_register_type(G_TYPE_MODULE(plugin));
    }
    --- a/lua/gplugin-lua-loader.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/gplugin-lua-loader.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -24,11 +24,15 @@
    G_BEGIN_DECLS
    #define GPLUGIN_LUA_TYPE_LOADER (gplugin_lua_loader_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginLuaLoader, gplugin_lua_loader, GPLUGIN_LUA, LOADER, GPluginLoader)
    +G_DECLARE_FINAL_TYPE(
    + GPluginLuaLoader,
    + gplugin_lua_loader,
    + GPLUGIN_LUA,
    + LOADER,
    + GPluginLoader)
    void gplugin_lua_loader_register(GPluginNativePlugin *plugin);
    G_END_DECLS
    #endif /* GPLUGIN_LUA_LOADER_H */
    -
    --- a/lua/gplugin-lua-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/gplugin-lua-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -31,6 +31,7 @@
    GPluginLoader *loader;
    GPluginPluginInfo *info;
    GPluginPluginState state;
    + GError *error;
    };
    /******************************************************************************
    @@ -44,9 +45,12 @@
    PROP_FILENAME = N_PROPERTIES,
    PROP_LOADER,
    PROP_INFO,
    - PROP_STATE
    + PROP_STATE,
    + PROP_ERROR,
    };
    -static GParamSpec *properties[N_PROPERTIES] = {NULL,};
    +static GParamSpec *properties[N_PROPERTIES] = {
    + NULL,
    +};
    /* I hate forward declarations... */
    static void gplugin_lua_plugin_iface_init(GPluginPluginInterface *iface);
    @@ -56,22 +60,25 @@
    gplugin_lua_plugin,
    G_TYPE_OBJECT,
    0,
    - G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_PLUGIN, gplugin_lua_plugin_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_PLUGIN, gplugin_lua_plugin_iface_init));
    /******************************************************************************
    * GPluginPlugin Implementation
    *****************************************************************************/
    static void
    -gplugin_lua_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface) {
    +gplugin_lua_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface)
    +{
    }
    /******************************************************************************
    * Object Stuff
    *****************************************************************************/
    static void
    -gplugin_lua_plugin_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *pspec)
    +gplugin_lua_plugin_get_property(
    + GObject *obj,
    + guint param_id,
    + GValue *value,
    + GParamSpec *pspec)
    {
    GPluginLuaPlugin *plugin = GPLUGIN_LUA_PLUGIN(obj);
    @@ -93,6 +100,9 @@
    case PROP_STATE:
    g_value_set_enum(value, plugin->state);
    break;
    + case PROP_ERROR:
    + g_value_set_boxed(value, plugin->error);
    + break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    @@ -101,8 +111,11 @@
    }
    static void
    -gplugin_lua_plugin_set_property(GObject *obj, guint param_id,
    - const GValue *value, GParamSpec *pspec)
    +gplugin_lua_plugin_set_property(
    + GObject *obj,
    + guint param_id,
    + const GValue *value,
    + GParamSpec *pspec)
    {
    GPluginLuaPlugin *plugin = GPLUGIN_LUA_PLUGIN(obj);
    @@ -124,6 +137,9 @@
    case PROP_STATE:
    plugin->state = g_value_get_enum(value);
    break;
    + case PROP_ERROR:
    + plugin->error = g_value_dup_boxed(value);
    + break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    @@ -132,27 +148,32 @@
    }
    static void
    -gplugin_lua_plugin_finalize(GObject *obj) {
    +gplugin_lua_plugin_finalize(GObject *obj)
    +{
    GPluginLuaPlugin *plugin = GPLUGIN_LUA_PLUGIN(obj);
    g_clear_pointer(&plugin->L, lua_close);
    g_clear_pointer(&plugin->filename, g_free);
    g_clear_object(&plugin->loader);
    g_clear_object(&plugin->info);
    + g_clear_error(&plugin->error);
    G_OBJECT_CLASS(gplugin_lua_plugin_parent_class)->finalize(obj);
    }
    static void
    -gplugin_lua_plugin_init(G_GNUC_UNUSED GPluginLuaPlugin *plugin) {
    +gplugin_lua_plugin_init(G_GNUC_UNUSED GPluginLuaPlugin *plugin)
    +{
    }
    static void
    -gplugin_lua_plugin_class_finalize(G_GNUC_UNUSED GPluginLuaPluginClass *klass) {
    +gplugin_lua_plugin_class_finalize(G_GNUC_UNUSED GPluginLuaPluginClass *klass)
    +{
    }
    static void
    -gplugin_lua_plugin_class_init(GPluginLuaPluginClass *klass) {
    +gplugin_lua_plugin_class_init(GPluginLuaPluginClass *klass)
    +{
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    obj_class->get_property = gplugin_lua_plugin_get_property;
    @@ -160,10 +181,10 @@
    obj_class->finalize = gplugin_lua_plugin_finalize;
    properties[PROP_LUA_STATE] = g_param_spec_pointer(
    - "lua-state", "lua-state",
    + "lua-state",
    + "lua-state",
    "The lua state for the plugin",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    @@ -172,18 +193,21 @@
    g_object_class_override_property(obj_class, PROP_LOADER, "loader");
    g_object_class_override_property(obj_class, PROP_INFO, "info");
    g_object_class_override_property(obj_class, PROP_STATE, "state");
    + g_object_class_override_property(obj_class, PROP_ERROR, "error");
    }
    /******************************************************************************
    * API
    *****************************************************************************/
    void
    -gplugin_lua_plugin_register(GPluginNativePlugin *native) {
    +gplugin_lua_plugin_register(GPluginNativePlugin *native)
    +{
    gplugin_lua_plugin_register_type(G_TYPE_MODULE(native));
    }
    lua_State *
    -gplugin_lua_plugin_get_state(GPluginLuaPlugin *plugin) {
    +gplugin_lua_plugin_get_state(GPluginLuaPlugin *plugin)
    +{
    g_return_val_if_fail(GPLUGIN_LUA_IS_PLUGIN(plugin), NULL);
    return plugin->L;
    --- a/lua/gplugin-lua-plugin.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/gplugin-lua-plugin.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -26,7 +26,12 @@
    G_BEGIN_DECLS
    #define GPLUGIN_LUA_TYPE_PLUGIN (gplugin_lua_plugin_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginLuaPlugin, gplugin_lua_plugin, GPLUGIN_LUA, PLUGIN, GObject)
    +G_DECLARE_FINAL_TYPE(
    + GPluginLuaPlugin,
    + gplugin_lua_plugin,
    + GPLUGIN_LUA,
    + PLUGIN,
    + GObject)
    void gplugin_lua_plugin_register(GPluginNativePlugin *native);
    @@ -35,4 +40,3 @@
    G_END_DECLS
    #endif /* GPLUGIN_LUA_PLUGIN_H */
    -
    --- a/lua/gplugin-lua-test-lgi.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/gplugin-lua-test-lgi.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -18,30 +18,36 @@
    #include <stdio.h>
    #include <string.h>
    +#include <lauxlib.h>
    #include <lua.h>
    -#include <lauxlib.h>
    #include <lualib.h>
    static void
    -_add_require_path(lua_State *L, const char *path) {
    +_add_require_path(lua_State *L, const char *path)
    +{
    const char *pkg_path = NULL;
    char buff[255];
    lua_getglobal(L, "package");
    - lua_getfield(L, -1, "path"); // get field "path" from table at top of stack (-1)
    + lua_getfield(
    + L,
    + -1,
    + "path"); // get field "path" from table at top of stack (-1)
    pkg_path = lua_tostring(L, -1); // grab path string from top of stack
    memset(buff, 0, sizeof(buff));
    snprintf(buff, sizeof(buff), "%s;%s", pkg_path, path);
    - lua_pop(L, 1); // pop off the path field
    - lua_pushstring(L, buff); // push the new one
    - lua_setfield(L, -2, "path"); // set the field "path" in table at -2 with value at top of stack
    - lua_pop( L, 1 ); // get rid of package table from top of stack
    + lua_pop(L, 1); // pop off the path field
    + lua_pushstring(L, buff); // push the new one
    + lua_setfield(L, -2, "path"); // set the field "path" in table at -2 with
    + // value at top of stack
    + lua_pop(L, 1); // get rid of package table from top of stack
    }
    int
    -main(int argc, char *argv[]) {
    +main(int argc, char *argv[])
    +{
    lua_State *L = NULL;
    int ret = 0;
    @@ -63,4 +69,3 @@
    return ret;
    }
    -
    --- a/lua/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    if get_option('lua')
    - if not get_option('gobject-introspection')
    + if not get_option('introspection')
    error('Lua plugin requires GObject Introspection.')
    endif
    @@ -14,16 +14,30 @@
    'gplugin-lua-plugin.h',
    ]
    - _LUAS = [['lua', '>=5.1.0'],
    - ['lua5.1', '>=5.1.0'],
    - ['luajit', '>=2.0.0'],
    - ['lua5.2', '>=5.2.0'],
    - ['lua-5.2', '>=5.2.0']]
    + # These are ordered from most to least preferred, which would normally
    + # be from the highest to lowest version.
    + _LUAS = [
    + ['lua5.3', '>=5.3.0'],
    + ['lua-5.3', '>=5.3.0'],
    + ['lua5.2', '>=5.2.0'],
    + ['lua-5.2', '>=5.2.0'],
    + ['luajit', '>=2.0.0'],
    + ['lua5.1', '>=5.1.0'],
    + ['lua-5.1', '>=5.1.0'],
    + ['lua', '>=5.1.0'],
    + ]
    LUA_FOUND = false
    foreach _LUA : _LUAS
    - if not LUA_FOUND
    - LUA = dependency(_LUA[0], version : _LUA[1], required : false)
    - LUA_FOUND = LUA.found()
    + LUA = dependency(_LUA[0], version : _LUA[1], required : false)
    + if LUA.found()
    + # Compile and run our lua-lgi test program
    + lua_lgi_test = compiler.run(files('gplugin-lua-test-lgi.c'),
    + dependencies : LUA,
    + name : 'lua "lgi" module')
    + if lua_lgi_test.compiled() and lua_lgi_test.returncode() == 0
    + LUA_FOUND = true
    + break
    + endif
    endif
    endforeach
    @@ -31,14 +45,6 @@
    error('No usable Lua library was found')
    endif
    - # Compile and run our lua-lgi test program
    - lua_lgi_test = compiler.run(files('gplugin-lua-test-lgi.c'),
    - dependencies : LUA,
    - name : 'lua "lgi" module')
    - if not lua_lgi_test.compiled() or lua_lgi_test.returncode() != 0
    - error('Failed to find the "lgi" lua module')
    - endif
    -
    # now add the library
    shared_library('gplugin-lua',
    GPLUGIN_LUA_SOURCES,
    @@ -46,7 +52,7 @@
    name_prefix : '',
    dependencies : [LUA, gplugin_dep],
    install : true,
    - install_dir : join_paths(get_option('libdir'), 'gplugin')
    + install_dir : get_option('libdir') / 'gplugin'
    )
    endif # lua
    --- a/lua/tests/lua-plugins/basic.lua Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,44 +0,0 @@
    ---[[
    - Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    -
    - This library is free software; you can redistribute it and/or
    - modify it under the terms of the GNU Lesser General Public
    - License as published by the Free Software Foundation; either
    - version 2 of the License, or (at your option) any later version.
    -
    - This library is distributed in the hope that it will be useful,
    - but WITHOUT ANY WARRANTY; without even the implied warranty of
    - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - Lesser General Public License for more details.
    -
    - You should have received a copy of the GNU Lesser General Public
    - License along with this library; if not, see <http://www.gnu.org/licenses/>.
    ---]]
    -
    -local lgi = require 'lgi'
    -local GPlugin = lgi.GPlugin
    -
    -function gplugin_query()
    - return GPlugin.PluginInfo {
    - id = "gplugin/lua-basic-plugin",
    - abi_version = 0x01020304,
    - name = "basic plugin",
    - category = "test",
    - version = "version",
    - license_id = "license",
    - summary = "summary",
    - description = "description",
    - authors = { "author1" },
    - website = "website"
    - }
    -end
    -
    -function gplugin_load(plugin)
    - return true
    -end
    -
    -function gplugin_unload(plugin)
    - return true
    -end
    -
    -print(gplugin_query())
    --- a/lua/tests/lua-plugins/dependent.lua Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,35 +0,0 @@
    ---[[
    - Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    -
    - This library is free software; you can redistribute it and/or
    - modify it under the terms of the GNU Lesser General Public
    - License as published by the Free Software Foundation; either
    - version 2 of the License, or (at your option) any later version.
    -
    - This library is distributed in the hope that it will be useful,
    - but WITHOUT ANY WARRANTY; without even the implied warranty of
    - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - Lesser General Public License for more details.
    -
    - You should have received a copy of the GNU Lesser General Public
    - License along with this library; if not, see <http://www.gnu.org/licenses/>.
    ---]]
    -
    -local lgi = require 'lgi'
    -local GPlugin = lgi.GPlugin
    -
    -function gplugin_query()
    - return GPlugin.PluginInfo {
    - id="gplugin/lua-dependent-plugin",
    - dependencies={"dependency1", "dependency2"},
    - }
    -end
    -
    -function gplugin_load(plugin)
    - return false
    -end
    -
    -function gplugin_unload(plugin)
    - return false
    -end
    -
    --- a/lua/tests/lua-plugins/load-exception.lua Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    ---[[
    - Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    -
    - This library is free software; you can redistribute it and/or
    - modify it under the terms of the GNU Lesser General Public
    - License as published by the Free Software Foundation; either
    - version 2 of the License, or (at your option) any later version.
    -
    - This library is distributed in the hope that it will be useful,
    - but WITHOUT ANY WARRANTY; without even the implied warranty of
    - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - Lesser General Public License for more details.
    -
    - You should have received a copy of the GNU Lesser General Public
    - License along with this library; if not, see <http://www.gnu.org/licenses/>.
    ---]]
    -
    -local lgi = require 'lgi'
    -local GPlugin = lgi.GPlugin
    -
    -function gplugin_query()
    - return GPlugin.PluginInfo {
    - id="gplugin/lua-load-exception",
    - }
    -end
    -
    -function gplugin_load(plugin)
    - error('explosion!')
    -end
    -
    -function gplugin_unload(plugin)
    - return false
    -end
    -
    --- a/lua/tests/lua-plugins/load-failed.lua Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    ---[[
    - Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    -
    - This library is free software; you can redistribute it and/or
    - modify it under the terms of the GNU Lesser General Public
    - License as published by the Free Software Foundation; either
    - version 2 of the License, or (at your option) any later version.
    -
    - This library is distributed in the hope that it will be useful,
    - but WITHOUT ANY WARRANTY; without even the implied warranty of
    - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - Lesser General Public License for more details.
    -
    - You should have received a copy of the GNU Lesser General Public
    - License along with this library; if not, see <http://www.gnu.org/licenses/>.
    ---]]
    -
    -local lgi = require 'lgi'
    -local GPlugin = lgi.GPlugin
    -
    -function gplugin_query()
    - return GPlugin.PluginInfo {
    - id="gplugin/lua-load-failed",
    - }
    -end
    -
    -function gplugin_load(plugin)
    - return false
    -end
    -
    -function gplugin_unload(plugin)
    - return true
    -end
    -
    --- a/lua/tests/lua-plugins/unload-failed.lua Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    ---[[
    - Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    -
    - This library is free software; you can redistribute it and/or
    - modify it under the terms of the GNU Lesser General Public
    - License as published by the Free Software Foundation; either
    - version 2 of the License, or (at your option) any later version.
    -
    - This library is distributed in the hope that it will be useful,
    - but WITHOUT ANY WARRANTY; without even the implied warranty of
    - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - Lesser General Public License for more details.
    -
    - You should have received a copy of the GNU Lesser General Public
    - License along with this library; if not, see <http://www.gnu.org/licenses/>.
    ---]]
    -
    -local lgi = require 'lgi'
    -local GPlugin = lgi.GPlugin
    -
    -function gplugin_query()
    - return GPlugin.PluginInfo {
    - id="gplugin/lua-unload-failed",
    - }
    -end
    -
    -function gplugin_load(plugin)
    - return true
    -end
    -
    -function gplugin_unload(plugin)
    - return false
    -end
    -
    --- a/lua/tests/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/tests/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -2,28 +2,12 @@
    e = executable('test-lua-loader', 'test-lua-loader.c',
    c_args : [
    - '-DLUA_LOADER_DIR="@0@"'.format(join_paths(meson.current_build_dir(), '..')),
    - '-DLUA_PLUGIN_DIR="@0@/lua-plugins"'.format(
    - meson.current_source_dir()),
    - '-DMOONSCRIPT_PLUGIN_DIR="@0@/moonscript-plugins"'.format(
    + '-DLUA_LOADER_DIR="@0@/.."'.format(meson.current_build_dir()),
    + '-DLUA_PLUGIN_DIR="@0@/plugins"'.format(
    meson.current_source_dir()),
    ],
    link_with : gplugin_loader_tests,
    dependencies : [GLIB, GOBJECT, LUA, gplugin_dep])
    test('Lua Loader', e)
    -if get_option('moonscript-tests')
    - e = executable('test-lua-moon-loader', 'test-lua-moon-loader.c',
    - c_args : [
    - '-DLUA_LOADER_DIR="@0@"'.format(join_paths(meson.current_build_dir(), '..')),
    - '-DLUA_PLUGIN_DIR="@0@/lua-plugins"'.format(
    - meson.current_source_dir()),
    - '-DMOONSCRIPT_PLUGIN_DIR="@0@/moonscript-plugins"'.format(
    - meson.current_source_dir()),
    - ],
    - link_with : gplugin_loader_tests,
    - dependencies : [GLIB, GOBJECT, LUA, gplugin_dep])
    - test('Lua Moon Loader', e)
    -endif
    -
    endif # lua
    --- a/lua/tests/moonscript-plugins/basic.moon Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,39 +0,0 @@
    --- Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    ---
    --- This library is free software; you can redistribute it and/or
    --- modify it under the terms of the GNU Lesser General Public
    --- License as published by the Free Software Foundation; either
    --- version 2 of the License, or (at your option) any later version.
    ---
    --- This library is distributed in the hope that it will be useful,
    --- but WITHOUT ANY WARRANTY; without even the implied warranty of
    --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    --- Lesser General Public License for more details.
    ---
    --- You should have received a copy of the GNU Lesser General Public
    --- License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -
    -lgi = require 'lgi'
    -GPlugin = lgi.GPlugin
    -
    -export gplugin_query = ->
    - return GPlugin.PluginInfo {
    - id: "gplugin/moon-basic-plugin",
    - abi_version: 0x01020304,
    - name: "basic plugin",
    - category: "test",
    - version: "version",
    - license_id: "license",
    - summary: "summary",
    - description: "description",
    - authors: { "author1" },
    - website: "website"
    - }
    -
    -export gplugin_load = ->
    - return true
    -
    -
    -export gplugin_unload = ->
    - return true
    -
    --- a/lua/tests/moonscript-plugins/dependent.moon Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,30 +0,0 @@
    --- Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    ---
    --- This library is free software; you can redistribute it and/or
    --- modify it under the terms of the GNU Lesser General Public
    --- License as published by the Free Software Foundation; either
    --- version 2 of the License, or (at your option) any later version.
    ---
    --- This library is distributed in the hope that it will be useful,
    --- but WITHOUT ANY WARRANTY; without even the implied warranty of
    --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    --- Lesser General Public License for more details.
    ---
    --- You should have received a copy of the GNU Lesser General Public
    --- License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -
    -lgi = require 'lgi'
    -GPlugin = lgi.GPlugin
    -
    -export gplugin_query = ->
    - return GPlugin.PluginInfo {
    - id: "gplugin/moon-dependent-plugin",
    - dependencies: {"dependency1", "dependency2"}
    - }
    -
    -export gplugin_load = ->
    - return false
    -
    -
    -export gplugin_unload = ->
    - return false
    --- a/lua/tests/moonscript-plugins/load-exception.moon Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,30 +0,0 @@
    --- Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    ---
    --- This library is free software; you can redistribute it and/or
    --- modify it under the terms of the GNU Lesser General Public
    --- License as published by the Free Software Foundation; either
    --- version 2 of the License, or (at your option) any later version.
    ---
    --- This library is distributed in the hope that it will be useful,
    --- but WITHOUT ANY WARRANTY; without even the implied warranty of
    --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    --- Lesser General Public License for more details.
    ---
    --- You should have received a copy of the GNU Lesser General Public
    --- License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -
    -lgi = require 'lgi'
    -GPlugin = lgi.GPlugin
    -
    -export gplugin_query = ->
    - return GPlugin.PluginInfo {
    - id: "gplugin/moon-load-exception"
    - }
    -
    -export gplugin_load = ->
    - error("explode")
    - return false
    -
    -
    -export gplugin_unload = ->
    - return false
    --- a/lua/tests/moonscript-plugins/load-failed.moon Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,30 +0,0 @@
    --- Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    ---
    --- This library is free software; you can redistribute it and/or
    --- modify it under the terms of the GNU Lesser General Public
    --- License as published by the Free Software Foundation; either
    --- version 2 of the License, or (at your option) any later version.
    ---
    --- This library is distributed in the hope that it will be useful,
    --- but WITHOUT ANY WARRANTY; without even the implied warranty of
    --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    --- Lesser General Public License for more details.
    ---
    --- You should have received a copy of the GNU Lesser General Public
    --- License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -
    -lgi = require 'lgi'
    -GPlugin = lgi.GPlugin
    -
    -export gplugin_query = ->
    - return GPlugin.PluginInfo {
    - id: "gplugin/moon-load-failed"
    - }
    -
    -
    -export gplugin_load = ->
    - return false
    -
    -
    -export gplugin_unload = ->
    - return true
    --- a/lua/tests/moonscript-plugins/unload-failed.moon Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,30 +0,0 @@
    --- Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    ---
    --- This library is free software; you can redistribute it and/or
    --- modify it under the terms of the GNU Lesser General Public
    --- License as published by the Free Software Foundation; either
    --- version 2 of the License, or (at your option) any later version.
    ---
    --- This library is distributed in the hope that it will be useful,
    --- but WITHOUT ANY WARRANTY; without even the implied warranty of
    --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    --- Lesser General Public License for more details.
    ---
    --- You should have received a copy of the GNU Lesser General Public
    --- License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -
    -lgi = require 'lgi'
    -GPlugin = lgi.GPlugin
    -
    -export gplugin_query = ->
    - return GPlugin.PluginInfo {
    - id: "gplugin/moon-unload-failed"
    - }
    -
    -
    -export gplugin_load = ->
    - return true
    -
    -
    -export gplugin_unload = ->
    - return false
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/lua/tests/plugins/basic.lua Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,42 @@
    +--[[
    + Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +
    + This library is free software; you can redistribute it and/or
    + modify it under the terms of the GNU Lesser General Public
    + License as published by the Free Software Foundation; either
    + version 2 of the License, or (at your option) any later version.
    +
    + This library is distributed in the hope that it will be useful,
    + but WITHOUT ANY WARRANTY; without even the implied warranty of
    + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + Lesser General Public License for more details.
    +
    + You should have received a copy of the GNU Lesser General Public
    + License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +--]]
    +
    +local lgi = require 'lgi'
    +local GPlugin = lgi.require('GPlugin', '1.0')
    +
    +function gplugin_query()
    + return GPlugin.PluginInfo {
    + id = "gplugin/lua-basic-plugin",
    + abi_version = 0x01020304,
    + name = "basic plugin",
    + category = "test",
    + version = "version",
    + license_id = "license",
    + summary = "summary",
    + description = "description",
    + authors = { "author1" },
    + website = "website"
    + }
    +end
    +
    +function gplugin_load(plugin)
    + return true
    +end
    +
    +function gplugin_unload(plugin)
    + return true
    +end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/lua/tests/plugins/dependent.lua Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,34 @@
    +--[[
    + Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +
    + This library is free software; you can redistribute it and/or
    + modify it under the terms of the GNU Lesser General Public
    + License as published by the Free Software Foundation; either
    + version 2 of the License, or (at your option) any later version.
    +
    + This library is distributed in the hope that it will be useful,
    + but WITHOUT ANY WARRANTY; without even the implied warranty of
    + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + Lesser General Public License for more details.
    +
    + You should have received a copy of the GNU Lesser General Public
    + License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +--]]
    +
    +local lgi = require 'lgi'
    +local GPlugin = lgi.require('GPlugin', '1.0')
    +
    +function gplugin_query()
    + return GPlugin.PluginInfo {
    + id="gplugin/lua-dependent-plugin",
    + dependencies={"dependency1", "dependency2"},
    + }
    +end
    +
    +function gplugin_load(plugin)
    + return false
    +end
    +
    +function gplugin_unload(plugin)
    + return false
    +end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/lua/tests/plugins/load-exception.lua Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,33 @@
    +--[[
    + Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +
    + This library is free software; you can redistribute it and/or
    + modify it under the terms of the GNU Lesser General Public
    + License as published by the Free Software Foundation; either
    + version 2 of the License, or (at your option) any later version.
    +
    + This library is distributed in the hope that it will be useful,
    + but WITHOUT ANY WARRANTY; without even the implied warranty of
    + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + Lesser General Public License for more details.
    +
    + You should have received a copy of the GNU Lesser General Public
    + License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +--]]
    +
    +local lgi = require 'lgi'
    +local GPlugin = lgi.require('GPlugin', '1.0')
    +
    +function gplugin_query()
    + return GPlugin.PluginInfo {
    + id="gplugin/lua-load-exception",
    + }
    +end
    +
    +function gplugin_load(plugin)
    + error('explosion!')
    +end
    +
    +function gplugin_unload(plugin)
    + return false
    +end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/lua/tests/plugins/load-failed.lua Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,33 @@
    +--[[
    + Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +
    + This library is free software; you can redistribute it and/or
    + modify it under the terms of the GNU Lesser General Public
    + License as published by the Free Software Foundation; either
    + version 2 of the License, or (at your option) any later version.
    +
    + This library is distributed in the hope that it will be useful,
    + but WITHOUT ANY WARRANTY; without even the implied warranty of
    + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + Lesser General Public License for more details.
    +
    + You should have received a copy of the GNU Lesser General Public
    + License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +--]]
    +
    +local lgi = require 'lgi'
    +local GPlugin = lgi.require('GPlugin', '1.0')
    +
    +function gplugin_query()
    + return GPlugin.PluginInfo {
    + id="gplugin/lua-load-failed",
    + }
    +end
    +
    +function gplugin_load(plugin)
    + return false
    +end
    +
    +function gplugin_unload(plugin)
    + return true
    +end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/lua/tests/plugins/unload-failed.lua Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,33 @@
    +--[[
    + Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +
    + This library is free software; you can redistribute it and/or
    + modify it under the terms of the GNU Lesser General Public
    + License as published by the Free Software Foundation; either
    + version 2 of the License, or (at your option) any later version.
    +
    + This library is distributed in the hope that it will be useful,
    + but WITHOUT ANY WARRANTY; without even the implied warranty of
    + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + Lesser General Public License for more details.
    +
    + You should have received a copy of the GNU Lesser General Public
    + License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +--]]
    +
    +local lgi = require 'lgi'
    +local GPlugin = lgi.require('GPlugin', '1.0')
    +
    +function gplugin_query()
    + return GPlugin.PluginInfo {
    + id="gplugin/lua-unload-failed",
    + }
    +end
    +
    +function gplugin_load(plugin)
    + return true
    +end
    +
    +function gplugin_unload(plugin)
    + return false
    +end
    --- a/lua/tests/test-lua-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/lua/tests/test-lua-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -16,16 +16,16 @@
    */
    #include <glib.h>
    +
    #include <gplugin.h>
    -
    #include <gplugin/gplugin-loader-tests.h>
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_loader_tests_main(LUA_LOADER_DIR, LUA_PLUGIN_DIR, "lua");
    return g_test_run();
    }
    -
    --- a/lua/tests/test-lua-moon-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,31 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <glib.h>
    -#include <gplugin.h>
    -
    -#include <gplugin/gplugin-loader-tests.h>
    -
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - gplugin_loader_tests_main(LUA_LOADER_DIR, MOONSCRIPT_PLUGIN_DIR, "moon");
    -
    - return g_test_run();
    -}
    -
    --- a/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -1,8 +1,8 @@
    ###############################################################################
    # Project Info
    ###############################################################################
    -project('gplugin', 'c', version : '0.29.0',
    - meson_version : '>=0.42.0',
    +project('gplugin', 'c', version : '0.30.0-dev',
    + meson_version : '>=0.50.0',
    default_options : ['c_std=c99'])
    parts = meson.project_version().split('-')
    @@ -22,7 +22,7 @@
    version_conf.set('GPLUGIN_EXTRA_VERSION', extra)
    version_conf.set('GPLUGIN_VERSION', meson.project_version())
    -LOCALE_DIR = join_paths(get_option('prefix'), get_option('localedir'))
    +LOCALE_DIR = get_option('prefix') / get_option('localedir')
    add_project_arguments('-DLOCALEDIR="@0@"'.format(LOCALE_DIR), language : 'c')
    ###############################################################################
    @@ -61,7 +61,7 @@
    add_project_arguments(
    '-DPREFIX="@0@"'.format(get_option('prefix')),
    '-DLIBDIR="@0@"'.format(get_option('libdir')),
    - '-DGPLUGIN_WEBSITE="https://bitbucket.org/gplugin/gplugin"',
    + '-DGPLUGIN_WEBSITE="https://keep.imfreedom.org/gplugin/gplugin"',
    language : 'c'
    )
    @@ -72,23 +72,12 @@
    endif
    endif
    -configure_file(
    - input : 'gplugin-version.sh.in',
    - output : 'gplugin-version.sh',
    - configuration : version_conf)
    -
    toplevel_inc = include_directories('.')
    -
    ###############################################################################
    # gtk-doc
    ###############################################################################
    ENABLE_DOC = get_option('doc')
    -if ENABLE_DOC
    - if meson.version().version_compare('<0.41.2')
    - error('Meson 0.41.2 or newer is required to build documentation.')
    - endif
    -endif
    ###############################################################################
    # Subdirectories
    @@ -96,12 +85,12 @@
    subdir('gplugin')
    subdir('gplugin-gtk')
    subdir('gplugin-gtk-viewer')
    +subdir('gplugin-query')
    subdir('packaging')
    -subdir('plugins')
    subdir('po')
    subdir('lua')
    -subdir('perl')
    +subdir('perl5')
    subdir('python')
    subdir('tcc')
    subdir('vala')
    @@ -110,6 +99,5 @@
    # Install stuff
    ###############################################################################
    # documentation
    -install_data('ChangeLog', 'INSTALL.md', 'README.md', 'HACKING',
    - install_dir : join_paths(get_option('datadir'), 'doc', 'gplugin'))
    -
    +install_data('ChangeLog', 'INSTALL.md', 'README.md', 'HACKING.md',
    + install_dir : get_option('datadir') / 'doc' / 'gplugin')
    --- a/meson_options.txt Thu Nov 07 23:09:55 2019 -0600
    +++ b/meson_options.txt Sat Sep 05 20:05:36 2020 -0500
    @@ -1,16 +1,19 @@
    -###############################################################################
    -# Options
    -###############################################################################
    option(
    - 'gobject-introspection',
    - type : 'boolean', value : true,
    + 'doc',
    + type : 'boolean', value : true, yield : true,
    + description : 'build documentation with gtk-doc'
    +)
    +
    +option(
    + 'introspection',
    + type : 'boolean', value : true, yield : true,
    description : 'Whether or not to build a GObject Introspection type library'
    )
    option(
    - 'nls',
    + 'gtk3',
    type : 'boolean', value : true,
    - description : 'Install translation files'
    + description : 'Whether or not to build the gtk3 library'
    )
    option(
    @@ -20,37 +23,25 @@
    )
    option(
    - 'doc',
    - type : 'boolean', value : true,
    - description : 'build documentation with gtk-doc'
    -)
    -
    -option(
    - 'gtk3',
    - type : 'boolean', value : true,
    - description : 'Whether or not to build the gtk3 library'
    -)
    -
    -option(
    'lua',
    type : 'boolean', value : true,
    description : 'Whether or not to build the Lua plugin loader'
    )
    option(
    - 'moonscript-tests',
    - type : 'boolean', value : false,
    - description : 'Whether or not to run the moonscript tests'
    + 'nls',
    + type : 'boolean', value : true,
    + description : 'Install translation files'
    )
    option(
    - 'perl',
    - type : 'boolean', value : false,
    + 'perl5',
    + type : 'boolean', value : true,
    description : 'Whether or not to build the Perl plugin loader'
    )
    option(
    - 'python',
    + 'python3',
    type : 'boolean', value : true,
    description : 'Whether or not to build the Python 3.x plugin loader'
    )
    --- a/packaging/debian/compat Thu Nov 07 23:09:55 2019 -0600
    +++ b/packaging/debian/compat Sat Sep 05 20:05:36 2020 -0500
    @@ -1,1 +1,1 @@
    -9
    +10
    --- a/packaging/debian/control Thu Nov 07 23:09:55 2019 -0600
    +++ b/packaging/debian/control Sat Sep 05 20:05:36 2020 -0500
    @@ -6,7 +6,7 @@
    meson (>=0.37.0), libglib2.0-dev, libgtk-3-dev,
    gettext, help2man,
    gobject-introspection, libgirepository1.0-dev,
    - liblua5.1-0-dev, lua-lgi,
    + liblua5.3-dev, lua-lgi,
    python3-dev, python-gi-dev, python3-gi,
    valac
    Homepage: https://bitbucket.org/gplugin/gplugin
    @@ -51,6 +51,7 @@
    Architecture: all
    Depends: ${misc:Depends}, ${shlibs:Depends},
    libgplugin-lua,
    + libgplugin-perl5,
    libgplugin-python
    Description: metapackage for all gplugin loaders
    GPlugin is a GObject based library that implements a reusable plugin system
    @@ -91,7 +92,7 @@
    .
    This package contains the gplugin-gtk-viewer application for viewing plugins.
    -Package: gir1.2-gplugin-0.0
    +Package: gir1.2-gplugin-1.0
    Architecture: any
    Depends: ${gir:Depends}, ${misc:Depends}, libgplugin0
    Section: introspection
    @@ -103,6 +104,18 @@
    This package can be used by other packages using the GIRepository format to
    generate dynamic bindings for libgplugin.
    +Package: gir1.2-gplugin-gtk-1.0
    +Architecture: any
    +Depends: ${gir:Depends}, ${misc:Depends}, libgplugin0
    +Section: introspection
    +Description: typelibe for libgplugin
    + GPlugin is a GObject based library that implements a reusable plugin system
    + which supports loading plugins in other languages via loaders. It relies
    + heavily on GObjectIntrospection to expose its API to the other languages.
    + .
    + This package can be used by other packages using the GIRepository format to
    + generate dynamic bindings for libgplugin-gtk.
    +
    Package: libgplugin-lua
    Architecture: any
    Depends: ${misc:Depends}, ${shlibs:Depends}, liblua5.1-0,
    @@ -115,7 +128,7 @@
    This package allows GPlugin to load plugins written in the Lua programming
    language.
    -Package: libgplugin-python
    +Package: libgplugin-python3
    Architecture: any
    Depends: ${misc:Depends}, ${shlibs:Depends}, python3, python3-gi,
    libgplugin0, gir1.2-gplugin-0.0
    @@ -124,6 +137,18 @@
    which supports loading plugins in other languages via loaders. It relies
    heavily on GObjectIntrospection to expose its API to the other languages.
    .
    - This package allows GPlugin to load plugin written in the Python programming
    + This package allows GPlugin to load plugins written in the Python programming
    language.
    +Package: libgplugin-perl5
    +Architecture: any
    +Depends: ${misc:Depends}, ${shlibs:Depends}, perl, libglib-perl,
    + libglib-object-introspection-perl, libgplugin0, gir1.2-gplugin-0.0
    +Description: GPlugin Perl5 Loader
    + GPlugin is a GObject based library that implements a reusable plugin system
    + which supports loading plugins in other languages via loaders. It relies
    + heavily on GObjectIntrospection to expose its API to the other languages.
    + .
    + This package allows GPlugin to load plugins written in the Perl5 programming
    + language.
    +
    --- a/packaging/debian/gir1.2-gplugin-0.0.install Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2 +0,0 @@
    -debian/tmp/usr/lib/*/girepository-1.0/
    -debian/tmp/usr/share/gir-1.0/
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/packaging/debian/gir1.2-gplugin-1.0.install Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,1 @@
    +debian/tmp/usr/lib/*/girepository-1.0/GPlugin-1.0.typelib
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/packaging/debian/gir1.2-gplugin-gtk-1.0.install Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,1 @@
    +debian/tmp/usr/lib/*/girepository-1.0/GPluginGtk-1.0.typelib
    --- a/packaging/debian/libgplugin-dev.install Thu Nov 07 23:09:55 2019 -0600
    +++ b/packaging/debian/libgplugin-dev.install Sat Sep 05 20:05:36 2020 -0500
    @@ -3,6 +3,7 @@
    debian/tmp/usr/include/gplugin-1.0/gplugin-native.h
    debian/tmp/usr/lib/*/libgplugin.so
    debian/tmp/usr/lib/*/pkgconfig/gplugin.pc
    +debian/tmp/usr/share/gir-1.0/GPlugin-1.0.gir
    +debian/tmp/usr/share/gplugin/valgrind/gplugin.supp
    debian/tmp/usr/share/vala/vapi/gplugin.deps
    debian/tmp/usr/share/vala/vapi/gplugin.vapi
    -
    --- a/packaging/debian/libgplugin-gtk-dev.install Thu Nov 07 23:09:55 2019 -0600
    +++ b/packaging/debian/libgplugin-gtk-dev.install Sat Sep 05 20:05:36 2020 -0500
    @@ -2,5 +2,6 @@
    usr/include/gplugin-1.0/gplugin-gtk.h
    debian/tmp/usr/lib/*/libgplugin-gtk.so
    debian/tmp/usr/lib/*/pkgconfig/gplugin-gtk.pc
    +debian/tmp/usr/share/gir-1.0/GPluginGtk-1.0.gir
    debian/tmp/usr/share/vala/vapi/gplugin-gtk.deps
    -debian/tmp/usr/share/vala/vapi/gplugin-gtk.vapi
    +debian/tmp/usr/share/vala/vapi/gplugin-gtk.vapi
    \ No newline at end of file
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/packaging/debian/libgplugin-perl5.install Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,1 @@
    +debian/tmp/usr/lib/*/gplugin/gplugin-perl5.so
    --- a/packaging/debian/libgplugin-python.install Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1 +0,0 @@
    -debian/tmp/usr/lib/*/gplugin/gplugin-python.so
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/packaging/debian/libgplugin-python3.install Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,1 @@
    +debian/tmp/usr/lib/*/gplugin/gplugin-python3.so
    --- a/packaging/gplugin.spec.in Thu Nov 07 23:09:55 2019 -0600
    +++ b/packaging/gplugin.spec.in Sat Sep 05 20:05:36 2020 -0500
    @@ -23,7 +23,14 @@
    BuildRequires: python3-devel
    BuildRequires: python3-gobject
    BuildRequires: vala
    -BuildRequires: /usr/lib64/pkgconfig/pygobject-3.0.pc
    +BuildRequires: perl-Glib
    +BuildRequires: perl-Glib-Object-Introspection
    +%if 0%{?fedora}
    +BuildRequires: perl-devel
    +%endif
    +%if 0%{?opensuse}
    +BuildRequires: perl-ExtUtils-Config
    +%endif
    %package -n libgplugin0
    Summary: A GObject based library that implements a reusable plugin system
    @@ -54,6 +61,10 @@
    Summary: A GObject based library that implements a reusable plugin system
    Group: Development/Libraries
    +%package perl5
    +Summary: A GObject based library that implements a reusable plugin system
    +Group: Development/Libraries
    +
    %package python3
    Summary: A GObject based library that implements a reusable plugin system
    Group: Development/Libraries
    @@ -68,31 +79,16 @@
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    -
    %description -n libgplugin0
    GPlugin is a GObject based library that implements a reusable plugin system
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    -
    %description devel
    GPlugin is a GObject based library that implements a reusable plugin system
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    -
    This package contains all necessary include files and libraries needed
    to develop applications that require these.
    @@ -101,11 +97,6 @@
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    -
    This package contains additional GTK3 dependencies for %{name} library.
    %description gtk3-devel
    @@ -113,11 +104,6 @@
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    -
    This package contains all necessary include files and libraries needed
    to develop GTK3 applications that require these.
    @@ -126,11 +112,6 @@
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    -
    This package contains the vapi bindings allowing GPluginGtk to be used from
    vala.
    @@ -139,23 +120,20 @@
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    +This package contains the Lua Loader for %{name} library.
    -This package contains the Lua Loader for %{name} library.
    +%description perl5
    +GPlugin is a GObject based library that implements a reusable plugin system
    +that supports loading plugins in other languages via loaders. GPlugin also
    +implements dependencies among the plugins.
    +
    +This package contains Perl loader for %{name} library.
    %description python3
    GPlugin is a GObject based library that implements a reusable plugin system
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    -
    This package contains Python 3 loader for %{name} library.
    %description vala
    @@ -163,18 +141,11 @@
    that supports loading plugins in other languages via loaders. GPlugin also
    implements dependencies among the plugins.
    -It was started due to the infamous "Plugin Problem" for Guifications 3, which
    -was that I needed plugins that could depend on each other, be able to be
    -written in other languages, have plugins that are loaded before the main load
    -phase, and allow plugins to register types into the GObject type system.
    -
    This package contains the vapi bindings allowing GPlugin to be used from vala.
    %build
    CFLAGS="%{optflags}" meson \
    --prefix=%{_prefix} \
    - -Dlua=true \
    - -Dpython=true \
    build
    ninja -C build %{?_smp_mflags}
    @@ -196,21 +167,26 @@
    %files
    %defattr(-,root,root)
    %doc README.md ChangeLog
    +%license COPYING
    %{_bindir}/gplugin-query
    %{_libdir}/gplugin/
    %{_mandir}/man1/gplugin-query.1*
    %files -n libgplugin0
    %defattr(-,root,root)
    +%doc README.md
    +%license COPYING
    %{_libdir}/libgplugin.so.0.1.0
    %files devel
    %defattr(-,root,root)
    -%doc HACKING README.md
    -%{_datadir}/gir-1.0/GPlugin-0.0.gir
    +%doc HACKING.md README.md
    +%license COPYING
    +%{_datadir}/gplugin/
    +%{_datadir}/gir-1.0/GPlugin-1.0.gir
    %{_datadir}/gtk-doc/html/gplugin/
    %{_includedir}/gplugin-1.0/
    -%{_libdir}/girepository-1.0/GPlugin-0.0.typelib
    +%{_libdir}/girepository-1.0/GPlugin-1.0.typelib
    %{_libdir}/libgplugin.so
    %{_libdir}/libgplugin.so.0
    %{_libdir}/pkgconfig/gplugin-gtk.pc
    @@ -219,6 +195,7 @@
    %files gtk3
    %defattr(-,root,root)
    %doc README.md
    +%license COPYING
    %{_bindir}/gplugin-gtk-viewer
    %{_libdir}/libgplugin-gtk.so.0.1.0
    %{_mandir}/man1/gplugin-gtk-viewer.1*
    @@ -227,23 +204,38 @@
    %defattr(-,root,root)
    %dir %{_datadir}/glade/catalogs/
    %doc README.md
    -%{_datadir}/gir-1.0/GPluginGtk-0.0.gir
    +%license COPYING
    +%{_datadir}/gir-1.0/GPluginGtk-1.0.gir
    %{_datadir}/glade/catalogs/gplugin-gtk.xml
    %{_datadir}/gtk-doc/html/gplugin-gtk/
    -%{_libdir}/girepository-1.0/GPluginGtk-0.0.typelib
    +%{_libdir}/girepository-1.0/GPluginGtk-1.0.typelib
    %{_libdir}/libgplugin-gtk.so
    %{_libdir}/libgplugin-gtk.so.0
    +%files gtk3-vala
    +%defattr(-,root,root)
    +%doc README.md
    +%license COPYING
    +%{_datadir}/vala/vapi/gplugin-gtk.vapi
    +%{_datadir}/vala/vapi/gplugin-gtk.deps
    +
    %files lua
    %defattr(-,root,root)
    %doc README.md
    %license COPYING
    %{_libdir}/gplugin/gplugin-lua.so
    +%files perl5
    +%defattr(-,root,root)
    +%doc README.md
    +%license COPYING
    +%{_libdir}/gplugin/gplugin-perl5.so
    +
    %files python3
    %defattr(-,root,root)
    %doc README.md
    -%{_libdir}/gplugin/gplugin-python.so
    +%license COPYING
    +%{_libdir}/gplugin/gplugin-python3.so
    %files vala
    %defattr(-,root,root)
    @@ -252,14 +244,14 @@
    %{_datadir}/vala/vapi/gplugin.vapi
    %{_datadir}/vala/vapi/gplugin.deps
    -%files gtk3-vala
    -%defattr(-,root,root)
    -%doc README.md
    -%license COPYING
    -%{_datadir}/vala/vapi/gplugin-gtk.vapi
    -%{_datadir}/vala/vapi/gplugin-gtk.deps
    +%changelog
    +* Tue Aug 25 2020 Gary Kramlich <grim@reaperworld.com>
    +- Updated the perl packages for its new name.
    -%changelog
    +* Tue Feb 18 2020 Gary Kramlich <grim@reaperworld.com>
    +- Add the perl package
    +- Updated the python files to reference the new name for gplugin-python3
    +
    * Thu Oct 31 2019 Gary Kramlich <grim@reaperworld.com>
    - Fixed an include bug in gplugin-gtk
    - Fixed building again python3.8
    --- a/packaging/mingw-cross/PKGBUILD Thu Nov 07 23:09:55 2019 -0600
    +++ b/packaging/mingw-cross/PKGBUILD Sat Sep 05 20:05:36 2020 -0500
    @@ -26,8 +26,9 @@
    --cross-file=${MINGW_PACKAGE_PREFIX}.txt \
    -Ddoc=false\
    -Dlua=false \
    - -Dpython=false \
    - -Dgobject-introspection=false \
    + -Dperl5=false \
    + -Dpython3=false \
    + -Dintrospection=false \
    -Dvapi=false \
    -Dhelp2man=false \
    ../
    --- a/perl/gplugin-perl-core.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,63 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -#include "gplugin-perl-loader.h"
    -#include "gplugin-perl-plugin.h"
    -
    -G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(GError **error) {
    - const gchar * const authors[] = {
    - "Gary Kramlich <grim@reaperworld.com>",
    - NULL,
    - };
    -
    - return gplugin_plugin_info_new(
    - "gplugin/perl-loader",
    - GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - "internal", TRUE,
    - "load-on-query", TRUE,
    - "name", "Perl plugin loader",
    - "version", GPLUGIN_VERSION,
    - "license-id", "GPL3",
    - "summary", "A plugin that can load perl plugins",
    - "description", "This plugin allows the loading of plugins written in "
    - "the perl programming language.",
    - "authors", authors,
    - "website", GPLUGIN_WEBSITE,
    - "category", "loaders",
    - NULL
    - );
    -}
    -
    -G_MODULE_EXPORT gboolean
    -gplugin_load(GPluginNativePlugin *plugin, GError **error) {
    - gplugin_perl_plugin_register(plugin);
    - gplugin_perl_loader_register(plugin);
    -
    - gplugin_manager_register_loader(GPLUGIN_PERL_TYPE_LOADER);
    -
    - return TRUE;
    -}
    -
    -G_MODULE_EXPORT gboolean
    -gplugin_unload(GPluginNativePlugin *plugin, GError **error) {
    - return TRUE;
    -}
    -
    --- a/perl/gplugin-perl-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,138 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include "gplugin-perl-loader.h"
    -
    -#include <EXTERN.h>
    -#include <perl.h>
    -
    -/* perl define's _() to something completely different that we don't use. So
    - * we undef it so that we can use it for gettext.
    - */
    -#undef _
    -#include <glib/gi18n.h>
    -
    -struct _GPluginPerlLoader {
    - GPluginLoader parent;
    -};
    -
    -G_DEFINE_DYNAMIC_TYPE(GPluginPerlLoader, gplugin_perl_loader, GPLUGIN_TYPE_LOADER);
    -
    -/* I can't believe I have to use this variable name... */
    -static PerlInterpreter *my_perl = NULL;
    -
    -/******************************************************************************
    - * GPluginLoaderInterface API
    - *****************************************************************************/
    -static GSList *
    -gplugin_perl_loader_class_supported_extensions(const GPluginLoaderClass *klass) {
    - return g_slist_append(NULL, "pl");
    -}
    -
    -static GPluginPlugin *
    -gplugin_perl_loader_query(GPluginLoader *loader,
    - const gchar *filename,
    - GError **error)
    -{
    - const gchar *args[] = { "", filename };
    - gchar **argv = (gchar **)args;
    - gint argc = 2;
    -
    - perl_parse(my_perl, NULL, argc, argv, NULL);
    -
    - call_argv("gplugin_plugin_query", G_DISCARD | G_NOARGS, argv);
    -
    - return NULL;
    -}
    -
    -static gboolean
    -gplugin_perl_loader_load(GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - GError **error)
    -{
    - return FALSE;
    -}
    -
    -static gboolean
    -gplugin_perl_loader_unload(GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - GError **error)
    -{
    - return FALSE;
    -}
    -
    -/******************************************************************************
    - * Perl Stuff
    - *****************************************************************************/
    -static void
    -gplugin_perl_loader_init_perl(void) {
    - gchar *args[] = { "", };
    - gchar **argv = (gchar **)args;
    - gint argc = 1;
    -
    - PERL_SYS_INIT(&argc, &argv);
    -
    - my_perl = perl_alloc();
    - PERL_SET_CONTEXT(my_perl);
    - PL_perl_destruct_level = 1;
    - perl_construct(my_perl);
    -}
    -
    -static void
    -gplugin_perl_loader_uninit_perl(void) {
    - PERL_SYS_TERM();
    -
    - perl_destruct(my_perl);
    - perl_free(my_perl);
    - my_perl = NULL;
    -}
    -
    -/******************************************************************************
    - * GObject Stuff
    - *****************************************************************************/
    -static void
    -gplugin_perl_loader_init(G_GNUC_UNUSED GPluginPerlLoader *loader) {
    -}
    -
    -static void
    -gplugin_perl_loader_class_init(GPluginPerlLoaderClass *klass) {
    - GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
    -
    - loader_class->supported_extensions =
    - gplugin_perl_loader_class_supported_extensions;
    - loader_class->query = gplugin_perl_loader_query;
    - loader_class->load = gplugin_perl_loader_load;
    - loader_class->unload = gplugin_perl_loader_unload;
    -
    - /* perl initialization */
    - gplugin_perl_loader_init_perl();
    -}
    -
    -static void
    -gplugin_perl_loader_class_finalize(G_GNUC_UNUSED GPluginPerlLoaderClass *klass)
    -{
    - /* perl uninitialization */
    - gplugin_perl_loader_uninit_perl();
    -}
    -
    -/******************************************************************************
    - * API
    - *****************************************************************************/
    -void
    -gplugin_perl_loader_register(GPluginNativePlugin *plugin) {
    - gplugin_perl_loader_register_type(G_TYPE_MODULE(plugin));
    -}
    --- a/perl/gplugin-perl-loader.h Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#ifndef GPLUGIN_PERL_LOADER_H
    -#define GPLUGIN_PERL_LOADER_H
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -G_BEGIN_DECLS
    -
    -#define GPLUGIN_PERL_TYPE_LOADER (gplugin_perl_loader_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginPerlLoader, gplugin_perl_loader, GPLUGIN_PERL, LOADER, GPluginLoader)
    -
    -void gplugin_perl_loader_register(GPluginNativePlugin *plugin);
    -
    -G_END_DECLS
    -
    -#endif /* GPLUGIN_PERL_PLUGIN_LOADER_H */
    -
    --- a/perl/gplugin-perl-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,205 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include "gplugin-perl-plugin.h"
    -
    -#include <EXTERN.h>
    -#include <perl.h>
    -
    -/* perl define's _() to something completely different that we don't use. So
    - * we undef it so that we can use it for gettext.
    - */
    -#undef _
    -#include <glib/gi18n.h>
    -
    -/******************************************************************************
    - * Typedefs
    - *****************************************************************************/
    -struct _GPluginPerlPlugin {
    - GObject parent;
    -
    - PerlInterpreter *interpreter;
    -
    - /* overrides */
    - gchar *filename;
    - GPluginLoader *loader;
    - GPluginPluginInfo *info;
    - GPluginPluginState state;
    -};
    -
    -/******************************************************************************
    - * Enums
    - *****************************************************************************/
    -enum {
    - PROP_ZERO,
    - PROP_INTERPRETER,
    - N_PROPERTIES,
    - /* overrides */
    - PROP_FILENAME = N_PROPERTIES,
    - PROP_LOADER,
    - PROP_INFO,
    - PROP_STATE
    -};
    -static GParamSpec *properties[N_PROPERTIES] = {NULL,};
    -
    -/* I hate forward declarations... */
    -static void gplugin_perl_plugin_iface_init(GPluginPluginInterface *iface);
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    - GPluginPerlPlugin,
    - gplugin_perl_plugin,
    - G_TYPE_OBJECT,
    - 0,
    - G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_PLUGIN, gplugin_perl_plugin_iface_init)
    -);
    -
    -/******************************************************************************
    - * GPluginPlugin Implementation
    - *****************************************************************************/
    -static void
    -gplugin_perl_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface)
    -{
    -}
    -
    -/******************************************************************************
    - * Object Stuff
    - *****************************************************************************/
    -static void
    -gplugin_perl_plugin_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *pspec)
    -{
    - GPluginPerlPlugin *plugin = GPLUGIN_PERL_PLUGIN(obj);
    -
    - switch(param_id) {
    - case PROP_INTERPRETER:
    - g_value_set_pointer(value,
    - gplugin_perl_plugin_get_interpreter(plugin));
    - break;
    -
    - /* overrides */
    - case PROP_FILENAME:
    - g_value_set_string(value, plugin->filename);
    - break;
    - case PROP_LOADER:
    - g_value_set_object(value, plugin->loader);
    - break;
    - case PROP_INFO:
    - g_value_set_object(value, plugin->info);
    - break;
    - case PROP_STATE:
    - g_value_set_enum(value, plugin->state);
    - break;
    -
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    - break;
    - }
    -}
    -
    -static void
    -gplugin_perl_plugin_set_property(GObject *obj, guint param_id,
    - const GValue *value, GParamSpec *pspec)
    -{
    - GPluginPerlPlugin *plugin = GPLUGIN_PERL_PLUGIN(obj);
    -
    - switch(param_id) {
    - case PROP_INTERPRETER:
    - plugin->interpreter = g_value_get_pointer(value);
    - break;
    -
    - /* overrides */
    - case PROP_FILENAME:
    - plugin->filename = g_value_dup_string(value);
    - break;
    - case PROP_LOADER:
    - plugin->loader = g_value_dup_object(value);
    - break;
    - case PROP_INFO:
    - plugin->info = g_value_dup_object(value);
    - break;
    - case PROP_STATE:
    - plugin->state = g_value_get_enum(value);
    - break;
    -
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    - }
    -}
    -
    -static void
    -gplugin_perl_plugin_finalize(GObject *obj) {
    - GPluginPerlPlugin *plugin = GPLUGIN_PERL_PLUGIN(obj);
    -
    - perl_destruct(plugin->interpreter);
    - perl_free(plugin->interpreter);
    - plugin->interpreter = NULL;
    -
    - g_clear_pointer(&plugin->filename, g_free);
    - g_clear_object(&plugin->loader);
    - g_clear_object(&plugin->info);
    -
    - G_OBJECT_CLASS(gplugin_perl_plugin_parent_class)->finalize(obj);
    -}
    -
    -static void
    -gplugin_perl_plugin_init(G_GNUC_UNUSED GPluginPerlPlugin *plugin)
    -{
    -}
    -
    -static void
    -gplugin_perl_plugin_class_finalize(G_GNUC_UNUSED GPluginPerlPluginClass *klass)
    -{
    -}
    -
    -static void
    -gplugin_perl_plugin_class_init(GPluginPerlPluginClass *klass) {
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    -
    - obj_class->get_property = gplugin_perl_plugin_get_property;
    - obj_class->set_property = gplugin_perl_plugin_set_property;
    - obj_class->finalize = gplugin_perl_plugin_finalize;
    -
    - properties[PROP_INTERPRETER] = g_param_spec_pointer(
    - "interpreter", "interpreter",
    - "The PERL interpreter for this plugin",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
    - );
    -
    - g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    -
    - /* add our overrides */
    - g_object_class_override_property(obj_class, PROP_FILENAME, "filename");
    - g_object_class_override_property(obj_class, PROP_LOADER, "loader");
    - g_object_class_override_property(obj_class, PROP_INFO, "info");
    - g_object_class_override_property(obj_class, PROP_STATE, "state");
    -}
    -
    -/******************************************************************************
    - * API
    - *****************************************************************************/
    -void
    -gplugin_perl_plugin_register(GPluginNativePlugin *native)
    -{
    - gplugin_perl_plugin_register_type(G_TYPE_MODULE(native));
    -}
    -
    -PerlInterpreter *
    -gplugin_perl_plugin_get_interpreter(GPluginPerlPlugin *plugin) {
    - g_return_val_if_fail(GPLUGIN_PERL_IS_PLUGIN(plugin), NULL);
    -
    - return plugin->interpreter;
    -}
    --- a/perl/gplugin-perl-plugin.h Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,39 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#ifndef GPLUGIN_PERL_PLUGIN_H
    -#define GPLUGIN_PERL_PLUGIN_H
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -#include <EXTERN.h>
    -#include <perl.h>
    -#undef _
    -
    -G_BEGIN_DECLS
    -
    -#define GPLUGIN_PERL_TYPE_PLUGIN (gplugin_perl_plugin_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginPerlPlugin, gplugin_perl_plugin, GPLUGIN_PERL, PLUGIN, GObject)
    -
    -void gplugin_perl_plugin_register(GPluginNativePlugin *native);
    -
    -PerlInterpreter *gplugin_perl_plugin_get_interpreter(GPluginPerlPlugin *plugin);
    -
    -G_END_DECLS
    -
    -#endif /* GPLUGIN_PERL_PLUGIN_H */
    --- a/perl/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,37 +0,0 @@
    -if get_option('perl')
    - if not get_option('gobject-introspection')
    - error('Perl plugin requires GObject Introspection.')
    - endif
    -
    - GPLUGIN_PERL_SOURCES = [
    - 'gplugin-perl-core.c',
    - 'gplugin-perl-loader.c',
    - 'gplugin-perl-plugin.c',
    - ]
    -
    - GPLUGIN_PERL_HEADERS = [
    - 'gplugin-perl-loader.h',
    - 'gplugin-perl-plugin.h',
    - ]
    -
    - PERL = find_program('perl')
    - PERL_CFLAGS = run_command(PERL, '-MExtUtils::Embed', '-e', 'ccopts').stdout()
    - PERL_LDFLAGS = run_command(PERL, '-MExtUtils::Embed', '-e', 'ldopts').stdout()
    -
    - message('PERL_CFLAGS ' + PERL_CFLAGS)
    - message('PERL_LDFLAGS ' + PERL_LDFLAGS)
    -
    - PERL_CFLAGS = PERL_CFLAGS.split()
    - PERL_LDFLAGS = PERL_LDFLAGS.split()
    -
    - shared_library('gplugin-perl',
    - GPLUGIN_PERL_SOURCES,
    - GPLUGIN_PERL_HEADERS,
    - c_args : PERL_CFLAGS,
    - link_args : PERL_LDFLAGS,
    - name_prefix : '',
    - dependencies : [gplugin_dep],
    - install : true,
    - install_dir : join_paths(get_option('libdir'), 'gplugin')
    - )
    -endif # perl
    --- a/perl/tests/plugins/basic.pl Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,49 +0,0 @@
    -# vi:et:ts=4 sw=4 sts=4
    -# Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    -#
    -# This library is free software; you can redistribute it and/or
    -# modify it under the terms of the GNU Lesser General Public
    -# License as published by the Free Software Foundation; either
    -# version 2 of the License, or (at your option) any later version.
    -#
    -# This library is distributed in the hope that it will be useful,
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    -# Lesser General Public License for more details.
    -#
    -# You should have received a copy of the GNU Lesser General Public
    -# License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -
    -use strict;
    -
    -use Glib::Object::Introspection;
    -
    -Glib::Object::Introspection->setup(basename => "GPlugin", version => "0.0", package=> "GPlugin");
    -
    -sub gplugin_plugin_query() {
    - return GPlugin::PluginInfo->new(
    - id => "gplugin-perl/basic-plugin",
    - abi_version => 0x01000001,
    - name => "basic plugin",
    - authors => ("Gary Kramlich <grim\@reaperworld.com>"),
    - category => "test",
    - version => "version",
    - license_id => "license id",
    - summary => "summary",
    - website => "website",
    - description => "description",
    - );
    -}
    -
    -sub gplugin_plugin_load() {
    - my $plugin = shift;
    -
    - return 0;
    -}
    -
    -sub gplugin_plugin_unload() {
    - my $plugin = shift;
    -
    - return 0;
    -}
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/gplugin-perl5-core.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,73 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +#include "gplugin-perl5-loader.h"
    +#include "gplugin-perl5-plugin.h"
    +
    +#undef _
    +#include <glib/gi18n-lib.h>
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(GError **error)
    +{
    + const gchar *const authors[] = {
    + "Gary Kramlich <grim@reaperworld.com>",
    + NULL,
    + };
    +
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/perl-loader",
    + GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    + "internal", TRUE,
    + "load-on-query", TRUE,
    + "name", "Perl plugin loader",
    + "version", GPLUGIN_VERSION,
    + "license-id", "LGPL-2.0-or-later",
    + "summary", "A plugin that can load perl plugins",
    + "description", "This plugin allows the loading of plugins written in "
    + "the perl programming language.",
    + "authors", authors,
    + "website", GPLUGIN_WEBSITE,
    + "category", "loaders",
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(GPluginNativePlugin *plugin, GError **error)
    +{
    + gplugin_perl_plugin_register(plugin);
    + gplugin_perl_loader_register(plugin);
    +
    + return gplugin_manager_register_loader(GPLUGIN_PERL_TYPE_LOADER, error);
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(GPluginNativePlugin *plugin, GError **error)
    +{
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("The Perl loader can not be unloaded"));
    +
    + return FALSE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/gplugin-perl5-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,332 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include "gplugin-perl5-loader.h"
    +
    +#include "gplugin-perl5-plugin.h"
    +
    +#include <gperl.h>
    +
    +struct _GPluginPerlLoader {
    + GPluginLoader parent;
    +};
    +
    +G_DEFINE_DYNAMIC_TYPE(
    + GPluginPerlLoader,
    + gplugin_perl_loader,
    + GPLUGIN_TYPE_LOADER);
    +
    +static PerlInterpreter *my_perl = NULL;
    +
    +/******************************************************************************
    + * Perl Stuff
    + *****************************************************************************/
    +extern void boot_DynaLoader(pTHX_ CV *cv);
    +
    +static void gplugin_perl_loader_xs_init(pTHX)
    +{
    + dXSUB_SYS;
    +
    + newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
    +}
    +
    +static void
    +gplugin_perl_loader_init_perl(void)
    +{
    + gchar *args[] = {
    + "",
    + };
    + gchar **argv = (gchar **)args;
    + gint argc = 1;
    +
    + PERL_SYS_INIT(&argc, &argv);
    +
    + my_perl = perl_alloc();
    + PERL_SET_CONTEXT(my_perl);
    + PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
    + perl_construct(my_perl);
    +}
    +
    +static void
    +gplugin_perl_loader_uninit_perl(void)
    +{
    + PERL_SET_CONTEXT(my_perl);
    + perl_destruct(my_perl);
    + perl_free(my_perl);
    + PERL_SYS_TERM();
    +}
    +
    +static GPluginPluginInfo *
    +gplugin_perl_loader_call_gplugin_query(
    + PerlInterpreter *interpreter,
    + GError **error)
    +{
    + GPluginPluginInfo *info = NULL;
    + PerlInterpreter *old = NULL;
    + gint ret = 0;
    +
    + dSP;
    +
    + old = my_perl;
    + my_perl = interpreter;
    + PERL_SET_CONTEXT(interpreter);
    +
    + ENTER;
    + SAVETMPS;
    +
    + PUSHMARK(SP);
    + PUTBACK;
    +
    + ret = call_pv("gplugin_query", G_EVAL | G_NOARGS);
    +
    + SPAGAIN;
    +
    + if(ret != 1) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "gplugin_query did not return a GPluginPluginInfo");
    + } else {
    + if(SvTRUE(ERRSV)) {
    + const gchar *errmsg = SvPVutf8_nolen(ERRSV);
    +
    + g_set_error_literal(error, GPLUGIN_DOMAIN, 0, errmsg);
    + } else {
    + info = (GPluginPluginInfo *)gperl_get_object(POPs);
    +
    + /* if we did get a real GPluginPluginInfo ref it because the perl
    + * code below will take it out of scope and delete it if its
    + * reference count is zero.
    + */
    + if(GPLUGIN_IS_PLUGIN_INFO(info)) {
    + g_object_ref(G_OBJECT(info));
    + }
    + }
    + }
    +
    + PUTBACK;
    + FREETMPS;
    + LEAVE;
    +
    + my_perl = old;
    +
    + return info;
    +}
    +
    +static gboolean
    +gplugin_perl_loader_call_boolean(
    + PerlInterpreter *interpreter,
    + const gchar *func,
    + GError **error)
    +{
    + PerlInterpreter *old = NULL;
    + gboolean r = FALSE;
    + gint count = 0;
    +
    + dSP;
    +
    + old = my_perl;
    + my_perl = interpreter;
    + PERL_SET_CONTEXT(interpreter);
    +
    + ENTER;
    + SAVETMPS;
    + PUSHMARK(SP);
    + PUTBACK;
    +
    + count = call_pv(func, G_EVAL | G_SCALAR);
    +
    + SPAGAIN;
    +
    + if(count != 1) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "%s did not return a value",
    + func);
    + } else {
    + if(SvTRUE(ERRSV)) {
    + const gchar *errmsg = SvPVutf8_nolen(ERRSV);
    +
    + g_set_error_literal(error, GPLUGIN_DOMAIN, 0, errmsg);
    + } else {
    + if(POPi == 0) {
    + r = TRUE;
    + }
    + }
    + }
    +
    + PUTBACK;
    + FREETMPS;
    + LEAVE;
    +
    + my_perl = old;
    +
    + return r;
    +}
    +
    +/******************************************************************************
    + * GPluginLoaderInterface API
    + *****************************************************************************/
    +static GSList *
    +gplugin_perl_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *l)
    +{
    + return g_slist_append(NULL, "pl");
    +}
    +
    +static GPluginPlugin *
    +gplugin_perl_loader_query(
    + GPluginLoader *loader,
    + const gchar *filename,
    + GError **error)
    +{
    + GPluginPlugin *plugin = NULL;
    + GPluginPluginInfo *info = NULL;
    + PerlInterpreter *interpreter = NULL;
    + const gchar *args[] = {"", filename};
    + gchar **argv = (gchar **)args;
    + gint argc = 2, ret = 0;
    +
    + interpreter = perl_alloc();
    + PERL_SET_CONTEXT(interpreter);
    + PL_perl_destruct_level = 1;
    + perl_construct(interpreter);
    +
    + ret =
    + perl_parse(interpreter, gplugin_perl_loader_xs_init, argc, argv, NULL);
    + if(ret != 0) {
    + const gchar *errmsg = "unknown error";
    +
    + if(SvTRUE(ERRSV)) {
    + errmsg = SvPVutf8_nolen(ERRSV);
    + }
    +
    + g_set_error_literal(error, GPLUGIN_DOMAIN, 0, errmsg);
    +
    + perl_destruct(interpreter);
    + perl_free(interpreter);
    +
    + return NULL;
    + }
    +
    + ret = perl_run(interpreter);
    + if(ret != 0) {
    + const gchar *errmsg = "unknown error";
    +
    + if(SvTRUE(ERRSV)) {
    + errmsg = SvPVutf8_nolen(ERRSV);
    + }
    +
    + g_set_error_literal(error, GPLUGIN_DOMAIN, 0, errmsg);
    +
    + perl_destruct(interpreter);
    + perl_free(interpreter);
    +
    + return NULL;
    + }
    +
    + info = gplugin_perl_loader_call_gplugin_query(interpreter, error);
    + if(!GPLUGIN_IS_PLUGIN_INFO(info)) {
    + g_set_error_literal(error, GPLUGIN_DOMAIN, 0, "failed to query");
    +
    + return NULL;
    + }
    +
    + /* clang-format off */
    + plugin = g_object_new(
    + GPLUGIN_PERL_TYPE_PLUGIN,
    + "interpreter", interpreter,
    + "filename", filename,
    + "info", info,
    + "loader", g_object_ref(loader),
    + NULL);
    + /* clang-format on */
    +
    + return plugin;
    +}
    +
    +static gboolean
    +gplugin_perl_loader_load(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    +{
    + GPluginPerlPlugin *pplugin = GPLUGIN_PERL_PLUGIN(plugin);
    + PerlInterpreter *interpreter = NULL;
    +
    + interpreter = gplugin_perl_plugin_get_interpreter(pplugin);
    +
    + return gplugin_perl_loader_call_boolean(interpreter, "gplugin_load", error);
    +}
    +
    +static gboolean
    +gplugin_perl_loader_unload(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    +{
    + GPluginPerlPlugin *pplugin = GPLUGIN_PERL_PLUGIN(plugin);
    + PerlInterpreter *interpreter = NULL;
    +
    + interpreter = gplugin_perl_plugin_get_interpreter(pplugin);
    +
    + return gplugin_perl_loader_call_boolean(
    + interpreter,
    + "gplugin_unload",
    + error);
    +}
    +
    +/******************************************************************************
    + * GObject Stuff
    + *****************************************************************************/
    +static void
    +gplugin_perl_loader_init(G_GNUC_UNUSED GPluginPerlLoader *loader)
    +{
    +}
    +
    +static void
    +gplugin_perl_loader_class_init(GPluginPerlLoaderClass *klass)
    +{
    + GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
    +
    + loader_class->supported_extensions =
    + gplugin_perl_loader_supported_extensions;
    + loader_class->query = gplugin_perl_loader_query;
    + loader_class->load = gplugin_perl_loader_load;
    + loader_class->unload = gplugin_perl_loader_unload;
    +
    + /* perl initialization */
    + gplugin_perl_loader_init_perl();
    +}
    +
    +static void
    +gplugin_perl_loader_class_finalize(G_GNUC_UNUSED GPluginPerlLoaderClass *klass)
    +{
    + /* perl uninitialization */
    + gplugin_perl_loader_uninit_perl();
    +}
    +
    +/******************************************************************************
    + * API
    + *****************************************************************************/
    +void
    +gplugin_perl_loader_register(GPluginNativePlugin *plugin)
    +{
    + gplugin_perl_loader_register_type(G_TYPE_MODULE(plugin));
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/gplugin-perl5-loader.h Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,38 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#ifndef GPLUGIN_PERL_LOADER_H
    +#define GPLUGIN_PERL_LOADER_H
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_BEGIN_DECLS
    +
    +#define GPLUGIN_PERL_TYPE_LOADER (gplugin_perl_loader_get_type())
    +G_DECLARE_FINAL_TYPE(
    + GPluginPerlLoader,
    + gplugin_perl_loader,
    + GPLUGIN_PERL,
    + LOADER,
    + GPluginLoader)
    +
    +void gplugin_perl_loader_register(GPluginNativePlugin *plugin);
    +
    +G_END_DECLS
    +
    +#endif /* GPLUGIN_PERL_PLUGIN_LOADER_H */
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/gplugin-perl5-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,217 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include "gplugin-perl5-plugin.h"
    +
    +/******************************************************************************
    + * Typedefs
    + *****************************************************************************/
    +struct _GPluginPerlPlugin {
    + GObject parent;
    +
    + PerlInterpreter *interpreter;
    +
    + /* overrides */
    + gchar *filename;
    + GPluginLoader *loader;
    + GPluginPluginInfo *info;
    + GPluginPluginState state;
    + GError *error;
    +};
    +
    +/******************************************************************************
    + * Enums
    + *****************************************************************************/
    +enum {
    + PROP_ZERO,
    + PROP_INTERPRETER,
    + N_PROPERTIES,
    + /* overrides */
    + PROP_FILENAME = N_PROPERTIES,
    + PROP_LOADER,
    + PROP_INFO,
    + PROP_STATE,
    + PROP_ERROR,
    +};
    +static GParamSpec *properties[N_PROPERTIES] = {
    + NULL,
    +};
    +
    +/* I hate forward declarations... */
    +static void gplugin_perl_plugin_iface_init(GPluginPluginInterface *iface);
    +
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + GPluginPerlPlugin,
    + gplugin_perl_plugin,
    + G_TYPE_OBJECT,
    + 0,
    + G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_PLUGIN, gplugin_perl_plugin_iface_init));
    +
    +/******************************************************************************
    + * GPluginPlugin Implementation
    + *****************************************************************************/
    +static void
    +gplugin_perl_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface)
    +{
    +}
    +
    +/******************************************************************************
    + * Object Stuff
    + *****************************************************************************/
    +static void
    +gplugin_perl_plugin_get_property(
    + GObject *obj,
    + guint param_id,
    + GValue *value,
    + GParamSpec *pspec)
    +{
    + GPluginPerlPlugin *plugin = GPLUGIN_PERL_PLUGIN(obj);
    +
    + switch(param_id) {
    + case PROP_INTERPRETER:
    + g_value_set_pointer(
    + value,
    + gplugin_perl_plugin_get_interpreter(plugin));
    + break;
    +
    + /* overrides */
    + case PROP_FILENAME:
    + g_value_set_string(value, plugin->filename);
    + break;
    + case PROP_LOADER:
    + g_value_set_object(value, plugin->loader);
    + break;
    + case PROP_INFO:
    + g_value_set_object(value, plugin->info);
    + break;
    + case PROP_STATE:
    + g_value_set_enum(value, plugin->state);
    + break;
    + case PROP_ERROR:
    + g_value_set_boxed(value, plugin->error);
    + break;
    +
    + default:
    + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    + break;
    + }
    +}
    +
    +static void
    +gplugin_perl_plugin_set_property(
    + GObject *obj,
    + guint param_id,
    + const GValue *value,
    + GParamSpec *pspec)
    +{
    + GPluginPerlPlugin *plugin = GPLUGIN_PERL_PLUGIN(obj);
    +
    + switch(param_id) {
    + case PROP_INTERPRETER:
    + plugin->interpreter = g_value_get_pointer(value);
    + break;
    +
    + /* overrides */
    + case PROP_FILENAME:
    + plugin->filename = g_value_dup_string(value);
    + break;
    + case PROP_LOADER:
    + plugin->loader = g_value_dup_object(value);
    + break;
    + case PROP_INFO:
    + plugin->info = g_value_dup_object(value);
    + break;
    + case PROP_STATE:
    + plugin->state = g_value_get_enum(value);
    + break;
    + case PROP_ERROR:
    + plugin->error = g_value_dup_boxed(value);
    + break;
    +
    + default:
    + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    + }
    +}
    +
    +static void
    +gplugin_perl_plugin_finalize(GObject *obj)
    +{
    + GPluginPerlPlugin *plugin = GPLUGIN_PERL_PLUGIN(obj);
    +
    + perl_destruct(plugin->interpreter);
    + perl_free(plugin->interpreter);
    + plugin->interpreter = NULL;
    +
    + g_clear_pointer(&plugin->filename, g_free);
    + g_clear_object(&plugin->loader);
    + g_clear_object(&plugin->info);
    + g_clear_error(&plugin->error);
    +
    + G_OBJECT_CLASS(gplugin_perl_plugin_parent_class)->finalize(obj);
    +}
    +
    +static void
    +gplugin_perl_plugin_init(G_GNUC_UNUSED GPluginPerlPlugin *plugin)
    +{
    +}
    +
    +static void
    +gplugin_perl_plugin_class_finalize(G_GNUC_UNUSED GPluginPerlPluginClass *klass)
    +{
    +}
    +
    +static void
    +gplugin_perl_plugin_class_init(GPluginPerlPluginClass *klass)
    +{
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    +
    + obj_class->get_property = gplugin_perl_plugin_get_property;
    + obj_class->set_property = gplugin_perl_plugin_set_property;
    + obj_class->finalize = gplugin_perl_plugin_finalize;
    +
    + properties[PROP_INTERPRETER] = g_param_spec_pointer(
    + "interpreter",
    + "interpreter",
    + "The PERL interpreter for this plugin",
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
    +
    + g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    +
    + /* add our overrides */
    + g_object_class_override_property(obj_class, PROP_FILENAME, "filename");
    + g_object_class_override_property(obj_class, PROP_LOADER, "loader");
    + g_object_class_override_property(obj_class, PROP_INFO, "info");
    + g_object_class_override_property(obj_class, PROP_STATE, "state");
    + g_object_class_override_property(obj_class, PROP_ERROR, "error");
    +}
    +
    +/******************************************************************************
    + * API
    + *****************************************************************************/
    +void
    +gplugin_perl_plugin_register(GPluginNativePlugin *native)
    +{
    + gplugin_perl_plugin_register_type(G_TYPE_MODULE(native));
    +}
    +
    +PerlInterpreter *
    +gplugin_perl_plugin_get_interpreter(GPluginPerlPlugin *plugin)
    +{
    + g_return_val_if_fail(GPLUGIN_PERL_IS_PLUGIN(plugin), NULL);
    +
    + return plugin->interpreter;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/gplugin-perl5-plugin.h Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,57 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#ifndef GPLUGIN_PERL_PLUGIN_H
    +#define GPLUGIN_PERL_PLUGIN_H
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +#define PERL_NO_GET_CONTEXT
    +#ifdef __FreeBSD__
    +/* On FreeBSD, perl.h includes libutil.h (which doesn't appear necessary), and
    + * that defines some generic 'properties' type. So use the preprocessor to hide
    + * that name. */
    +#define properties freebsd_properties
    +#endif
    +#include <EXTERN.h>
    +#include <perl.h>
    +/* perl define's _() to something completely different that we don't use. So
    + * we undef it so that we can use it for gettext.
    + */
    +#undef _
    +#ifdef __FreeBSD__
    +#undef properties
    +#endif
    +
    +G_BEGIN_DECLS
    +
    +#define GPLUGIN_PERL_TYPE_PLUGIN (gplugin_perl_plugin_get_type())
    +G_DECLARE_FINAL_TYPE(
    + GPluginPerlPlugin,
    + gplugin_perl_plugin,
    + GPLUGIN_PERL,
    + PLUGIN,
    + GObject)
    +
    +void gplugin_perl_plugin_register(GPluginNativePlugin *native);
    +
    +PerlInterpreter *gplugin_perl_plugin_get_interpreter(GPluginPerlPlugin *plugin);
    +
    +G_END_DECLS
    +
    +#endif /* GPLUGIN_PERL_PLUGIN_H */
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,66 @@
    +if get_option('perl5')
    + if not get_option('introspection')
    + error('Perl plugin requires GObject Introspection.')
    + endif
    +
    + GPLUGIN_PERL_SOURCES = [
    + 'gplugin-perl5-core.c',
    + 'gplugin-perl5-loader.c',
    + 'gplugin-perl5-plugin.c',
    + ]
    +
    + GPLUGIN_PERL_HEADERS = [
    + 'gplugin-perl5-loader.h',
    + 'gplugin-perl5-plugin.h',
    + ]
    +
    + # make sure we have the perl executable, we need it to figure out the
    + # build arguments.
    + PERL = find_program('perl')
    +
    + # make sure we have the gobject introspection perl module.
    + run_command(
    + PERL,
    + '-e use Glib::Object::Introspection;',
    + check : true,
    + )
    +
    + perl_dep = declare_dependency(
    + compile_args : run_command(PERL, '-MExtUtils::Embed', '-e', 'ccopts', check : true).stdout().split(),
    + link_args : run_command(PERL, '-MExtUtils::Embed', '-e', 'ldopts', check : true).stdout().split(),
    + )
    +
    + if host_machine.system() == 'freebsd'
    + perl_arch = 'sitearchexp'
    + else
    + perl_arch = 'vendorarchexp'
    + endif
    + perl_arch = run_command(PERL,
    + '-MConfig',
    + '-e', 'if ($Config{@0@}) { print "$Config{@0@}" } else { exit 1 }'.format(perl_arch),
    + check : true).stdout().split()
    + glib_perl_incdir = '-I@0@/Glib/Install'.format(perl_arch[0])
    + glib_perl_libdir = '@0@/auto/Glib/'.format(perl_arch[0])
    + glib_perl_dep = compiler.find_library(
    + 'Glib',
    + dirs : glib_perl_libdir,
    + has_headers : 'gperl.h',
    + header_args : glib_perl_incdir
    + )
    + glib_perl_dep = declare_dependency(
    + dependencies : glib_perl_dep,
    + # Annoyingly, this is not transferred over from the header_args.
    + compile_args : glib_perl_incdir
    + )
    +
    + shared_library('gplugin-perl5',
    + GPLUGIN_PERL_SOURCES,
    + GPLUGIN_PERL_HEADERS,
    + name_prefix : '',
    + dependencies : [GMODULE, gplugin_dep, perl_dep, glib_perl_dep],
    + install : true,
    + install_dir : get_option('libdir') / 'gplugin'
    + )
    +endif
    +
    +subdir('tests')
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/tests/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,12 @@
    +if get_option('perl5')
    +
    +e = executable('test-perl5-loader', 'test-perl5-loader.c',
    + c_args : [
    + '-DPERL5_LOADER_DIR="@0@/.."'.format(meson.current_build_dir()),
    + '-DPERL5_PLUGIN_DIR="@0@/plugins"'.format(meson.current_source_dir()),
    + ],
    + link_with : gplugin_loader_tests,
    + dependencies : [GLIB, GOBJECT, gplugin_dep, perl_dep])
    +test('Perl5 Loader', e)
    +
    +endif # perl5
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/tests/plugins/basic.pl Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,43 @@
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +#
    +# This library is free software; you can redistribute it and/or
    +# modify it under the terms of the GNU Lesser General Public
    +# License as published by the Free Software Foundation; either
    +# version 2 of the License, or (at your option) any later version.
    +#
    +# This library is distributed in the hope that it will be useful,
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    +# Lesser General Public License for more details.
    +#
    +# You should have received a copy of the GNU Lesser General Public
    +# License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +
    +use strict;
    +
    +use Glib::Object::Introspection;
    +
    +Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package=> "GPlugin");
    +
    +sub gplugin_query {
    + return GPlugin::PluginInfo->new(
    + id => "gplugin/perl5-basic-plugin",
    + abi_version => 0x01020304,
    + name => "basic plugin",
    + authors => ("author1"),
    + category => "test",
    + version => "version",
    + license_id => "license",
    + summary => "summary",
    + website => "website",
    + description => "description",
    + );
    +}
    +
    +sub gplugin_load {
    + return 0;
    +}
    +
    +sub gplugin_unload {
    + return 0;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/tests/plugins/dependent.pl Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,35 @@
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +#
    +# This library is free software; you can redistribute it and/or
    +# modify it under the terms of the GNU Lesser General Public
    +# License as published by the Free Software Foundation; either
    +# version 2 of the License, or (at your option) any later version.
    +#
    +# This library is distributed in the hope that it will be useful,
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    +# Lesser General Public License for more details.
    +#
    +# You should have received a copy of the GNU Lesser General Public
    +# License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +
    +use strict;
    +
    +use Glib::Object::Introspection;
    +
    +Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package=> "GPlugin");
    +
    +sub gplugin_query() {
    + return GPlugin::PluginInfo->new(
    + id => "gplugin/perl5-dependent-plugin",
    + dependencies => ['dependency1', 'dependency2'],
    + );
    +}
    +
    +sub gplugin_load() {
    + return 1;
    +}
    +
    +sub gplugin_unload() {
    + return 1;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/tests/plugins/load-exception.pl Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,36 @@
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +#
    +# This library is free software; you can redistribute it and/or
    +# modify it under the terms of the GNU Lesser General Public
    +# License as published by the Free Software Foundation; either
    +# version 2 of the License, or (at your option) any later version.
    +#
    +# This library is distributed in the hope that it will be useful,
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    +# Lesser General Public License for more details.
    +#
    +# You should have received a copy of the GNU Lesser General Public
    +# License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +
    +use strict;
    +
    +use Glib::Object::Introspection;
    +
    +Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package=> "GPlugin");
    +
    +sub gplugin_query() {
    + return GPlugin::PluginInfo->new(
    + id => "gplugin/perl5-load-exception",
    + );
    +}
    +
    +sub gplugin_load() {
    + die("you be dead");
    +
    + return 0;
    +}
    +
    +sub gplugin_unload() {
    + return 0;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/tests/plugins/load-failed.pl Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,34 @@
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +#
    +# This library is free software; you can redistribute it and/or
    +# modify it under the terms of the GNU Lesser General Public
    +# License as published by the Free Software Foundation; either
    +# version 2 of the License, or (at your option) any later version.
    +#
    +# This library is distributed in the hope that it will be useful,
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    +# Lesser General Public License for more details.
    +#
    +# You should have received a copy of the GNU Lesser General Public
    +# License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +
    +use strict;
    +
    +use Glib::Object::Introspection;
    +
    +Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package=> "GPlugin");
    +
    +sub gplugin_query {
    + return GPlugin::PluginInfo->new(
    + id => "gplugin/perl5-load-failed",
    + );
    +}
    +
    +sub gplugin_load {
    + return 1;
    +}
    +
    +sub gplugin_unload {
    + return 0;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/tests/plugins/unload-failed.pl Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,34 @@
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    +#
    +# This library is free software; you can redistribute it and/or
    +# modify it under the terms of the GNU Lesser General Public
    +# License as published by the Free Software Foundation; either
    +# version 2 of the License, or (at your option) any later version.
    +#
    +# This library is distributed in the hope that it will be useful,
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    +# Lesser General Public License for more details.
    +#
    +# You should have received a copy of the GNU Lesser General Public
    +# License along with this library; if not, see <http://www.gnu.org/licenses/>.
    +
    +use strict;
    +
    +use Glib::Object::Introspection;
    +
    +Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package=> "GPlugin");
    +
    +sub gplugin_query() {
    + return GPlugin::PluginInfo->new(
    + id => "gplugin/perl5-unload-failed",
    + );
    +}
    +
    +sub gplugin_load() {
    + return 0;
    +}
    +
    +sub gplugin_unload() {
    + return 1;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/tests/test-perl5-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,31 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <glib.h>
    +
    +#include <gplugin.h>
    +#include <gplugin/gplugin-loader-tests.h>
    +
    +gint
    +main(gint argc, gchar **argv)
    +{
    + g_test_init(&argc, &argv, NULL);
    +
    + gplugin_loader_tests_main(PERL5_LOADER_DIR, PERL5_PLUGIN_DIR, "perl5");
    +
    + return g_test_run();
    +}
    --- a/plugins/gplugin-license-check.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,53 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Gary Kramlich <grim@reaperworld.com>",
    - NULL
    - };
    -
    - return gplugin_plugin_info_new(
    - "gplugin/license-check",
    - GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - "name", "License Check",
    - "version", GPLUGIN_VERSION,
    - "summary", "Checks the license compatibility of plugins",
    - "license-id", "GPL3",
    - "authors", authors,
    - "website", GPLUGIN_WEBSITE,
    - NULL);
    -}
    -
    -G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    -{
    - return TRUE;
    -}
    -
    -G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    -{
    - return TRUE;
    -}
    -
    --- a/plugins/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,6 +0,0 @@
    -shared_library('gplugin-license-check',
    - 'gplugin-license-check.c',
    - name_prefix : '',
    - dependencies : [gplugin_dep],
    - install : true,
    - install_dir : join_paths(get_option('libdir'), 'gplugin'))
    --- a/po/POTFILES Thu Nov 07 23:09:55 2019 -0600
    +++ b/po/POTFILES Sat Sep 05 20:05:36 2020 -0500
    @@ -5,6 +5,7 @@
    gplugin-gtk/gplugin-gtk-plugin-info.c
    gplugin-gtk/gplugin-gtk-store.c
    gplugin-gtk/gplugin-gtk-view.c
    +gplugin-query/gplugin-query.c
    gplugin/gplugin-core.c
    gplugin/gplugin-file-tree.c
    gplugin/gplugin-loader.c
    @@ -15,22 +16,19 @@
    gplugin/gplugin-plugin-info.c
    gplugin/gplugin-plugin.c
    gplugin/gplugin-private.c
    -gplugin/gplugin-query.c
    gplugin/gplugin-version.c
    lua/gplugin-lua-core.c
    lua/gplugin-lua-loader.c
    lua/gplugin-lua-plugin.c
    lua/gplugin-lua-test-lgi.c
    -perl/gplugin-perl-core.c
    -perl/gplugin-perl-loader.c
    -perl/gplugin-perl-plugin.c
    -plugins/gplugin-license-check.c
    -python/gplugin-python-core.c
    -python/gplugin-python-loader.c
    -python/gplugin-python-plugin.c
    -python/gplugin-python-test-pygobject.c
    -python/gplugin-python-utils.c
    -ruby/gplugin-ruby-protect.c
    +perl5/gplugin-perl5-core.c
    +perl5/gplugin-perl5-loader.c
    +perl5/gplugin-perl5-plugin.c
    +python/gplugin-python3-core.c
    +python/gplugin-python3-loader.c
    +python/gplugin-python3-plugin.c
    +python/gplugin-python3-test-pygobject.c
    +python/gplugin-python3-utils.c
    tcc/gplugin-tcc-core.c
    tcc/gplugin-tcc-loader.c
    tcc/gplugin-tcc-plugin.c
    --- a/python/gplugin-python-core.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,76 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include "gplugin-python-loader.h"
    -#include "gplugin-python-plugin.h"
    -
    -G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Gary Kramlich <grim@reaperworld.com>",
    - NULL
    - };
    -
    - return gplugin_plugin_info_new(
    - "gplugin/python-loader",
    - GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - "internal", TRUE,
    - "load-on-query", TRUE,
    - "name", "Python Plugin Loader",
    - "version", GPLUGIN_VERSION,
    - "license-id", "GPL3",
    - "summary", "A plugin that can load python plugins",
    - "description", "This plugin allows the loading of plugins written in "
    - "the python programming language.",
    - "authors", authors,
    - "website", GPLUGIN_WEBSITE,
    - "category", "loaders",
    - NULL
    - );
    -}
    -
    -G_MODULE_EXPORT gboolean
    -gplugin_load(GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    -{
    - gplugin_python_plugin_register(plugin);
    - gplugin_python_loader_register(plugin);
    -
    - gplugin_manager_register_loader(GPLUGIN_PYTHON_TYPE_LOADER);
    -
    - return TRUE;
    -}
    -
    -G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - GError **error)
    -{
    - g_set_error_literal(
    - error,
    - GPLUGIN_DOMAIN,
    - 0,
    - _("The Python loader can not be unloaded")
    - );
    -
    - return FALSE;
    -}
    -
    --- a/python/gplugin-python-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,394 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <Python.h>
    -
    -#include <stdlib.h>
    -
    -#include "gplugin-python-loader.h"
    -
    -#include "gplugin-python-plugin.h"
    -#include "gplugin-python-utils.h"
    -
    -#include <glib/gi18n.h>
    -
    -#include <pygobject.h>
    -
    -struct _GPluginPythonLoader {
    - GPluginLoader parent;
    -
    - PyThreadState *py_thread_state;
    - guint gc_id;
    -};
    -
    -G_DEFINE_DYNAMIC_TYPE(GPluginPythonLoader, gplugin_python_loader, GPLUGIN_TYPE_LOADER);
    -
    -/******************************************************************************
    - * GPluginLoader Implementation
    - *****************************************************************************/
    -static GSList *
    -gplugin_python_loader_class_supported_extensions(G_GNUC_UNUSED GPluginLoaderClass *klass) {
    - return g_slist_append(NULL, "py");
    -}
    -
    -static GPluginPlugin *
    -gplugin_python_loader_query(GPluginLoader *loader,
    - const gchar *filename,
    - G_GNUC_UNUSED GError **error)
    -{
    - GPluginPlugin *plugin = NULL;
    - GObject *info = NULL;
    - PyObject *pyinfo = NULL, *args = NULL;
    - PyObject *module = NULL, *package_list = NULL, *module_dict = NULL;
    - PyObject *query = NULL, *load = NULL, *unload = NULL;
    - PyGILState_STATE state;
    - gchar *module_name = NULL, *dir_name = NULL;
    -
    - /* lock the gil */
    - state = pyg_gil_state_ensure();
    -
    - /* create package_list as a tuple to handle 'import foo.bar' */
    - package_list = PyTuple_New(0);
    -
    - /* now figure out the module name from the filename */
    - module_name = gplugin_python_filename_to_module(filename);
    -
    - /* grab the dirname since we need it on sys.path to import the module */
    - dir_name = g_path_get_dirname(filename);
    - gplugin_python_add_module_path(dir_name);
    - g_free(dir_name);
    -
    - /* import the module */
    - module = PyImport_ImportModuleEx(module_name, NULL, NULL, package_list);
    - if(PyErr_Occurred()) {
    - g_warning(_("Failed to query %s"), filename);
    - PyErr_Print();
    -
    - /* clean some stuff up */
    - g_free(module_name);
    - Py_DECREF(package_list);
    -
    - pyg_gil_state_release(state);
    -
    - return NULL;
    - }
    -
    - /* clean some stuff up */
    - g_free(module_name);
    - Py_DECREF(package_list);
    -
    - /* at this point we have the module, lets find the query, load, and unload
    - * functions.
    - */
    - module_dict = PyModule_GetDict(module);
    -
    - query = PyDict_GetItemString(module_dict, "gplugin_query");
    - if(query == NULL) {
    - g_warning(_("Failed to find the gplugin_query function in %s"),
    - filename);
    -
    - Py_DECREF(module);
    - pyg_gil_state_release(state);
    -
    - return NULL;
    - }
    - if(!PyCallable_Check(query)) {
    - g_warning(_("Found gplugin_query in %s but it is not a "
    - "function"),
    - filename);
    -
    - Py_DECREF(module);
    - pyg_gil_state_release(state);
    -
    - return NULL;
    - }
    -
    - load = PyDict_GetItemString(module_dict, "gplugin_load");
    - if(load == NULL) {
    - g_warning(_("Failed to find the gplugin_load function in %s"),
    - filename);
    -
    - Py_DECREF(module);
    - pyg_gil_state_release(state);
    -
    - return NULL;
    - }
    - if(!PyCallable_Check(load)) {
    - g_warning(_("Found gplugin_load in %s but it is not a "
    - "function"),
    - filename);
    -
    - Py_DECREF(module);
    - pyg_gil_state_release(state);
    -
    - return NULL;
    - }
    -
    - unload = PyDict_GetItemString(module_dict, "gplugin_unload");
    - if(unload == NULL) {
    - g_warning(_("Failed to find the gplugin_unload function in %s"),
    - filename);
    -
    - Py_DECREF(module);
    - pyg_gil_state_release(state);
    -
    - return NULL;
    - }
    - if(!PyCallable_Check(unload)) {
    - g_warning(_("Found gplugin_unload in %s but it is not a "
    - "function"),
    - filename);
    -
    - Py_DECREF(module);
    - pyg_gil_state_release(state);
    -
    - return NULL;
    - }
    -
    - /* now that we have everything, call the query method and get the plugin's
    - * info.
    - */
    - args = PyTuple_New(0);
    - pyinfo = PyObject_Call(query, args, NULL);
    - Py_DECREF(args);
    -
    - info = pygobject_get(pyinfo);
    -
    - /* now that we have everything, create the plugin */
    - plugin = g_object_new(GPLUGIN_PYTHON_TYPE_PLUGIN,
    - "filename", filename,
    - "loader", loader,
    - "module", module,
    - "info", info,
    - "load-func", load,
    - "unload-func", unload,
    - NULL);
    -
    - Py_DECREF(pyinfo);
    - Py_DECREF(module);
    -
    - /* unlock the gil */
    - pyg_gil_state_release(state);
    -
    - return plugin;
    -}
    -
    -static gboolean
    -gplugin_python_loader_load(G_GNUC_UNUSED GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - GError **error)
    -{
    - PyObject *load = NULL, *pyplugin = NULL, *result = NULL;
    - gboolean ret = FALSE;
    -
    - g_object_get(G_OBJECT(plugin), "load-func", &load, NULL);
    -
    - pyplugin = pygobject_new(G_OBJECT(plugin));
    -
    - result = PyObject_CallFunctionObjArgs(load, pyplugin, NULL);
    - Py_DECREF(pyplugin);
    -
    - if (PyErr_Occurred()) {
    - Py_XDECREF(result);
    -
    - if (error) {
    - *error = gplugin_python_exception_to_gerror();
    - }
    -
    - return FALSE;
    - }
    -
    - ret = PyObject_IsTrue(result);
    - Py_DECREF(result);
    -
    - if(!ret) {
    - g_set_error_literal(error, GPLUGIN_DOMAIN, 0,
    - _("Failed to load plugin"));
    - }
    -
    - return ret;
    -}
    -
    -static gboolean
    -gplugin_python_loader_unload(G_GNUC_UNUSED GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - GError **error)
    -{
    - PyObject *unload = NULL, *pyplugin = NULL, *result = NULL;
    - gboolean ret = FALSE;
    -
    - g_object_get(G_OBJECT(plugin), "unload-func", &unload, NULL);
    -
    - pyplugin = pygobject_new(G_OBJECT(plugin));
    -
    - result = PyObject_CallFunctionObjArgs(unload, pyplugin, NULL);
    - Py_DECREF(pyplugin);
    -
    - if(PyErr_Occurred()) {
    - PyErr_Print();
    -
    - Py_XDECREF(result);
    -
    - return FALSE;
    - }
    -
    - ret = PyObject_IsTrue(result);
    - Py_DECREF(result);
    -
    - if(!ret) {
    - g_set_error_literal(error, GPLUGIN_DOMAIN, 0,
    - _("Failed to unload plugin"));
    - }
    -
    - return ret;
    -}
    -
    -/******************************************************************************
    - * Python Stuff
    - *****************************************************************************/
    -static gboolean
    -gplugin_python_loader_init_pygobject(void) {
    - pygobject_init(3, 0, 0);
    - if(PyErr_Occurred()) {
    - PyObject *type = NULL, *value = NULL, *tb = NULL, *obj = NULL;
    -
    - PyErr_Fetch(&type, &value, &tb);
    - Py_DECREF(type);
    - Py_XDECREF(tb);
    -
    - obj = PyUnicode_AsUTF8String(value);
    - Py_DECREF(value);
    -
    - g_warning("Failed to initialize PyGObject : %s", PyBytes_AsString(obj));
    - Py_DECREF(obj);
    -
    - return FALSE;
    - }
    -
    - /* enable threads */
    - pyg_enable_threads();
    -
    - /* disable g_log redirections */
    - pyg_disable_warning_redirections();
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -gplugin_python_loader_init_gettext(void) {
    - PyObject *module_dict = NULL, *install = NULL;
    - PyObject *gettext = NULL, *result = NULL;
    -
    - gettext = PyImport_ImportModule("gettext");
    - if(gettext == NULL) {
    - g_warning("Failed to import gettext");
    -
    - return FALSE;
    - }
    -
    - module_dict = PyModule_GetDict(gettext);
    - install = PyDict_GetItemString(module_dict, "install");
    - result = PyObject_CallFunction(install, "ss", GETTEXT_PACKAGE, LOCALEDIR);
    - Py_XDECREF(result);
    - Py_DECREF(gettext);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -gplugin_python_loader_init_python(void) {
    - const gchar *program = NULL;
    - wchar_t *argv[] = { NULL, NULL };
    - size_t len;
    -
    - /* Initializes Python */
    - if(!Py_IsInitialized())
    - Py_InitializeEx(FALSE);
    -
    - program = g_get_prgname();
    - program = program ? program : "";
    - len = mbstowcs(NULL, program, 0);
    - if(len == (size_t)-1) {
    - g_warning("Could not convert program name to wchar_t string.");
    - return FALSE;
    - }
    -
    - argv[0] = g_new(wchar_t, len + 1);
    - len = mbstowcs(argv[0], program, len + 1);
    - if(len == (size_t)-1) {
    - g_warning("Could not convert program name to wchar_t string.");
    -
    - g_free(argv[0]);
    -
    - return FALSE;
    - }
    -
    - /* setup sys.path according to
    - * https://docs.python.org/3/c-api/init.html#PySys_SetArgvEx
    - */
    -#if PY_VERSION_HEX < 0x03010300
    - PySys_SetArgv(1, argv);
    - PyRun_SimpleString("import sys; sys.path.pop(0)\n");
    - g_free(argv[0]);
    -#else
    - PySys_SetArgvEx(1, argv, 0);
    - g_free(argv[0]);
    -#endif
    -
    - /* initialize pygobject */
    - if(gplugin_python_loader_init_pygobject()) {
    - if(gplugin_python_loader_init_gettext()) {
    - return TRUE;
    - }
    - }
    -
    - return FALSE;
    -}
    -
    -/******************************************************************************
    - * Object Stuff
    - *****************************************************************************/
    -static void
    -gplugin_python_loader_init(G_GNUC_UNUSED GPluginPythonLoader *loader) {
    -}
    -
    -static void
    -gplugin_python_loader_class_finalize(G_GNUC_UNUSED GPluginPythonLoaderClass *klass)
    -{
    -}
    -
    -static void
    -gplugin_python_loader_class_init(GPluginPythonLoaderClass *klass) {
    - GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
    -
    - loader_class->supported_extensions =
    - gplugin_python_loader_class_supported_extensions;
    - loader_class->query = gplugin_python_loader_query;
    - loader_class->load = gplugin_python_loader_load;
    - loader_class->unload = gplugin_python_loader_unload;
    -}
    -
    -/******************************************************************************
    - * API
    - *****************************************************************************/
    -void
    -gplugin_python_loader_register(GPluginNativePlugin *native) {
    - gplugin_python_loader_register_type(G_TYPE_MODULE(native));
    -
    - gplugin_python_loader_init_python();
    -}
    -
    --- a/python/gplugin-python-loader.h Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#ifndef GPLUGIN_PYTHON_LOADER_H
    -#define GPLUGIN_PYTHON_LOADER_H
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -G_BEGIN_DECLS
    -
    -#define GPLUGIN_PYTHON_TYPE_LOADER (gplugin_python_loader_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginPythonLoader, gplugin_python_loader, GPLUGIN_PYTHON, LOADER, GPluginLoader)
    -
    -void gplugin_python_loader_register(GPluginNativePlugin *native);
    -
    -G_END_DECLS
    -
    -#endif /* GPLUGIN_PYTHON_LOADER_H */
    -
    --- a/python/gplugin-python-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,277 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <Python.h>
    -
    -#include "gplugin-python-plugin.h"
    -
    -#include <pygobject.h>
    -
    -/******************************************************************************
    - * Typedefs
    - *****************************************************************************/
    -struct _GPluginPythonPlugin {
    - GObject parent;
    -
    - PyObject *module;
    - PyObject *query;
    - PyObject *load;
    - PyObject *unload;
    -
    - /* overrides */
    - gchar *filename;
    - GPluginLoader *loader;
    - GPluginPluginInfo *info;
    - GPluginPluginState state;
    -};
    -
    -/******************************************************************************
    - * Enums
    - *****************************************************************************/
    -enum {
    - PROP_ZERO,
    - PROP_MODULE,
    - PROP_LOAD_FUNC,
    - PROP_UNLOAD_FUNC,
    - N_PROPERTIES,
    - /* overrides */
    - PROP_FILENAME = N_PROPERTIES,
    - PROP_LOADER,
    - PROP_INFO,
    - PROP_STATE
    -};
    -static GParamSpec *properties[N_PROPERTIES] = {NULL,};
    -
    -/* I hate forward declarations... */
    -static void gplugin_python_plugin_iface_init(GPluginPluginInterface *iface);
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    - GPluginPythonPlugin,
    - gplugin_python_plugin,
    - G_TYPE_OBJECT,
    - 0,
    - G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_PLUGIN, gplugin_python_plugin_iface_init)
    -);
    -
    -/******************************************************************************
    - * GPluginPlugin Implementation
    - *****************************************************************************/
    -static void
    -gplugin_python_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface) {
    -}
    -
    -/******************************************************************************
    - * Private Stuff
    - *****************************************************************************/
    -static void
    -gplugin_python_plugin_set_module(GPluginPythonPlugin *plugin,
    - PyObject *module)
    -{
    - g_return_if_fail(GPLUGIN_IS_PLUGIN(plugin));
    - g_return_if_fail(module);
    -
    - Py_XINCREF(module);
    - Py_CLEAR(plugin->module);
    - plugin->module = module;
    -}
    -
    -static gpointer
    -gplugin_python_plugin_get_load_func(GPluginPythonPlugin *plugin) {
    - g_return_val_if_fail(GPLUGIN_PYTHON_IS_PLUGIN(plugin), NULL);
    -
    - return plugin->load;
    -}
    -
    -static void
    -gplugin_python_plugin_set_load_func(GPluginPythonPlugin *plugin,
    - PyObject *func)
    -{
    - g_return_if_fail(GPLUGIN_PYTHON_IS_PLUGIN(plugin));
    - g_return_if_fail(func != NULL);
    -
    - Py_XINCREF(func);
    - Py_CLEAR(plugin->load);
    - plugin->load = func;
    -}
    -
    -static gpointer
    -gplugin_python_plugin_get_unload_func(GPluginPythonPlugin *plugin) {
    - g_return_val_if_fail(GPLUGIN_PYTHON_IS_PLUGIN(plugin), NULL);
    -
    - return plugin->unload;
    -}
    -
    -static void
    -gplugin_python_plugin_set_unload_func(GPluginPythonPlugin *plugin,
    - PyObject *func)
    -{
    - g_return_if_fail(GPLUGIN_PYTHON_IS_PLUGIN(plugin));
    - g_return_if_fail(func != NULL);
    -
    - Py_XINCREF(func);
    - Py_CLEAR(plugin->unload);
    - plugin->unload = func;
    -}
    -
    -/******************************************************************************
    - * Object Stuff
    - *****************************************************************************/
    -static void
    -gplugin_python_plugin_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *pspec)
    -{
    - GPluginPythonPlugin *plugin = GPLUGIN_PYTHON_PLUGIN(obj);
    -
    - switch(param_id) {
    - case PROP_MODULE:
    - g_value_set_pointer(value, plugin->module);
    - break;
    - case PROP_LOAD_FUNC:
    - g_value_set_pointer(value,
    - gplugin_python_plugin_get_load_func(plugin));
    - break;
    - case PROP_UNLOAD_FUNC:
    - g_value_set_pointer(value,
    - gplugin_python_plugin_get_unload_func(plugin));
    - break;
    -
    - /* overrides */
    - case PROP_FILENAME:
    - g_value_set_string(value, plugin->filename);
    - break;
    - case PROP_LOADER:
    - g_value_set_object(value, plugin->loader);
    - break;
    - case PROP_INFO:
    - g_value_set_object(value, plugin->info);
    - break;
    - case PROP_STATE:
    - g_value_set_enum(value, plugin->state);
    - break;
    -
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    - break;
    - }
    -}
    -
    -static void
    -gplugin_python_plugin_set_property(GObject *obj, guint param_id,
    - const GValue *value, GParamSpec *pspec)
    -{
    - GPluginPythonPlugin *plugin = GPLUGIN_PYTHON_PLUGIN(obj);
    -
    - switch(param_id) {
    - case PROP_MODULE:
    - gplugin_python_plugin_set_module(plugin,
    - g_value_get_pointer(value));
    - break;
    - case PROP_LOAD_FUNC:
    - gplugin_python_plugin_set_load_func(plugin,
    - g_value_get_pointer(value));
    - break;
    - case PROP_UNLOAD_FUNC:
    - gplugin_python_plugin_set_unload_func(plugin,
    - g_value_get_pointer(value));
    - break;
    -
    - /* overrides */
    - case PROP_FILENAME:
    - plugin->filename = g_value_dup_string(value);
    - break;
    - case PROP_LOADER:
    - plugin->loader = g_value_dup_object(value);
    - break;
    - case PROP_INFO:
    - plugin->info = g_value_dup_object(value);
    - break;
    - case PROP_STATE:
    - plugin->state = g_value_get_enum(value);
    - break;
    -
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    - break;
    - }
    -}
    -
    -static void
    -gplugin_python_plugin_finalize(GObject *obj) {
    - GPluginPythonPlugin *plugin = GPLUGIN_PYTHON_PLUGIN(obj);
    -
    - Py_CLEAR(plugin->module);
    - Py_CLEAR(plugin->load);
    - Py_CLEAR(plugin->unload);
    -
    - g_clear_pointer(&plugin->filename, g_free);
    - g_clear_object(&plugin->loader);
    - g_clear_object(&plugin->info);
    -
    - G_OBJECT_CLASS(gplugin_python_plugin_parent_class)->finalize(obj);
    -}
    -
    -static void
    -gplugin_python_plugin_init(G_GNUC_UNUSED GPluginPythonPlugin *plugin) {
    -}
    -
    -static void
    -gplugin_python_plugin_class_finalize(G_GNUC_UNUSED GPluginPythonPluginClass *klass)
    -{
    -}
    -
    -static void
    -gplugin_python_plugin_class_init(GPluginPythonPluginClass *klass) {
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    -
    - obj_class->get_property = gplugin_python_plugin_get_property;
    - obj_class->set_property = gplugin_python_plugin_set_property;
    - obj_class->finalize = gplugin_python_plugin_finalize;
    -
    - properties[PROP_MODULE] = g_param_spec_pointer(
    - "module", "module",
    - "The python module object",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    -
    - properties[PROP_LOAD_FUNC] = g_param_spec_pointer(
    - "load-func", "load-func",
    - "The python load function",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    -
    - properties[PROP_UNLOAD_FUNC] = g_param_spec_pointer(
    - "unload-func", "unload-func",
    - "The python unload function",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    -
    - g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    -
    - /* add our overrides */
    - g_object_class_override_property(obj_class, PROP_FILENAME, "filename");
    - g_object_class_override_property(obj_class, PROP_LOADER, "loader");
    - g_object_class_override_property(obj_class, PROP_INFO, "info");
    - g_object_class_override_property(obj_class, PROP_STATE, "state");
    -}
    -
    -/******************************************************************************
    - * API
    - *****************************************************************************/
    -void
    -gplugin_python_plugin_register(GPluginNativePlugin *native) {
    - gplugin_python_plugin_register_type(G_TYPE_MODULE(native));
    -}
    --- a/python/gplugin-python-plugin.h Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#ifndef GPLUGIN_PYTHON_PLUGIN_H
    -#define GPLUGIN_PYTHON_PLUGIN_H
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -G_BEGIN_DECLS
    -
    -#define GPLUGIN_PYTHON_TYPE_PLUGIN (gplugin_python_plugin_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginPythonPlugin, gplugin_python_plugin, GPLUGIN_PYTHON, PLUGIN, GObject)
    -
    -void gplugin_python_plugin_register(GPluginNativePlugin *native);
    -
    -G_END_DECLS
    -
    -#endif /* GPLUGIN_PYTHON_PLUGIN_H */
    -
    --- a/python/gplugin-python-test-pygobject.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,66 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <stdio.h>
    -
    -#include <glib.h>
    -
    -#include <Python.h>
    -
    -#include <pygobject.h>
    -
    -gint
    -main(gint argc, gchar *argv[]) {
    - wchar_t *wargv[] = { NULL, NULL };
    - size_t len;
    -
    - /* initialize python */
    - if(!Py_IsInitialized())
    - Py_InitializeEx(FALSE);
    -
    - /* setup wargv */
    - len = mbstowcs(NULL, argv[0], 0);
    - if(len == (size_t)-1)
    - return -1;
    -
    - wargv[0] = g_new0(wchar_t, len + 1);
    - len = mbstowcs(wargv[0], argv[0], len + 1);
    - if(len == (size_t)-1) {
    - g_free(wargv[0]);
    - return -1;
    - }
    -
    - /* setup sys.path */
    -#if PY_VERSION_HEX < 0x03010300
    - PySys_SetArgv(1, wargv);
    - PyRun_SimpleString("import sys; sys.path.pop(0)\n");
    -#else
    - PySys_SetArgvEx(1, wargv, 0);
    -#endif
    -
    - g_free(wargv[0]);
    -
    - /* initialize pygobject */
    - pygobject_init(3, 0, 0);
    - if(PyErr_Occurred()) {
    - PyErr_Print();
    - return -1;
    - }
    -
    - return 0;
    -}
    -
    --- a/python/gplugin-python-utils.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,112 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <Python.h>
    -
    -#include <gplugin/gplugin.h>
    -
    -#include "gplugin-python-utils.h"
    -
    -#include <string.h>
    -
    -gchar *
    -gplugin_python_filename_to_module(const gchar *filename) {
    - gchar *base = NULL;
    - gchar *e = NULL, *r = NULL;
    -
    - g_return_val_if_fail(filename != NULL, NULL);
    -
    - /* first make sure we just have a filename */
    - base = g_path_get_basename(filename);
    -
    - /* now find the last . for the extension */
    - e = g_utf8_strrchr(base, g_utf8_strlen(base, -1), g_utf8_get_char("."));
    - if(e == NULL) {
    - return base;
    - }
    -
    - /* now copy the module name into r */
    - r = g_malloc(e - base + 1);
    - memcpy(r, base, e - base);
    - r[e - base] = 0;
    -
    - /* free the basename */
    - g_free(base);
    -
    - return r;
    -}
    -
    -gboolean
    -gplugin_python_add_module_path(const gchar *module_path) {
    - PyObject *sys_path = NULL, *path = NULL;
    - gboolean ret = FALSE;
    -
    - sys_path = PySys_GetObject("path");
    -
    - path = PyUnicode_FromString(module_path);
    -
    - if(PySequence_Contains(sys_path, path) == 0) {
    - PyList_Insert(sys_path, 0, path);
    - ret = TRUE;
    - }
    -
    - Py_DECREF(path);
    -
    - return ret;
    -}
    -
    -GError *
    -gplugin_python_exception_to_gerror(void) {
    - GError *error = NULL;
    - PyObject *type = NULL, *value = NULL, *trace = NULL;
    - PyObject *type_name = NULL, *value_str = NULL, *obj = NULL;
    -
    - if(!PyErr_Occurred())
    - return NULL;
    -
    - PyErr_Fetch(&type, &value, &trace);
    - if(type == NULL)
    - return NULL;
    -
    - PyErr_NormalizeException(&type, &value, &trace);
    - Py_XDECREF(trace);
    -
    - type_name = PyObject_GetAttrString(type, "__name__");
    - Py_DECREF(type);
    -
    - value_str = PyObject_Str(value);
    - Py_DECREF(value);
    -
    - /* now decode the utf8 into a string we can use */
    - obj = PyUnicode_AsUTF8String(type_name);
    - Py_DECREF(type_name);
    - type_name = obj;
    -
    - obj = PyUnicode_AsUTF8String(value_str);
    - Py_DECREF(value_str);
    - value_str = obj;
    -
    - error = g_error_new(GPLUGIN_DOMAIN, 0, "%s: %s",
    - PyBytes_AsString(type_name),
    - PyBytes_AsString(value_str));
    -
    - Py_DECREF(type_name);
    - Py_DECREF(value_str);
    -
    - return error;
    -}
    -
    --- a/python/gplugin-python-utils.h Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,36 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#ifndef GPLUGIN_PYTHON_UTILS_H
    -#define GPLUGIN_PYTHON_UTILS_H
    -
    -#include <glib.h>
    -
    -#define GPLUGIN_PYTHON_DOMAIN (g_quark_from_static_string("gplugin-python"))
    -
    -G_BEGIN_DECLS
    -
    -gchar *gplugin_python_filename_to_module(const gchar *filename);
    -
    -gboolean gplugin_python_add_module_path(const gchar *module_path);
    -
    -GError *gplugin_python_exception_to_gerror(void);
    -
    -G_END_DECLS
    -
    -#endif /* GPLUGIN_PYTHON_UTILS_H */
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/gplugin-python3-core.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,74 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <glib/gi18n-lib.h>
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +#include "gplugin-python3-loader.h"
    +#include "gplugin-python3-plugin.h"
    +
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + /* clang-format-11 formats this correctly, so this can be removed then. */
    + /* clang-format off */
    + const gchar *const authors[] = {
    + "Gary Kramlich <grim@reaperworld.com>",
    + NULL};
    + /* clang-format on */
    +
    + /* clang-format off */
    + return gplugin_plugin_info_new(
    + "gplugin/python3-loader",
    + GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    + "internal", TRUE,
    + "load-on-query", TRUE,
    + "name", "Python Plugin Loader",
    + "version", GPLUGIN_VERSION,
    + "license-id", "LGPL-2.0-or-later",
    + "summary", "A plugin that can load python plugins",
    + "description", "This plugin allows the loading of plugins written in "
    + "the python programming language.",
    + "authors", authors,
    + "website", GPLUGIN_WEBSITE,
    + "category", "loaders",
    + NULL);
    + /* clang-format on */
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(GPluginNativePlugin *plugin, G_GNUC_UNUSED GError **error)
    +{
    + gplugin_python3_plugin_register(plugin);
    + gplugin_python3_loader_register(plugin);
    +
    + return gplugin_manager_register_loader(GPLUGIN_PYTHON3_TYPE_LOADER, error);
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +{
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("The Python loader can not be unloaded"));
    +
    + return FALSE;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/gplugin-python3-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,401 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include "gplugin-python3-loader.h"
    +
    +#include <stdlib.h>
    +
    +#include <glib/gi18n-lib.h>
    +
    +#include <Python.h>
    +#include <pygobject.h>
    +
    +#include "gplugin-python3-plugin.h"
    +#include "gplugin-python3-utils.h"
    +
    +struct _GPluginPython3Loader {
    + GPluginLoader parent;
    +
    + PyThreadState *py_thread_state;
    + guint gc_id;
    +};
    +
    +G_DEFINE_DYNAMIC_TYPE(
    + GPluginPython3Loader,
    + gplugin_python3_loader,
    + GPLUGIN_TYPE_LOADER);
    +
    +/******************************************************************************
    + * GPluginLoader Implementation
    + *****************************************************************************/
    +static GSList *
    +gplugin_python3_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *l)
    +{
    + return g_slist_append(NULL, "py");
    +}
    +
    +static GPluginPlugin *
    +gplugin_python3_loader_query(
    + GPluginLoader *loader,
    + const gchar *filename,
    + G_GNUC_UNUSED GError **error)
    +{
    + GPluginPlugin *plugin = NULL;
    + GObject *info = NULL;
    + PyObject *pyinfo = NULL, *args = NULL;
    + PyObject *module = NULL, *package_list = NULL, *module_dict = NULL;
    + PyObject *query = NULL, *load = NULL, *unload = NULL;
    + PyGILState_STATE state;
    + gchar *module_name = NULL, *dir_name = NULL;
    +
    + /* lock the gil */
    + state = pyg_gil_state_ensure();
    +
    + /* create package_list as a tuple to handle 'import foo.bar' */
    + package_list = PyTuple_New(0);
    +
    + /* now figure out the module name from the filename */
    + module_name = gplugin_python3_filename_to_module(filename);
    +
    + /* grab the dirname since we need it on sys.path to import the module */
    + dir_name = g_path_get_dirname(filename);
    + gplugin_python3_add_module_path(dir_name);
    + g_free(dir_name);
    +
    + /* import the module */
    + module = PyImport_ImportModuleEx(module_name, NULL, NULL, package_list);
    + if(PyErr_Occurred()) {
    + g_warning(_("Failed to query %s"), filename);
    + PyErr_Print();
    +
    + /* clean some stuff up */
    + g_free(module_name);
    + Py_DECREF(package_list);
    +
    + pyg_gil_state_release(state);
    +
    + return NULL;
    + }
    +
    + /* clean some stuff up */
    + g_free(module_name);
    + Py_DECREF(package_list);
    +
    + /* at this point we have the module, lets find the query, load, and unload
    + * functions.
    + */
    + module_dict = PyModule_GetDict(module);
    +
    + query = PyDict_GetItemString(module_dict, "gplugin_query");
    + if(query == NULL) {
    + g_warning(
    + _("Failed to find the gplugin_query function in %s"),
    + filename);
    +
    + Py_DECREF(module);
    + pyg_gil_state_release(state);
    +
    + return NULL;
    + }
    + if(!PyCallable_Check(query)) {
    + g_warning(
    + _("Found gplugin_query in %s but it is not a "
    + "function"),
    + filename);
    +
    + Py_DECREF(module);
    + pyg_gil_state_release(state);
    +
    + return NULL;
    + }
    +
    + load = PyDict_GetItemString(module_dict, "gplugin_load");
    + if(load == NULL) {
    + g_warning(
    + _("Failed to find the gplugin_load function in %s"),
    + filename);
    +
    + Py_DECREF(module);
    + pyg_gil_state_release(state);
    +
    + return NULL;
    + }
    + if(!PyCallable_Check(load)) {
    + g_warning(
    + _("Found gplugin_load in %s but it is not a "
    + "function"),
    + filename);
    +
    + Py_DECREF(module);
    + pyg_gil_state_release(state);
    +
    + return NULL;
    + }
    +
    + unload = PyDict_GetItemString(module_dict, "gplugin_unload");
    + if(unload == NULL) {
    + g_warning(
    + _("Failed to find the gplugin_unload function in %s"),
    + filename);
    +
    + Py_DECREF(module);
    + pyg_gil_state_release(state);
    +
    + return NULL;
    + }
    + if(!PyCallable_Check(unload)) {
    + g_warning(
    + _("Found gplugin_unload in %s but it is not a "
    + "function"),
    + filename);
    +
    + Py_DECREF(module);
    + pyg_gil_state_release(state);
    +
    + return NULL;
    + }
    +
    + /* now that we have everything, call the query method and get the plugin's
    + * info.
    + */
    + args = PyTuple_New(0);
    + pyinfo = PyObject_Call(query, args, NULL);
    + Py_DECREF(args);
    +
    + info = pygobject_get(pyinfo);
    +
    + /* now that we have everything, create the plugin */
    + /* clang-format off */
    + plugin = g_object_new(
    + GPLUGIN_PYTHON3_TYPE_PLUGIN,
    + "filename", filename,
    + "loader", loader,
    + "module", module,
    + "info", info,
    + "load-func", load,
    + "unload-func", unload,
    + NULL);
    + /* clang-format on */
    +
    + Py_DECREF(pyinfo);
    + Py_DECREF(module);
    +
    + /* unlock the gil */
    + pyg_gil_state_release(state);
    +
    + return plugin;
    +}
    +
    +static gboolean
    +gplugin_python3_loader_load(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    +{
    + PyObject *load = NULL, *pyplugin = NULL, *result = NULL;
    + gboolean ret = FALSE;
    +
    + g_object_get(G_OBJECT(plugin), "load-func", &load, NULL);
    +
    + pyplugin = pygobject_new(G_OBJECT(plugin));
    +
    + result = PyObject_CallFunctionObjArgs(load, pyplugin, NULL);
    + Py_DECREF(pyplugin);
    +
    + if(PyErr_Occurred()) {
    + Py_XDECREF(result);
    +
    + if(error) {
    + *error = gplugin_python3_exception_to_gerror();
    + }
    +
    + return FALSE;
    + }
    +
    + ret = PyObject_IsTrue(result);
    + Py_DECREF(result);
    +
    + if(!ret) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("Failed to load plugin"));
    + }
    +
    + return ret;
    +}
    +
    +static gboolean
    +gplugin_python3_loader_unload(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    +{
    + PyObject *unload = NULL, *pyplugin = NULL, *result = NULL;
    + gboolean ret = FALSE;
    +
    + g_object_get(G_OBJECT(plugin), "unload-func", &unload, NULL);
    +
    + pyplugin = pygobject_new(G_OBJECT(plugin));
    +
    + result = PyObject_CallFunctionObjArgs(unload, pyplugin, NULL);
    + Py_DECREF(pyplugin);
    +
    + if(PyErr_Occurred()) {
    + PyErr_Print();
    +
    + Py_XDECREF(result);
    +
    + return FALSE;
    + }
    +
    + ret = PyObject_IsTrue(result);
    + Py_DECREF(result);
    +
    + if(!ret) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("Failed to unload plugin"));
    + }
    +
    + return ret;
    +}
    +
    +/******************************************************************************
    + * Python3 Stuff
    + *****************************************************************************/
    +static gboolean
    +gplugin_python3_loader_init_pygobject(void)
    +{
    + pygobject_init(3, 0, 0);
    + if(PyErr_Occurred()) {
    + PyObject *type = NULL, *value = NULL, *tb = NULL, *obj = NULL;
    +
    + PyErr_Fetch(&type, &value, &tb);
    + Py_DECREF(type);
    + Py_XDECREF(tb);
    +
    + obj = PyUnicode_AsUTF8String(value);
    + Py_DECREF(value);
    +
    + g_warning("Failed to initialize PyGObject : %s", PyBytes_AsString(obj));
    + Py_DECREF(obj);
    +
    + return FALSE;
    + }
    +
    + /* enable threads */
    + pyg_enable_threads();
    +
    + /* disable g_log redirections */
    + pyg_disable_warning_redirections();
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +gplugin_python3_loader_init_gettext(void)
    +{
    + PyObject *module_dict = NULL, *install = NULL;
    + PyObject *gettext = NULL, *result = NULL;
    +
    + gettext = PyImport_ImportModule("gettext");
    + if(gettext == NULL) {
    + g_warning("Failed to import gettext");
    +
    + return FALSE;
    + }
    +
    + module_dict = PyModule_GetDict(gettext);
    + install = PyDict_GetItemString(module_dict, "install");
    + result = PyObject_CallFunction(install, "ss", GETTEXT_PACKAGE, LOCALEDIR);
    + Py_XDECREF(result);
    + Py_DECREF(gettext);
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +gplugin_python3_loader_init_python(void)
    +{
    + wchar_t *argv[] = {NULL, NULL};
    +
    + /* Initializes Python */
    + if(!Py_IsInitialized())
    + Py_InitializeEx(FALSE);
    +
    + argv[0] = Py_DecodeLocale(g_get_prgname(), NULL);
    + if(argv[0] == NULL) {
    + g_warning("Could not convert program name to wchar_t string.");
    + return FALSE;
    + }
    +
    + /* setup sys.path according to
    + * https://docs.python.org/3/c-api/init.html#PySys_SetArgvEx
    + */
    + PySys_SetArgvEx(1, argv, 0);
    + PyMem_RawFree(argv[0]);
    +
    + /* initialize pygobject */
    + if(gplugin_python3_loader_init_pygobject()) {
    + if(gplugin_python3_loader_init_gettext()) {
    + return TRUE;
    + }
    + }
    +
    + return FALSE;
    +}
    +
    +/******************************************************************************
    + * GObject Implementation
    + *****************************************************************************/
    +static void
    +gplugin_python3_loader_init(G_GNUC_UNUSED GPluginPython3Loader *loader)
    +{
    +}
    +
    +static void
    +gplugin_python3_loader_class_finalize(
    + G_GNUC_UNUSED GPluginPython3LoaderClass *klass)
    +{
    +}
    +
    +static void
    +gplugin_python3_loader_class_init(GPluginPython3LoaderClass *klass)
    +{
    + GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
    +
    + loader_class->supported_extensions =
    + gplugin_python3_loader_supported_extensions;
    + loader_class->query = gplugin_python3_loader_query;
    + loader_class->load = gplugin_python3_loader_load;
    + loader_class->unload = gplugin_python3_loader_unload;
    +}
    +
    +/******************************************************************************
    + * API
    + *****************************************************************************/
    +void
    +gplugin_python3_loader_register(GPluginNativePlugin *native)
    +{
    + gplugin_python3_loader_register_type(G_TYPE_MODULE(native));
    +
    + gplugin_python3_loader_init_python();
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/gplugin-python3-loader.h Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,38 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#ifndef GPLUGIN_PYTHON3_LOADER_H
    +#define GPLUGIN_PYTHON3_LOADER_H
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_BEGIN_DECLS
    +
    +#define GPLUGIN_PYTHON3_TYPE_LOADER (gplugin_python3_loader_get_type())
    +G_DECLARE_FINAL_TYPE(
    + GPluginPython3Loader,
    + gplugin_python3_loader,
    + GPLUGIN_PYTHON3,
    + LOADER,
    + GPluginLoader)
    +
    +void gplugin_python3_loader_register(GPluginNativePlugin *native);
    +
    +G_END_DECLS
    +
    +#endif /* GPLUGIN_PYTHON3_LOADER_H */
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/gplugin-python3-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,310 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include "gplugin-python3-plugin.h"
    +
    +#include <Python.h>
    +
    +/******************************************************************************
    + * Typedefs
    + *****************************************************************************/
    +struct _GPluginPython3Plugin {
    + GObject parent;
    +
    + PyObject *module;
    + PyObject *query;
    + PyObject *load;
    + PyObject *unload;
    +
    + /* overrides */
    + gchar *filename;
    + GPluginLoader *loader;
    + GPluginPluginInfo *info;
    + GPluginPluginState state;
    + GError *error;
    +};
    +
    +/******************************************************************************
    + * Enums
    + *****************************************************************************/
    +enum {
    + PROP_ZERO,
    + PROP_MODULE,
    + PROP_LOAD_FUNC,
    + PROP_UNLOAD_FUNC,
    + N_PROPERTIES,
    + /* overrides */
    + PROP_FILENAME = N_PROPERTIES,
    + PROP_LOADER,
    + PROP_INFO,
    + PROP_STATE,
    + PROP_ERROR,
    +};
    +static GParamSpec *properties[N_PROPERTIES] = {
    + NULL,
    +};
    +
    +/* I hate forward declarations... */
    +static void gplugin_python3_plugin_iface_init(GPluginPluginInterface *iface);
    +
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + GPluginPython3Plugin,
    + gplugin_python3_plugin,
    + G_TYPE_OBJECT,
    + 0,
    + G_IMPLEMENT_INTERFACE(
    + GPLUGIN_TYPE_PLUGIN,
    + gplugin_python3_plugin_iface_init));
    +
    +/******************************************************************************
    + * GPluginPlugin Implementation
    + *****************************************************************************/
    +static void
    +gplugin_python3_plugin_iface_init(G_GNUC_UNUSED GPluginPluginInterface *iface)
    +{
    +}
    +
    +/******************************************************************************
    + * Private Stuff
    + *****************************************************************************/
    +static void
    +gplugin_python3_plugin_set_module(
    + GPluginPython3Plugin *plugin,
    + PyObject *module)
    +{
    + g_return_if_fail(GPLUGIN_IS_PLUGIN(plugin));
    + g_return_if_fail(module);
    +
    + Py_XINCREF(module);
    + Py_CLEAR(plugin->module);
    + plugin->module = module;
    +}
    +
    +static gpointer
    +gplugin_python3_plugin_get_load_func(GPluginPython3Plugin *plugin)
    +{
    + g_return_val_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin), NULL);
    +
    + return plugin->load;
    +}
    +
    +static void
    +gplugin_python3_plugin_set_load_func(
    + GPluginPython3Plugin *plugin,
    + PyObject *func)
    +{
    + g_return_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin));
    + g_return_if_fail(func != NULL);
    +
    + Py_XINCREF(func);
    + Py_CLEAR(plugin->load);
    + plugin->load = func;
    +}
    +
    +static gpointer
    +gplugin_python3_plugin_get_unload_func(GPluginPython3Plugin *plugin)
    +{
    + g_return_val_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin), NULL);
    +
    + return plugin->unload;
    +}
    +
    +static void
    +gplugin_python3_plugin_set_unload_func(
    + GPluginPython3Plugin *plugin,
    + PyObject *func)
    +{
    + g_return_if_fail(GPLUGIN_PYTHON3_IS_PLUGIN(plugin));
    + g_return_if_fail(func != NULL);
    +
    + Py_XINCREF(func);
    + Py_CLEAR(plugin->unload);
    + plugin->unload = func;
    +}
    +
    +/******************************************************************************
    + * GObject Implementation
    + *****************************************************************************/
    +static void
    +gplugin_python3_plugin_get_property(
    + GObject *obj,
    + guint param_id,
    + GValue *value,
    + GParamSpec *pspec)
    +{
    + GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
    +
    + switch(param_id) {
    + case PROP_MODULE:
    + g_value_set_pointer(value, plugin->module);
    + break;
    + case PROP_LOAD_FUNC:
    + g_value_set_pointer(
    + value,
    + gplugin_python3_plugin_get_load_func(plugin));
    + break;
    + case PROP_UNLOAD_FUNC:
    + g_value_set_pointer(
    + value,
    + gplugin_python3_plugin_get_unload_func(plugin));
    + break;
    +
    + /* overrides */
    + case PROP_FILENAME:
    + g_value_set_string(value, plugin->filename);
    + break;
    + case PROP_LOADER:
    + g_value_set_object(value, plugin->loader);
    + break;
    + case PROP_INFO:
    + g_value_set_object(value, plugin->info);
    + break;
    + case PROP_STATE:
    + g_value_set_enum(value, plugin->state);
    + break;
    + case PROP_ERROR:
    + g_value_set_boxed(value, plugin->error);
    + break;
    +
    + default:
    + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    + break;
    + }
    +}
    +
    +static void
    +gplugin_python3_plugin_set_property(
    + GObject *obj,
    + guint param_id,
    + const GValue *value,
    + GParamSpec *pspec)
    +{
    + GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
    +
    + switch(param_id) {
    + case PROP_MODULE:
    + gplugin_python3_plugin_set_module(
    + plugin,
    + g_value_get_pointer(value));
    + break;
    + case PROP_LOAD_FUNC:
    + gplugin_python3_plugin_set_load_func(
    + plugin,
    + g_value_get_pointer(value));
    + break;
    + case PROP_UNLOAD_FUNC:
    + gplugin_python3_plugin_set_unload_func(
    + plugin,
    + g_value_get_pointer(value));
    + break;
    +
    + /* overrides */
    + case PROP_FILENAME:
    + plugin->filename = g_value_dup_string(value);
    + break;
    + case PROP_LOADER:
    + plugin->loader = g_value_dup_object(value);
    + break;
    + case PROP_INFO:
    + plugin->info = g_value_dup_object(value);
    + break;
    + case PROP_STATE:
    + plugin->state = g_value_get_enum(value);
    + break;
    + case PROP_ERROR:
    + plugin->error = g_value_dup_boxed(value);
    + break;
    +
    + default:
    + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    + break;
    + }
    +}
    +
    +static void
    +gplugin_python3_plugin_finalize(GObject *obj)
    +{
    + GPluginPython3Plugin *plugin = GPLUGIN_PYTHON3_PLUGIN(obj);
    +
    + Py_CLEAR(plugin->module);
    + Py_CLEAR(plugin->load);
    + Py_CLEAR(plugin->unload);
    +
    + g_clear_pointer(&plugin->filename, g_free);
    + g_clear_object(&plugin->loader);
    + g_clear_object(&plugin->info);
    + g_clear_error(&plugin->error);
    +
    + G_OBJECT_CLASS(gplugin_python3_plugin_parent_class)->finalize(obj);
    +}
    +
    +static void
    +gplugin_python3_plugin_init(G_GNUC_UNUSED GPluginPython3Plugin *plugin)
    +{
    +}
    +
    +static void
    +gplugin_python3_plugin_class_finalize(
    + G_GNUC_UNUSED GPluginPython3PluginClass *klass)
    +{
    +}
    +
    +static void
    +gplugin_python3_plugin_class_init(GPluginPython3PluginClass *klass)
    +{
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    +
    + obj_class->get_property = gplugin_python3_plugin_get_property;
    + obj_class->set_property = gplugin_python3_plugin_set_property;
    + obj_class->finalize = gplugin_python3_plugin_finalize;
    +
    + properties[PROP_MODULE] = g_param_spec_pointer(
    + "module",
    + "module",
    + "The python module object",
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    +
    + properties[PROP_LOAD_FUNC] = g_param_spec_pointer(
    + "load-func",
    + "load-func",
    + "The python load function",
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    +
    + properties[PROP_UNLOAD_FUNC] = g_param_spec_pointer(
    + "unload-func",
    + "unload-func",
    + "The python unload function",
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    +
    + g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    +
    + /* add our overrides */
    + g_object_class_override_property(obj_class, PROP_FILENAME, "filename");
    + g_object_class_override_property(obj_class, PROP_LOADER, "loader");
    + g_object_class_override_property(obj_class, PROP_INFO, "info");
    + g_object_class_override_property(obj_class, PROP_STATE, "state");
    + g_object_class_override_property(obj_class, PROP_ERROR, "error");
    +}
    +
    +/******************************************************************************
    + * API
    + *****************************************************************************/
    +void
    +gplugin_python3_plugin_register(GPluginNativePlugin *native)
    +{
    + gplugin_python3_plugin_register_type(G_TYPE_MODULE(native));
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/gplugin-python3-plugin.h Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,38 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#ifndef GPLUGIN_PYTHON3_PLUGIN_H
    +#define GPLUGIN_PYTHON3_PLUGIN_H
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +G_BEGIN_DECLS
    +
    +#define GPLUGIN_PYTHON3_TYPE_PLUGIN (gplugin_python3_plugin_get_type())
    +G_DECLARE_FINAL_TYPE(
    + GPluginPython3Plugin,
    + gplugin_python3_plugin,
    + GPLUGIN_PYTHON3,
    + PLUGIN,
    + GObject)
    +
    +void gplugin_python3_plugin_register(GPluginNativePlugin *native);
    +
    +G_END_DECLS
    +
    +#endif /* GPLUGIN_PYTHON3_PLUGIN_H */
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/gplugin-python3-test-pygobject.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,64 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <stdio.h>
    +
    +#include <glib.h>
    +
    +#include <Python.h>
    +#include <pygobject.h>
    +
    +gint
    +main(gint argc, gchar *argv[])
    +{
    + wchar_t *wargv[] = {NULL, NULL};
    + size_t len;
    +
    + /* initialize python */
    + if(!Py_IsInitialized())
    + Py_InitializeEx(FALSE);
    +
    + /* setup wargv */
    + len = mbstowcs(NULL, argv[0], 0);
    + if(len == (size_t)-1) {
    + printf("Failed to call mbstowcs to find length of argv[0]: %s\n", argv[0]);
    + return -1;
    + }
    +
    + wargv[0] = g_new0(wchar_t, len + 1);
    + len = mbstowcs(wargv[0], argv[0], len + 1);
    + if(len == (size_t)-1) {
    + g_free(wargv[0]);
    + printf("Failed to call mbstowcs to convert argv[0]: %s\n", argv[0]);
    + return -1;
    + }
    +
    + /* setup sys.path */
    + PySys_SetArgvEx(1, wargv, 0);
    +
    + g_free(wargv[0]);
    +
    + /* initialize pygobject */
    + pygobject_init(3, 0, 0);
    + if(PyErr_Occurred()) {
    + printf("Calling pygobject_init failed.\n");
    + PyErr_Print();
    + return -1;
    + }
    +
    + return 0;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/gplugin-python3-utils.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,117 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include "gplugin-python3-utils.h"
    +
    +#include <string.h>
    +
    +#include <gplugin.h>
    +
    +#include <Python.h>
    +
    +gchar *
    +gplugin_python3_filename_to_module(const gchar *filename)
    +{
    + gchar *base = NULL;
    + gchar *e = NULL, *r = NULL;
    +
    + g_return_val_if_fail(filename != NULL, NULL);
    +
    + /* first make sure we just have a filename */
    + base = g_path_get_basename(filename);
    +
    + /* now find the last . for the extension */
    + e = g_utf8_strrchr(base, g_utf8_strlen(base, -1), g_utf8_get_char("."));
    + if(e == NULL) {
    + return base;
    + }
    +
    + /* now copy the module name into r */
    + r = g_malloc(e - base + 1);
    + memcpy(r, base, e - base);
    + r[e - base] = 0;
    +
    + /* free the basename */
    + g_free(base);
    +
    + return r;
    +}
    +
    +gboolean
    +gplugin_python3_add_module_path(const gchar *module_path)
    +{
    + PyObject *sys_path = NULL, *path = NULL;
    + gboolean ret = FALSE;
    +
    + sys_path = PySys_GetObject("path");
    +
    + path = PyUnicode_FromString(module_path);
    +
    + if(PySequence_Contains(sys_path, path) == 0) {
    + PyList_Insert(sys_path, 0, path);
    + ret = TRUE;
    + }
    +
    + Py_DECREF(path);
    +
    + return ret;
    +}
    +
    +GError *
    +gplugin_python3_exception_to_gerror(void)
    +{
    + GError *error = NULL;
    + PyObject *type = NULL, *value = NULL, *trace = NULL;
    + PyObject *type_name = NULL, *value_str = NULL, *obj = NULL;
    +
    + if(!PyErr_Occurred())
    + return NULL;
    +
    + PyErr_Fetch(&type, &value, &trace);
    + if(type == NULL)
    + return NULL;
    +
    + PyErr_NormalizeException(&type, &value, &trace);
    + Py_XDECREF(trace);
    +
    + type_name = PyObject_GetAttrString(type, "__name__");
    + Py_DECREF(type);
    +
    + value_str = PyObject_Str(value);
    + Py_DECREF(value);
    +
    + /* now decode the utf8 into a string we can use */
    + obj = PyUnicode_AsUTF8String(type_name);
    + Py_DECREF(type_name);
    + type_name = obj;
    +
    + obj = PyUnicode_AsUTF8String(value_str);
    + Py_DECREF(value_str);
    + value_str = obj;
    +
    + error = g_error_new(
    + GPLUGIN_DOMAIN,
    + 0,
    + "%s: %s",
    + PyBytes_AsString(type_name),
    + PyBytes_AsString(value_str));
    +
    + Py_DECREF(type_name);
    + Py_DECREF(value_str);
    +
    + return error;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/gplugin-python3-utils.h Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,33 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#ifndef GPLUGIN_PYTHON3_UTILS_H
    +#define GPLUGIN_PYTHON3_UTILS_H
    +
    +#include <glib.h>
    +
    +G_BEGIN_DECLS
    +
    +gchar *gplugin_python3_filename_to_module(const gchar *filename);
    +
    +gboolean gplugin_python3_add_module_path(const gchar *module_path);
    +
    +GError *gplugin_python3_exception_to_gerror(void);
    +
    +G_END_DECLS
    +
    +#endif /* GPLUGIN_PYTHON3_UTILS_H */
    --- a/python/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/python/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -1,60 +1,60 @@
    -if get_option('python')
    - if not get_option('gobject-introspection')
    +if get_option('python3')
    + if not get_option('introspection')
    error('Python plugin requires GObject Introspection.')
    endif
    - GPLUGIN_PYTHON_SOURCES = [
    - 'gplugin-python-core.c',
    - 'gplugin-python-loader.c',
    - 'gplugin-python-plugin.c',
    - 'gplugin-python-utils.c',
    + GPLUGIN_PYTHON3_SOURCES = [
    + 'gplugin-python3-core.c',
    + 'gplugin-python3-loader.c',
    + 'gplugin-python3-plugin.c',
    + 'gplugin-python3-utils.c',
    ]
    - GPLUGIN_PYTHON_HEADERS = [
    - 'gplugin-python-loader.h',
    - 'gplugin-python-plugin.h',
    - 'gplugin-python-utils.h',
    + GPLUGIN_PYTHON3_HEADERS = [
    + 'gplugin-python3-loader.h',
    + 'gplugin-python3-plugin.h',
    + 'gplugin-python3-utils.h',
    ]
    - PYTHON3 = dependency('python3-embed', required: false)
    + PYTHON3 = dependency('python3-embed', version: '>=3.5.0', required: false)
    if not PYTHON3.found()
    - PYTHON3 = dependency('python3')
    + PYTHON3 = dependency('python3', version: '>=3.5.0')
    endif
    - PYGOBJECT = dependency('pygobject-3.0', version : '>=3.0.0')
    + PYGOBJECT = dependency('pygobject-3.0', version: '>=3.0.0')
    # Compile and run our python-gi test program
    - python_gi_test = compiler.run(files('gplugin-python-test-pygobject.c'),
    + python3_gi_test = compiler.run(files('gplugin-python3-test-pygobject.c'),
    dependencies : [GLIB, PYTHON3, PYGOBJECT],
    - name : 'Python GI')
    - if not python_gi_test.compiled() or python_gi_test.returncode() != 0
    - error('pygobject does not work with python3')
    + name : 'Python3 GI')
    + if not python3_gi_test.compiled() or python3_gi_test.returncode() != 0
    + error('pygobject does not work with Python3')
    endif
    # Now add our libraries
    - gplugin_python_inc = include_directories('.')
    - gplugin_python = shared_library('gplugin-python',
    - GPLUGIN_PYTHON_SOURCES,
    - GPLUGIN_PYTHON_HEADERS,
    + gplugin_python3_inc = include_directories('.')
    + gplugin_python3 = shared_library('gplugin-python3',
    + GPLUGIN_PYTHON3_SOURCES,
    + GPLUGIN_PYTHON3_HEADERS,
    name_prefix : '',
    dependencies : [PYTHON3, PYGOBJECT, gplugin_dep],
    install : true,
    - install_dir : join_paths(get_option('libdir'), 'gplugin')
    + install_dir : get_option('libdir') / 'gplugin'
    )
    - gplugin_python_dep = declare_dependency(
    - include_directories : gplugin_python_inc,
    - link_with : gplugin_python,
    + gplugin_python3_dep = declare_dependency(
    + include_directories : gplugin_python3_inc,
    + link_with : gplugin_python3,
    )
    - gplugin_python_static = static_library('gplugin-python-static',
    - GPLUGIN_PYTHON_SOURCES,
    - GPLUGIN_PYTHON_HEADERS,
    + gplugin_python3_static = static_library('gplugin-python3-static',
    + GPLUGIN_PYTHON3_SOURCES,
    + GPLUGIN_PYTHON3_HEADERS,
    dependencies : [PYTHON3, PYGOBJECT, gplugin_dep],
    )
    - gplugin_python_static_dep = declare_dependency(
    - include_directories : gplugin_python_inc,
    - link_with : gplugin_python_static,
    + gplugin_python3_static_dep = declare_dependency(
    + include_directories : gplugin_python3_inc,
    + link_with : gplugin_python3_static,
    )
    -endif # python
    +endif # python3
    subdir('tests')
    --- a/python/tests/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/python/tests/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -1,26 +1,22 @@
    -if get_option('python')
    +if get_option('python3')
    -e = executable('test-python-loader', 'test-python-loader.c',
    +e = executable('test-python3-loader', 'test-python3-loader.c',
    include_directories : include_directories('.'),
    c_args : [
    - '-DPYTHON_LOADER_DIR="@0@"'.format(join_paths(meson.current_build_dir(), '..')),
    - '-DPYTHON_PLUGIN_DIR="@0@/plugins"'.format(meson.current_source_dir()),
    + '-DPYTHON3_LOADER_DIR="@0@/.."'.format(meson.current_build_dir()),
    + '-DPYTHON3_PLUGIN_DIR="@0@/plugins"'.format(meson.current_source_dir()),
    ],
    link_with : gplugin_loader_tests,
    dependencies : [GLIB, GOBJECT, PYTHON3, PYGOBJECT, gplugin_dep])
    -test('Python loader', e)
    +test('Python3 loader', e)
    # we can't see the symbols in gplugin-python externally, so use the static
    # version for testing
    -e = executable('test-python-utils', 'test-python-utils.c',
    +e = executable('test-python3-utils', 'test-python3-utils.c',
    include_directories : include_directories('.'),
    - c_args : [
    - '-DPYTHON_LOADER_DIR="@0@"'.format(join_paths(meson.current_build_dir(), '..')),
    - '-DPYTHON_PLUGIN_DIR="@0@/plugins"'.format(meson.current_source_dir()),
    - ],
    link_with : [gplugin_loader_tests],
    dependencies : [GLIB, GOBJECT, PYTHON3, PYGOBJECT,
    - gplugin_python_static_dep])
    -test('Python utils', e)
    + gplugin_python3_static_dep])
    +test('Python3 utils', e)
    -endif # python
    +endif # python3
    --- a/python/tests/plugins/basic.py Thu Nov 07 23:09:55 2019 -0600
    +++ b/python/tests/plugins/basic.py Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    # vi:et:ts=4 sw=4 sts=4
    -# Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    #
    # This library is free software; you can redistribute it and/or
    # modify it under the terms of the GNU Lesser General Public
    @@ -14,11 +14,15 @@
    # You should have received a copy of the GNU Lesser General Public
    # License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -from gi.repository import GPlugin
    +import gi
    +
    +gi.require_version('GPlugin', '1.0')
    +from gi.repository import GPlugin # noqa
    +
    def gplugin_query():
    return GPlugin.PluginInfo(
    - id='gplugin/python-basic-plugin',
    + id='gplugin/python3-basic-plugin',
    abi_version=0x01020304,
    name='basic plugin',
    authors=['author1'],
    @@ -30,10 +34,10 @@
    description='description',
    )
    +
    def gplugin_load(plugin):
    return True
    def gplugin_unload(plugin):
    return True
    -
    --- a/python/tests/plugins/dependent.py Thu Nov 07 23:09:55 2019 -0600
    +++ b/python/tests/plugins/dependent.py Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    # vi:et:ts=4 sw=4 sts=4
    -# Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    #
    # This library is free software; you can redistribute it and/or
    # modify it under the terms of the GNU Lesser General Public
    @@ -14,18 +14,22 @@
    # You should have received a copy of the GNU Lesser General Public
    # License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -from gi.repository import GPlugin
    +import gi
    +
    +gi.require_version('GPlugin', '1.0')
    +from gi.repository import GPlugin # noqa
    +
    def gplugin_query():
    return GPlugin.PluginInfo(
    - id='gplugin/python-dependent-plugin',
    + id='gplugin/python3-dependent-plugin',
    dependencies=['dependency1', 'dependency2'],
    )
    +
    def gplugin_load(plugin):
    return False
    def gplugin_unload(plugin):
    return False
    -
    --- a/python/tests/plugins/load-exception.py Thu Nov 07 23:09:55 2019 -0600
    +++ b/python/tests/plugins/load-exception.py Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    # vi:et:ts=4 sw=4 sts=4
    -# Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    #
    # This library is free software; you can redistribute it and/or
    # modify it under the terms of the GNU Lesser General Public
    @@ -14,11 +14,15 @@
    # You should have received a copy of the GNU Lesser General Public
    # License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -from gi.repository import GPlugin
    +import gi
    +
    +gi.require_version('GPlugin', '1.0')
    +from gi.repository import GPlugin # noqa
    +
    def gplugin_query():
    return GPlugin.PluginInfo(
    - id="gplugin/python-load-exception",
    + id="gplugin/python3-load-exception",
    )
    @@ -28,5 +32,3 @@
    def gplugin_unload(plugin):
    return True
    -
    -
    --- a/python/tests/plugins/load-failed.py Thu Nov 07 23:09:55 2019 -0600
    +++ b/python/tests/plugins/load-failed.py Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    # vi:et:ts=4 sw=4 sts=4
    -# Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    #
    # This library is free software; you can redistribute it and/or
    # modify it under the terms of the GNU Lesser General Public
    @@ -14,11 +14,15 @@
    # You should have received a copy of the GNU Lesser General Public
    # License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -from gi.repository import GPlugin
    +import gi
    +
    +gi.require_version('GPlugin', '1.0')
    +from gi.repository import GPlugin # noqa
    +
    def gplugin_query():
    return GPlugin.PluginInfo(
    - id="gplugin/python-load-failed",
    + id="gplugin/python3-load-failed",
    )
    @@ -28,5 +32,3 @@
    def gplugin_unload(plugin):
    return True
    -
    -
    --- a/python/tests/plugins/unload-failed.py Thu Nov 07 23:09:55 2019 -0600
    +++ b/python/tests/plugins/unload-failed.py Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    # vi:et:ts=4 sw=4 sts=4
    -# Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    +# Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    #
    # This library is free software; you can redistribute it and/or
    # modify it under the terms of the GNU Lesser General Public
    @@ -14,11 +14,15 @@
    # You should have received a copy of the GNU Lesser General Public
    # License along with this library; if not, see <http://www.gnu.org/licenses/>.
    -from gi.repository import GPlugin
    +import gi
    +
    +gi.require_version('GPlugin', '1.0')
    +from gi.repository import GPlugin # noqa
    +
    def gplugin_query():
    return GPlugin.PluginInfo(
    - id="gplugin/python-unload-failed",
    + id="gplugin/python3-unload-failed",
    )
    @@ -28,5 +32,3 @@
    def gplugin_unload(plugin):
    return False
    -
    -
    --- a/python/tests/test-python-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <glib.h>
    -#include <gplugin.h>
    -
    -#include <gplugin/gplugin-loader-tests.h>
    -
    -/******************************************************************************
    - * Main
    - *****************************************************************************/
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - gplugin_loader_tests_main(PYTHON_LOADER_DIR, PYTHON_PLUGIN_DIR, "python");
    -
    - return g_test_run();
    -}
    -
    --- a/python/tests/test-python-utils.c Thu Nov 07 23:09:55 2019 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,76 +0,0 @@
    -/*
    - * Copyright (C) 2011-2014 Gary Kramlich <grim@reaperworld.com>
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    - */
    -
    -#include <Python.h>
    -
    -#include "gplugin-python-utils.h"
    -
    -#include <glib.h>
    -
    -/******************************************************************************
    - * filename to module tests
    - *****************************************************************************/
    -static void
    -test_filename_to_module_NULL_subprocess(void) {
    - gplugin_python_filename_to_module(NULL);
    -}
    -
    -static void
    -test_filename_to_module_NULL(void) {
    - g_test_trap_subprocess("/loaders/python/utils/filename_to_module/NULL/subprocess", 0, 0);
    -
    - g_test_trap_assert_failed();
    -}
    -
    -static void
    -test_filename_to_module_empty(void) {
    - gchar *module = gplugin_python_filename_to_module("");
    -
    - g_assert_cmpstr(module, ==, "");
    -
    - g_free(module);
    -}
    -
    -static void
    -test_filename_to_module_no_extension(void) {
    - gchar *module = gplugin_python_filename_to_module("foo");
    -
    - g_assert_cmpstr(module, ==, "foo");
    -
    - g_free(module);
    -}
    -
    -/******************************************************************************
    - * Main
    - *****************************************************************************/
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - /* tests */
    - g_test_add_func("/loaders/python/utils/filename_to_module/NULL",
    - test_filename_to_module_NULL);
    - g_test_add_func("/loaders/python/utils/filename_to_module/NULL/subprocess",
    - test_filename_to_module_NULL_subprocess);
    - g_test_add_func("/loaders/python/utils/filename_to_module/empty",
    - test_filename_to_module_empty);
    - g_test_add_func("/loaders/python/utils/filename_to_module/no-extension",
    - test_filename_to_module_no_extension);
    -
    - return g_test_run();
    -}
    -
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/tests/test-python3-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,37 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <glib.h>
    +
    +#include <gplugin.h>
    +#include <gplugin/gplugin-loader-tests.h>
    +
    +/******************************************************************************
    + * Main
    + *****************************************************************************/
    +gint
    +main(gint argc, gchar **argv)
    +{
    + g_test_init(&argc, &argv, NULL);
    +
    + gplugin_loader_tests_main(
    + PYTHON3_LOADER_DIR,
    + PYTHON3_PLUGIN_DIR,
    + "python3");
    +
    + return g_test_run();
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python/tests/test-python3-utils.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,87 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <glib.h>
    +
    +#include <Python.h>
    +
    +#include "gplugin-python3-utils.h"
    +
    +/******************************************************************************
    + * filename to module tests
    + *****************************************************************************/
    +static void
    +test_filename_to_module_NULL_subprocess(void)
    +{
    + gplugin_python3_filename_to_module(NULL);
    +}
    +
    +static void
    +test_filename_to_module_NULL(void)
    +{
    + g_test_trap_subprocess(
    + "/loaders/python/utils/filename_to_module/NULL/subprocess",
    + 0,
    + 0);
    +
    + g_test_trap_assert_failed();
    +}
    +
    +static void
    +test_filename_to_module_empty(void)
    +{
    + gchar *module = gplugin_python3_filename_to_module("");
    +
    + g_assert_cmpstr(module, ==, "");
    +
    + g_free(module);
    +}
    +
    +static void
    +test_filename_to_module_no_extension(void)
    +{
    + gchar *module = gplugin_python3_filename_to_module("foo");
    +
    + g_assert_cmpstr(module, ==, "foo");
    +
    + g_free(module);
    +}
    +
    +/******************************************************************************
    + * Main
    + *****************************************************************************/
    +gint
    +main(gint argc, gchar **argv)
    +{
    + g_test_init(&argc, &argv, NULL);
    +
    + /* tests */
    + g_test_add_func(
    + "/loaders/python/utils/filename_to_module/NULL",
    + test_filename_to_module_NULL);
    + g_test_add_func(
    + "/loaders/python/utils/filename_to_module/NULL/subprocess",
    + test_filename_to_module_NULL_subprocess);
    + g_test_add_func(
    + "/loaders/python/utils/filename_to_module/empty",
    + test_filename_to_module_empty);
    + g_test_add_func(
    + "/loaders/python/utils/filename_to_module/no-extension",
    + test_filename_to_module_no_extension);
    +
    + return g_test_run();
    +}
    --- a/tcc/gplugin-tcc-core.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/gplugin-tcc-core.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,36 +1,34 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    +#include <glib/gi18n-lib.h>
    +
    #include <gplugin.h>
    #include <gplugin-native.h>
    -#include <glib/gi18n-lib.h>
    -
    #include "gplugin-tcc-loader.h"
    #include "gplugin-tcc-plugin.h"
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + const gchar *const authors[] = {"Eion Robb <eion@robbmob.com>", NULL};
    -G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Eion Robb <eion@robbmob.com>",
    - NULL
    - };
    -
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/tcc-loader",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    @@ -38,39 +36,34 @@
    "load-on-query", TRUE,
    "name", "C source plugin loader",
    "version", GPLUGIN_VERSION,
    - "license-id", "LGPL2",
    + "license-id", "LGPL-2.0-or-later",
    "summary", "A plugin that can load C source plugins",
    - "description", "This plugin allows the loading of plugins written in C.",
    + "description", "This plugin allows the loading of plugins written in "
    + "C.",
    "authors", authors,
    "website", GPLUGIN_WEBSITE,
    "category", "loaders",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(GPluginNativePlugin *plugin, GError **error)
    {
    gplugin_tcc_loader_register(plugin);
    gplugin_tcc_plugin_register(plugin);
    - gplugin_manager_register_loader(GPLUGIN_TCC_TYPE_LOADER);
    -
    - return TRUE;
    + return gplugin_manager_register_loader(GPLUGIN_TCC_TYPE_LOADER, error);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - GError **error)
    +gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    {
    g_set_error_literal(
    error,
    GPLUGIN_DOMAIN,
    0,
    - _("The TCC loader can not be unloaded")
    - );
    + _("The TCC loader can not be unloaded"));
    return FALSE;
    }
    -
    --- a/tcc/gplugin-tcc-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/gplugin-tcc-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,49 +1,51 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #include "gplugin-tcc-loader.h"
    -#include "gplugin-tcc-plugin.h"
    -
    -#include <glib/gi18n.h>
    +#include <glib/gi18n-lib.h>
    #include <libtcc.h>
    +#include "gplugin-tcc-plugin.h"
    +
    struct _GPluginTccLoader {
    GPluginLoader parent;
    };
    -G_DEFINE_DYNAMIC_TYPE(GPluginTccLoader, gplugin_tcc_loader, GPLUGIN_TYPE_LOADER);
    +G_DEFINE_DYNAMIC_TYPE(
    + GPluginTccLoader,
    + gplugin_tcc_loader,
    + GPLUGIN_TYPE_LOADER);
    /******************************************************************************
    * GPluginLoaderInterface API
    *****************************************************************************/
    static GSList *
    -gplugin_tcc_loader_class_supported_extensions(G_GNUC_UNUSED const GPluginLoaderClass *klass) {
    - GSList *exts = NULL;
    -
    - exts = g_slist_append(exts, "c");
    -
    - return exts;
    +gplugin_tcc_loader_supported_extensions(G_GNUC_UNUSED GPluginLoader *l)
    +{
    + return g_list_append(NULL, "c");
    }
    static GPluginPlugin *
    -gplugin_tcc_loader_query(GPluginLoader *loader, const gchar *filename,
    - GError **error)
    +gplugin_tcc_loader_query(
    + GPluginLoader *loader,
    + const gchar *filename,
    + GError **error)
    {
    GPluginPlugin *plugin = NULL;
    GPluginPluginInfo *info = NULL;
    @@ -58,7 +60,12 @@
    tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
    if(tcc_add_file(s, filename) == -1) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0, "couldn't load file %s", filename);
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "couldn't load file %s",
    + filename);
    tcc_delete(s);
    @@ -67,7 +74,11 @@
    /* copy code into memory */
    if((memsize = tcc_relocate(s, NULL)) < 0) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0, "couldn't work out how much memory is needed");
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "couldn't work out how much memory is needed");
    tcc_delete(s);
    return NULL;
    @@ -75,16 +86,25 @@
    memneeded = g_malloc0(memsize);
    if(tcc_relocate(s, memneeded) < 0) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0, "could not relocate plugin into memory");
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "could not relocate plugin into memory");
    tcc_delete(s);
    g_free(memneeded);
    return NULL;
    }
    - gplugin_query = (GPluginTccPluginQueryFunc) tcc_get_symbol(s, "gplugin_query");
    - if (gplugin_query == NULL) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0, "no gplugin_query function found");
    + gplugin_query =
    + (GPluginTccPluginQueryFunc)tcc_get_symbol(s, "gplugin_query");
    + if(gplugin_query == NULL) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "no gplugin_query function found");
    tcc_delete(s);
    g_free(memneeded);
    @@ -98,27 +118,31 @@
    return NULL;
    }
    - plugin = g_object_new(GPLUGIN_TCC_TYPE_PLUGIN,
    - "filename", filename,
    - "loader", loader,
    - "state", s,
    - "memory", memneeded,
    - "info", info,
    - NULL);
    + /* clang-format off */
    + plugin = g_object_new(
    + GPLUGIN_TCC_TYPE_PLUGIN,
    + "filename", filename,
    + "loader", loader,
    + "state", s,
    + "memory", memneeded,
    + "info", info,
    + NULL);
    + /* clang-format on */
    return plugin;
    }
    static gboolean
    -gplugin_tcc_loader_load(G_GNUC_UNUSED GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - GError **error)
    +gplugin_tcc_loader_load(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    GPluginTccPluginLoadFunc gplugin_load = NULL;
    TCCState *s = gplugin_tcc_plugin_get_state(GPLUGIN_TCC_PLUGIN(plugin));
    - gplugin_load = (GPluginTccPluginLoadFunc) tcc_get_symbol(s, "gplugin_load");
    - if (gplugin_load == NULL) {
    + gplugin_load = (GPluginTccPluginLoadFunc)tcc_get_symbol(s, "gplugin_load");
    + if(gplugin_load == NULL) {
    g_set_error(error, GPLUGIN_DOMAIN, 0, "no gplugin_load function found");
    return FALSE;
    @@ -128,16 +152,22 @@
    }
    static gboolean
    -gplugin_tcc_loader_unload(G_GNUC_UNUSED GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - GError **error)
    +gplugin_tcc_loader_unload(
    + G_GNUC_UNUSED GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + GError **error)
    {
    GPluginTccPluginLoadFunc gplugin_unload = NULL;
    TCCState *s = gplugin_tcc_plugin_get_state(GPLUGIN_TCC_PLUGIN(plugin));
    - gplugin_unload = (GPluginTccPluginUnloadFunc) tcc_get_symbol(s, "gplugin_unload");
    - if (gplugin_unload == NULL) {
    - g_set_error(error, GPLUGIN_DOMAIN, 0, "no gplugin_unload function found");
    + gplugin_unload =
    + (GPluginTccPluginUnloadFunc)tcc_get_symbol(s, "gplugin_unload");
    + if(gplugin_unload == NULL) {
    + g_set_error(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + "no gplugin_unload function found");
    return FALSE;
    }
    @@ -149,19 +179,22 @@
    * GObject Stuff
    *****************************************************************************/
    static void
    -gplugin_tcc_loader_init(G_GNUC_UNUSED GPluginTccLoader *loader) {
    +gplugin_tcc_loader_init(G_GNUC_UNUSED GPluginTccLoader *loader)
    +{
    }
    static void
    -gplugin_tcc_loader_class_finalize(G_GNUC_UNUSED GPluginTccLoaderClass *klass) {
    +gplugin_tcc_loader_class_finalize(G_GNUC_UNUSED GPluginTccLoaderClass *klass)
    +{
    }
    static void
    -gplugin_tcc_loader_class_init(GPluginTccLoaderClass *klass) {
    +gplugin_tcc_loader_class_init(GPluginTccLoaderClass *klass)
    +{
    GPluginLoaderClass *loader_class = GPLUGIN_LOADER_CLASS(klass);
    loader_class->supported_extensions =
    - gplugin_tcc_loader_class_supported_extensions;
    + gplugin_tcc_loader_supported_extensions;
    loader_class->query = gplugin_tcc_loader_query;
    loader_class->load = gplugin_tcc_loader_load;
    loader_class->unload = gplugin_tcc_loader_unload;
    @@ -171,6 +204,7 @@
    * API
    *****************************************************************************/
    void
    -gplugin_tcc_loader_register(GPluginNativePlugin *plugin) {
    +gplugin_tcc_loader_register(GPluginNativePlugin *plugin)
    +{
    gplugin_tcc_loader_register_type(G_TYPE_MODULE(plugin));
    }
    --- a/tcc/gplugin-tcc-loader.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/gplugin-tcc-loader.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,18 +1,18 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #ifndef GPLUGIN_TCC_LOADER_H
    #define GPLUGIN_TCC_LOADER_H
    @@ -23,11 +23,15 @@
    G_BEGIN_DECLS
    #define GPLUGIN_TCC_TYPE_LOADER (gplugin_tcc_loader_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginTccLoader, gplugin_tcc_loader, GPLUGIN_TCC, LOADER, GPluginLoader)
    +G_DECLARE_FINAL_TYPE(
    + GPluginTccLoader,
    + gplugin_tcc_loader,
    + GPLUGIN_TCC,
    + LOADER,
    + GPluginLoader)
    void gplugin_tcc_loader_register(GPluginNativePlugin *plugin);
    G_END_DECLS
    #endif /* GPLUGIN_TCC_LOADER_H */
    -
    --- a/tcc/gplugin-tcc-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/gplugin-tcc-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,18 +1,18 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #include "gplugin-tcc-plugin.h"
    @@ -49,7 +49,9 @@
    PROP_INFO,
    PROP_STATE
    };
    -static GParamSpec *properties[N_PROPERTIES] = {NULL,};
    +static GParamSpec *properties[N_PROPERTIES] = {
    + NULL,
    +};
    /* I hate forward declarations... */
    static void gplugin_tcc_plugin_iface_init(GPluginPluginInterface *iface);
    @@ -59,8 +61,7 @@
    gplugin_tcc_plugin,
    G_TYPE_OBJECT,
    0,
    - G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_PLUGIN, gplugin_tcc_plugin_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE(GPLUGIN_TYPE_PLUGIN, gplugin_tcc_plugin_iface_init));
    /******************************************************************************
    * GPluginPlugin Implementation
    @@ -74,15 +75,17 @@
    * Object Stuff
    *****************************************************************************/
    static void
    -gplugin_tcc_plugin_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *pspec)
    +gplugin_tcc_plugin_get_property(
    + GObject *obj,
    + guint param_id,
    + GValue *value,
    + GParamSpec *pspec)
    {
    GPluginTccPlugin *plugin = GPLUGIN_TCC_PLUGIN(obj);
    switch(param_id) {
    case PROP_TCC_STATE:
    - g_value_set_pointer(value,
    - gplugin_tcc_plugin_get_state(plugin));
    + g_value_set_pointer(value, gplugin_tcc_plugin_get_state(plugin));
    break;
    /* overrides */
    @@ -106,8 +109,11 @@
    }
    static void
    -gplugin_tcc_plugin_set_property(GObject *obj, guint param_id,
    - const GValue *value, GParamSpec *pspec)
    +gplugin_tcc_plugin_set_property(
    + GObject *obj,
    + guint param_id,
    + const GValue *value,
    + GParamSpec *pspec)
    {
    GPluginTccPlugin *plugin = GPLUGIN_TCC_PLUGIN(obj);
    @@ -140,7 +146,8 @@
    }
    static void
    -gplugin_tcc_plugin_finalize(GObject *obj) {
    +gplugin_tcc_plugin_finalize(GObject *obj)
    +{
    GPluginTccPlugin *plugin = GPLUGIN_TCC_PLUGIN(obj);
    g_clear_pointer(&plugin->s, tcc_delete);
    @@ -154,15 +161,18 @@
    }
    static void
    -gplugin_tcc_plugin_init(G_GNUC_UNUSED GPluginTccPlugin *plugin) {
    +gplugin_tcc_plugin_init(G_GNUC_UNUSED GPluginTccPlugin *plugin)
    +{
    }
    static void
    -gplugin_tcc_plugin_class_finalize(G_GNUC_UNUSED GPluginTccPluginClass *klass) {
    +gplugin_tcc_plugin_class_finalize(G_GNUC_UNUSED GPluginTccPluginClass *klass)
    +{
    }
    static void
    -gplugin_tcc_plugin_class_init(GPluginTccPluginClass *klass) {
    +gplugin_tcc_plugin_class_init(GPluginTccPluginClass *klass)
    +{
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    obj_class->get_property = gplugin_tcc_plugin_get_property;
    @@ -170,16 +180,16 @@
    obj_class->finalize = gplugin_tcc_plugin_finalize;
    properties[PROP_TCC_STATE] = g_param_spec_pointer(
    - "tcc-state", "tcc-state",
    + "tcc-state",
    + "tcc-state",
    "The TCC compilation context for the plugin",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    properties[PROP_MEM] = g_param_spec_pointer(
    - "memory", "memory",
    + "memory",
    + "memory",
    "The memory allocated for the symbol table for the plugin",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    - );
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
    g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    @@ -194,14 +204,15 @@
    * API
    *****************************************************************************/
    void
    -gplugin_tcc_plugin_register(GPluginNativePlugin *native) {
    +gplugin_tcc_plugin_register(GPluginNativePlugin *native)
    +{
    gplugin_tcc_plugin_register_type(G_TYPE_MODULE(native));
    }
    TCCState *
    -gplugin_tcc_plugin_get_state(GPluginTccPlugin *plugin) {
    +gplugin_tcc_plugin_get_state(GPluginTccPlugin *plugin)
    +{
    g_return_val_if_fail(GPLUGIN_TCC_IS_PLUGIN(plugin), NULL);
    return plugin->s;
    }
    -
    --- a/tcc/gplugin-tcc-plugin.h Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/gplugin-tcc-plugin.h Sat Sep 05 20:05:36 2020 -0500
    @@ -1,18 +1,18 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #ifndef GPLUGIN_TCC_PLUGIN_H
    #define GPLUGIN_TCC_PLUGIN_H
    @@ -25,17 +25,23 @@
    G_BEGIN_DECLS
    #define GPLUGIN_TCC_TYPE_PLUGIN (gplugin_tcc_plugin_get_type())
    -G_DECLARE_FINAL_TYPE(GPluginTccPlugin, gplugin_tcc_plugin, GPLUGIN_TCC, PLUGIN, GObject)
    +G_DECLARE_FINAL_TYPE(
    + GPluginTccPlugin,
    + gplugin_tcc_plugin,
    + GPLUGIN_TCC,
    + PLUGIN,
    + GObject)
    void gplugin_tcc_plugin_register(GPluginNativePlugin *native);
    TCCState *gplugin_tcc_plugin_get_state(GPluginTccPlugin *plugin);
    typedef GPluginPluginInfo *(*GPluginTccPluginQueryFunc)(GError **error);
    -typedef gboolean (*GPluginTccPluginLoadFunc)(GPluginNativePlugin *plugin, GError **error);
    -typedef gboolean (*GPluginTccPluginUnloadFunc)(GPluginNativePlugin *plugin, GError **error);
    +typedef gboolean (
    + *GPluginTccPluginLoadFunc)(GPluginNativePlugin *plugin, GError **error);
    +typedef gboolean (
    + *GPluginTccPluginUnloadFunc)(GPluginNativePlugin *plugin, GError **error);
    G_END_DECLS
    #endif /* GPLUGIN_TCC_PLUGIN_H */
    -
    --- a/tcc/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -21,7 +21,7 @@
    name_prefix : '',
    dependencies : [TCC, gplugin_dep],
    install : true,
    - install_dir : join_paths(get_option('libdir'), 'gplugin')
    + install_dir : get_option('libdir') / 'gplugin'
    )
    endif # tcc
    --- a/tcc/tests/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/tests/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -2,7 +2,7 @@
    e = executable('test-tcc-loader', 'test-tcc-loader.c',
    c_args : [
    - '-DTCC_LOADER_DIR="@0@"'.format(join_paths(meson.current_build_dir(), '..')),
    + '-DTCC_LOADER_DIR="@0@/.."'.format(meson.current_build_dir()),
    '-DTCC_PLUGIN_DIR="@0@/plugins"'.format(
    meson.current_source_dir()),
    ],
    --- a/tcc/tests/plugins/basic-plugin.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/tests/plugins/basic-plugin.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,29 +1,28 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #include <gplugin.h>
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "author1",
    - NULL
    - };
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + const gchar *const authors[] = {"author1", NULL};
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/native-basic-plugin",
    0x01020304,
    @@ -35,21 +34,22 @@
    "description", "description",
    "authors", authors,
    "website", "website",
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/tcc/tests/plugins/dependent.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/tests/plugins/dependent.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,49 +1,48 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #include <gplugin.h>
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const dependencies[] = {
    - "dependency1",
    - "dependency2",
    - NULL
    - };
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    + const gchar *const dependencies[] = {"dependency1", "dependency2", NULL};
    + /* clang-format off */
    return gplugin_plugin_info_new(
    "gplugin/native-dependent-plugin",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    "dependencies", dependencies,
    - NULL
    - );
    + NULL);
    + /* clang-format on */
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/tcc/tests/plugins/load-exception.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/tests/plugins/load-exception.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,42 +1,43 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #include <gplugin.h>
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    return gplugin_plugin_info_new(
    "gplugin/native-load-exception",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - NULL
    - );
    + NULL);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return FALSE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/tcc/tests/plugins/load-failed.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/tests/plugins/load-failed.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,42 +1,43 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #include <gplugin.h>
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    return gplugin_plugin_info_new(
    "gplugin/native-load-failed",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - NULL
    - );
    + NULL);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error) {
    +gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +{
    g_set_error(error, GPLUGIN_DOMAIN, 0, "expected error");
    return FALSE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    -
    --- a/tcc/tests/plugins/unload-failed.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/tests/plugins/unload-failed.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,42 +1,43 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #include <gplugin.h>
    #include <gplugin-native.h>
    G_MODULE_EXPORT GPluginPluginInfo *
    -gplugin_query(G_GNUC_UNUSED GError **error) {
    +gplugin_query(G_GNUC_UNUSED GError **error)
    +{
    return gplugin_plugin_info_new(
    "gplugin/native-unload-failed",
    GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    - NULL
    - );
    + NULL);
    }
    G_MODULE_EXPORT gboolean
    -gplugin_load(G_GNUC_UNUSED GPluginNativePlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    +gplugin_load(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error) {
    +gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +{
    g_set_error(error, GPLUGIN_DOMAIN, 0, "expected error");
    return FALSE;
    }
    -
    --- a/tcc/tests/test-tcc-loader.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/tcc/tests/test-tcc-loader.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,31 +1,31 @@
    /*
    - * Copyright (C) 2011-2013 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    - * 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 library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    *
    - * This program is distributed in the hope that it will be useful,
    + * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    *
    - * You should have received a copy of the GNU General Public License
    - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    */
    #include <glib.h>
    +
    #include <gplugin.h>
    -
    #include <gplugin/gplugin-loader-tests.h>
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_loader_tests_main(TCC_LOADER_DIR, TCC_PLUGIN_DIR, "c");
    return g_test_run();
    }
    -
    --- a/vala/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/vala/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    if get_option('vapi')
    - if not get_option('gobject-introspection')
    + if not get_option('introspection')
    error('Vala generation requires GObject Introspection.')
    endif
    @@ -8,7 +8,7 @@
    gplugin_vapi = gnome.generate_vapi('gplugin',
    sources : gplugin_gir[0],
    install : true,
    - gir_dirs : join_paths(meson.current_build_dir(), '..', 'gplugin'),
    + gir_dirs : meson.current_build_dir() / '..' / 'gplugin',
    )
    if get_option('gtk3')
    @@ -16,7 +16,7 @@
    sources : gplugin_gtk_gir[0],
    packages : [ 'gtk+-3.0' ],
    install : true,
    - gir_dirs : join_paths(meson.current_build_dir(), '..', 'gplugin'),
    + gir_dirs : meson.current_build_dir() / '..' / 'gplugin',
    )
    endif
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/genie-plugins/basic.gs Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,49 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +uses GPlugin
    +
    +class BasicPluginInfo : GPlugin.PluginInfo
    + construct()
    + authors : array of string = {"author1"}
    +
    + Object(
    + id: "gplugin/genie-basic-plugin",
    + abi_version: 0x01020304,
    + name: "basic plugin",
    + authors: authors,
    + category: "test",
    + version: "version",
    + license_id: "license",
    + summary: "summary",
    + website: "website",
    + description: "description"
    + )
    +
    +def gplugin_query(out error : Error) : GPlugin.PluginInfo
    + error = null
    +
    + return new BasicPluginInfo()
    +
    +def gplugin_load(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    +
    +def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/genie-plugins/dependent.gs Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,41 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +uses GPlugin;
    +
    +class DependentPluginInfo : GPlugin.PluginInfo
    + construct()
    + dependencies : array of string = {"dependency1", "dependency2"}
    +
    + Object(
    + id: "gplugin/genie-dependent-plugin",
    + dependencies: dependencies
    + )
    +
    +def gplugin_query(out error : Error) : GPlugin.PluginInfo
    + error = null
    +
    + return new DependentPluginInfo()
    +
    +def gplugin_load(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    +
    +def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return false
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/genie-plugins/load-exception.gs Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,38 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +uses GPlugin
    +
    +class LoadExceptionPluginInfo : GPlugin.PluginInfo
    + construct ()
    + Object(
    + id: "gplugin/genie-load-exception"
    + )
    +
    +def gplugin_query(out error : Error) : GPlugin.PluginInfo
    + error = null
    +
    + return new LoadExceptionPluginInfo()
    +
    +def gplugin_load(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = new Error(Quark.from_string("gplugin"), 0, "explode")
    +
    + return false
    +
    +def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/genie-plugins/load-failed.gs Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,38 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +uses GPlugin
    +
    +class LoadFailedPluginInfo : GPlugin.PluginInfo
    + construct ()
    + Object(
    + id: "gplugin/genie-load-failed"
    + )
    +
    +def gplugin_query(out error : Error) : GPlugin.PluginInfo
    + error = null
    +
    + return new LoadFailedPluginInfo()
    +
    +def gplugin_load(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return false
    +
    +def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/genie-plugins/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,17 @@
    +if get_option('vapi')
    + shared_library('genie-basic-plugin', 'basic.gs',
    + name_prefix : '',
    + dependencies : [gplugin_dep, gplugin_vapi])
    + shared_library('genie-dependent-plugin', 'dependent.gs',
    + name_prefix : '',
    + dependencies : [gplugin_dep, gplugin_vapi])
    + shared_library('load-exception-plugin', 'load-exception.gs',
    + name_prefix : '',
    + dependencies : [gplugin_dep, gplugin_vapi])
    + shared_library('load-failed-plugin', 'load-failed.gs',
    + name_prefix : '',
    + dependencies : [gplugin_dep, gplugin_vapi])
    + shared_library('unload-failed-plugin', 'unload-failed.gs',
    + name_prefix : '',
    + dependencies : [gplugin_dep, gplugin_vapi])
    +endif # vapi
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/genie-plugins/unload-failed.gs Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,38 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +uses GPlugin
    +
    +class UnloadFailedPluginInfo : GPlugin.PluginInfo
    + construct()
    + Object(
    + id: "gplugin/genie-unload-failed"
    + )
    +
    +def gplugin_query(out error : Error) : GPlugin.PluginInfo
    + error = null
    +
    + return new UnloadFailedPluginInfo()
    +
    +def gplugin_load(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    +
    +def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return false
    --- a/vala/tests/meson.build Thu Nov 07 23:09:55 2019 -0600
    +++ b/vala/tests/meson.build Sat Sep 05 20:05:36 2020 -0500
    @@ -9,6 +9,16 @@
    dependencies : [GLIB, GOBJECT, gplugin_dep])
    test('Vala loading', e)
    +e = executable('test-genie-loading', 'test-genie-loading.c',
    + include_directories : include_directories('.'),
    + c_args : [
    + '-DGENIE_PLUGIN_DIR="@0@/genie-plugins"'.format(meson.current_build_dir()),
    + ],
    + link_with : gplugin_loader_tests,
    + dependencies : [GLIB, GOBJECT, gplugin_dep])
    +test('Genie loading', e)
    +
    +subdir('genie-plugins')
    subdir('plugins')
    endif # vapi
    --- a/vala/tests/plugins/basic.vala Thu Nov 07 23:09:55 2019 -0600
    +++ b/vala/tests/plugins/basic.vala Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2019 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    --- a/vala/tests/plugins/dependent.vala Thu Nov 07 23:09:55 2019 -0600
    +++ b/vala/tests/plugins/dependent.vala Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2019 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    --- a/vala/tests/plugins/load-exception.vala Thu Nov 07 23:09:55 2019 -0600
    +++ b/vala/tests/plugins/load-exception.vala Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2019 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    --- a/vala/tests/plugins/load-failed.vala Thu Nov 07 23:09:55 2019 -0600
    +++ b/vala/tests/plugins/load-failed.vala Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2019 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    --- a/vala/tests/plugins/unload-failed.vala Thu Nov 07 23:09:55 2019 -0600
    +++ b/vala/tests/plugins/unload-failed.vala Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2019 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/test-genie-loading.c Sat Sep 05 20:05:36 2020 -0500
    @@ -0,0 +1,34 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
    + */
    +
    +#include <glib.h>
    +
    +#include <gplugin.h>
    +#include <gplugin/gplugin-loader-tests.h>
    +
    +/******************************************************************************
    + * Main
    + *****************************************************************************/
    +gint
    +main(gint argc, gchar **argv)
    +{
    + g_test_init(&argc, &argv, NULL);
    +
    + gplugin_loader_tests_main(NULL, GENIE_PLUGIN_DIR, "genie");
    +
    + return g_test_run();
    +}
    --- a/vala/tests/test-vala-loading.c Thu Nov 07 23:09:55 2019 -0600
    +++ b/vala/tests/test-vala-loading.c Sat Sep 05 20:05:36 2020 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2019 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -16,19 +16,19 @@
    */
    #include <glib.h>
    +
    #include <gplugin.h>
    -
    #include <gplugin/gplugin-loader-tests.h>
    /******************************************************************************
    * Main
    *****************************************************************************/
    gint
    -main(gint argc, gchar **argv) {
    +main(gint argc, gchar **argv)
    +{
    g_test_init(&argc, &argv, NULL);
    gplugin_loader_tests_main(NULL, VALA_PLUGIN_DIR, "vala");
    return g_test_run();
    }
    -