Imported Upstream version 2.79.2 upstream/2.79.2
authorKarol Lewandowski <k.lewandowsk@samsung.com>
Tue, 3 Sep 2024 08:11:47 +0000 (10:11 +0200)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Tue, 3 Sep 2024 08:11:47 +0000 (10:11 +0200)
166 files changed:
.gitlab-ci.yml
.gitlab-ci/run-black.sh [deleted file]
.gitlab-ci/run-flake8.sh [deleted file]
.gitlab-ci/run-shellcheck.sh [deleted file]
.gitlab-ci/run-tests.sh
.gitlab-ci/test-msvc.bat
.gitlab-ci/test-msys2.sh
.gitlab-ci/thorough-test-wrapper.sh [new file with mode: 0644]
.reuse/dep5
NEWS
docs/reference/gio/gdbus-codegen.rst
docs/reference/gio/gio-unix.toml.in [new file with mode: 0644]
docs/reference/gio/gio-win32.toml.in [new file with mode: 0644]
docs/reference/gio/gio.toml.in
docs/reference/gio/meson.build
docs/reference/gio/migrating-posix.md
docs/reference/gio/overview.md
docs/reference/gio/unix-mounts.md
docs/reference/girepository/girepository.toml.in
docs/reference/girepository/meson.build
docs/reference/girepository/migrating-gi.md [new file with mode: 0644]
docs/reference/girepository/urlmap.js [deleted file]
docs/reference/glib/glib-unix.toml.in [new file with mode: 0644]
docs/reference/glib/glib-win32.toml.in [new file with mode: 0644]
docs/reference/glib/glib.toml.in
docs/reference/glib/main-loop.md
docs/reference/glib/meson.build
docs/reference/glib/unix.md
docs/reference/glib/urlmap.js [deleted file]
docs/reference/glib/windows.md
docs/reference/gmodule/gmodule.toml.in
docs/reference/gmodule/urlmap.js [deleted file]
docs/reference/gobject/gobject.toml.in
docs/reference/gobject/urlmap.js [deleted file]
docs/reference/meson.build
docs/reference/urlmap.js [moved from docs/reference/gio/urlmap.js with 61% similarity]
fuzzing/fuzz_resolver.c
gio/gappinfo.c
gio/gapplication.c
gio/gapplication.h
gio/gdbus-2.0/codegen/codegen.py
gio/gdbus-2.0/codegen/codegen_main.py
gio/gdesktopappinfo.c
gio/gfiledescriptorbased.c
gio/gfiledescriptorbased.h
gio/giotypes.h
gio/glocalfile.c
gio/gresource-tool.c
gio/gsimpleiostream.c
gio/gtask.c
gio/gthreadedresolver-private.h [new file with mode: 0644]
gio/gthreadedresolver.c
gio/gthreadedresolver.h
gio/gunixfdlist.c
gio/gunixfdmessage.c
gio/gunixinputstream.c
gio/gunixmounts.c
gio/gunixoutputstream.c
gio/meson.build
gio/tests/codegen.py
gio/tests/defaultvalue.c
gio/tests/gapplication-example-cmdline.c
gio/tests/gapplication.c
gio/tests/memory-monitor-dbus.py.in
gio/tests/memory-monitor-portal.py.in
gio/tests/power-profile-monitor-dbus.py.in
gio/tests/power-profile-monitor-portal.py.in
gio/tests/resolver-parsing.c
girepository/cmph/brz.c
girepository/compiler/compiler.c [moved from girepository/tools/compiler.c with 57% similarity]
girepository/compiler/meson.build [new file with mode: 0644]
girepository/decompiler/decompiler.c [new file with mode: 0644]
girepository/decompiler/meson.build [new file with mode: 0644]
girepository/giarginfo.c
girepository/giarginfo.h
girepository/gibaseinfo-private.h
girepository/gibaseinfo.c
girepository/gibaseinfo.h
girepository/giboxedinfo.c [deleted file]
girepository/giboxedinfo.h [deleted file]
girepository/gicallableinfo.c
girepository/gienuminfo.c
girepository/gifieldinfo.c
girepository/gifunctioninfo.c
girepository/giinterfaceinfo.c
girepository/giobjectinfo.c
girepository/giobjectinfo.h
girepository/giregisteredtypeinfo.c
girepository/giregisteredtypeinfo.h
girepository/girepository-private.h
girepository/girepository.c
girepository/girepository.h
girepository/girffi.c
girepository/girffi.h
girepository/girmodule.c
girepository/girnode-private.h
girepository/girnode.c
girepository/giroffsets.c
girepository/girparser.c
girepository/girwriter.c
girepository/gistructinfo.c
girepository/gitypeinfo.c
girepository/gitypeinfo.h
girepository/gitypelib-internal.h
girepository/gitypelib.c
girepository/gitypelib.h
girepository/gitypes.h
girepository/giunioninfo.c
girepository/giunioninfo.h
girepository/givfuncinfo.c
girepository/inspector/inspector.c [new file with mode: 0644]
girepository/inspector/meson.build [new file with mode: 0644]
girepository/introspection/meson.build [new file with mode: 0644]
girepository/meson.build
girepository/tests/function-info.c [new file with mode: 0644]
girepository/tests/meson.build
girepository/tests/object-info.c [new file with mode: 0644]
girepository/tests/registered-type-info.c [new file with mode: 0644]
girepository/tests/repository-search-paths.c
girepository/tests/repository.c
girepository/tests/struct-info.c [new file with mode: 0644]
girepository/tests/test-common.c [new file with mode: 0644]
girepository/tests/test-common.h [new file with mode: 0644]
girepository/tests/throws.c [new file with mode: 0644]
girepository/tests/union-info.c [new file with mode: 0644]
glib/garray.c
glib/gconvert.c
glib/gdataset.c
glib/gdatasetprivate.h
glib/glib-private.c
glib/glib-private.h
glib/glib-unix.c
glib/glib-unix.h
glib/gnulib/gl_cv_func_frexpl_works/meson.build
glib/gnulib/gl_cv_func_printf_directive_a/meson.build
glib/gnulib/gl_cv_func_printf_directive_f/meson.build
glib/gnulib/gl_cv_func_printf_flag_zero/meson.build
glib/gnulib/gl_cv_func_printf_long_double/meson.build
glib/gnulib/gl_cv_func_printf_precision/meson.build
glib/gprintf.c
glib/gslice.h
glib/gspawn.c
glib/gstrfuncs.c
glib/gstrfuncs.h
glib/gtestutils.c
glib/gwin32.h
glib/meson.build
glib/tests/dataset.c
glib/tests/mapping.c
glib/tests/testing.c
glib/tests/unix.c
gobject/gobject.c
gobject/tests/param.c
gobject/tests/reference.c
gobject/tests/references.c
introspection/meson.build [deleted file]
meson.build
po/ka.po
po/ru.po
tests/black.sh [new file with mode: 0755]
tests/check-missing-install-tag.py [moved from .gitlab-ci/check-missing-install-tag.py with 52% similarity]
tests/flake8.sh [new file with mode: 0755]
tests/lint-common.sh [new file with mode: 0644]
tests/meson.build [new file with mode: 0644]
tests/reuse.sh [moved from .gitlab-ci/run-reuse.sh with 62% similarity]
tests/shellcheck.sh [new file with mode: 0755]

index 4edde21..d1d59c6 100644 (file)
@@ -61,14 +61,57 @@ variables:
   except:
     - tags
 
+# Some jobs take a long time and are unlikely to find failures (or will find
+# failures which are not merge-blockers to fix), so they’re executed on a weekly
+# schedule in order to save CI resources and speed up branch pipelines.
+# But for specific merge requests, one may still want to run them, so make
+# possible to run them manually.
+.only-schedules-or-manual:
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
+    # As per rule before, the following rules will only apply to non MR events
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+      when: always
+    - if: $CI_PIPELINE_SOURCE != "schedule"
+      when: manual
+      allow_failure: true
+
+# Some jobs should be runnable only on schedules and when triggered by a branch
+# in the origin repository, but without
+.only-schedules-or-manual-in-default-branch:
+  rules:
+    - if: $CI_PROJECT_PATH != "GNOME/glib" || $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
+      when: never
+    # As per rule before, the following rules will apply only to GNOME/glib:main
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+      when: always
+    - if: $CI_PIPELINE_SOURCE != "schedule"
+      when: manual
+      allow_failure: true
+
+# Some jobs run on CI runners don’t have much available resource.
+# Limit those jobs to only ones on the origin repository (GNOME/glib),
+# rather than people’s forks or if ran manually.
+.only-origin-or-manual:
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: never
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+      when: never
+    - if: $CI_PROJECT_NAMESPACE == "GNOME"
+      when: always
+    - if: $CI_PROJECT_NAMESPACE != "GNOME"
+      when: manual
+      allow_failure: true
+
 .build-linux:
   before_script:
     - bash .gitlab-ci/show-execution-environment.sh
     - cp -r $HOME/subprojects/* subprojects/
 
 # FIXME: Work around https://gitlab.com/gitlab-org/gitlab/-/issues/391756
-.only-default-with-git:
-  extends: .only-default
+.with-git:
   before_script:
    - rm -rf subprojects/gvdb
    - git config --global --add safe.directory "${PWD}"
@@ -78,41 +121,55 @@ variables:
     GIT_SUBMODULE_DEPTH: 1
 
 style-check-advisory:
-  extends: .only-default-with-git
+  extends:
+    - .only-default
+    - .with-git
   image: $DEBIAN_IMAGE
   stage: style-check
   allow_failure: true
   script:
-    - .gitlab-ci/run-style-check-diff.sh
-    - .gitlab-ci/run-check-todos.sh
+    - failed=
+    - .gitlab-ci/run-style-check-diff.sh || failed=1
+    - .gitlab-ci/run-check-todos.sh || failed=1
+    - test -z "$failed"
 
 sh-and-py-check:
-  extends: .only-default
+  extends:
+    - .only-default
+    - .with-git
   image: $DEBIAN_IMAGE
   stage: style-check
   allow_failure: false
   script:
-    - git config --global --add safe.directory "${PWD}"
-    - .gitlab-ci/run-shellcheck.sh
-    - .gitlab-ci/run-black.sh
-    - .gitlab-ci/run-flake8.sh
+    - failed=
+    - tests/shellcheck.sh || failed=1
+    - tests/black.sh || failed=1
+    - tests/flake8.sh || failed=1
+    - test -z "$failed"
+  variables:
+    LINT_WARNINGS_ARE_ERRORS: '1'
   only:
     changes:
       - "**/*.py"
       - "**/*.sh"
 
 style-check-mandatory:
-  extends: .only-default-with-git
+  extends:
+    - .only-default
+    - .with-git
   image: $DEBIAN_IMAGE
   stage: style-check
   allow_failure: false
   script:
-    - .gitlab-ci/run-reuse.sh
+    - tests/reuse.sh
+  variables:
+    LINT_WARNINGS_ARE_ERRORS: '1'
 
 fedora-x86_64:
   extends:
     - .build-linux
     - .only-default-and-merges
+    - .with-git
   image: $FEDORA_IMAGE
   stage: build
   needs: []
@@ -141,9 +198,12 @@ fedora-x86_64:
     # can then pull it from there — see https://gitlab.gnome.org/GNOME/gtk/-/blob/docs-gtk-org/README.md
     - mkdir -p _reference/
     - mv _build/docs/reference/glib/glib/ _reference/glib/
+    - mv _build/docs/reference/glib/glib-unix/ _reference/glib-unix/
     - mv _build/docs/reference/gmodule/gmodule/ _reference/gmodule/
     - mv _build/docs/reference/gobject/gobject/ _reference/gobject/
     - mv _build/docs/reference/gio/gio/ _reference/gio/
+    - mv _build/docs/reference/gio/gio-unix/ _reference/gio-unix/
+    - mv _build/docs/reference/girepository/girepository/ _reference/girepository/
   artifacts:
     reports:
       junit:
@@ -163,6 +223,7 @@ debian-stable-x86_64:
   extends:
     - .build-linux
     - .only-default
+    - .with-git
   image: $DEBIAN_IMAGE
   stage: build
   needs: []
@@ -193,13 +254,19 @@ debian-stable-x86_64:
 
 hurd-i386:
   extends:
-    - .only-schedules
+    - .only-schedules-or-manual
+    - .with-git
   stage: build
   tags:
     - hurd
   needs: []
   script:
-    - meson setup ${MESON_COMMON_OPTIONS}
+    # FIXME: We can’t use ${MESON_COMMON_OPTIONS} here because the Hurd runner
+    # has Meson 1.3 installed. See the comment below about the same problem on
+    # FreeBSD.
+    - meson setup
+            --buildtype debug
+            --wrap-mode=nodownload
             --werror
             --default-library=both
             --prefix=$HOME/glib-installed
@@ -224,7 +291,8 @@ hurd-i386:
 muslc-alpine-x86_64:
   extends:
     - .build-linux
-    - .only-schedules
+    - .only-schedules-or-manual
+    - .with-git
   image: $ALPINE_IMAGE
   stage: build
   needs: []
@@ -255,7 +323,7 @@ muslc-alpine-x86_64:
 installed-tests:
   extends:
     - .build-linux
-    - .only-schedules
+    - .only-schedules-or-manual
   image: $FEDORA_IMAGE
   stage: build
   needs: []
@@ -293,10 +361,13 @@ installed-tests:
 G_DISABLE_ASSERT:
   extends:
     - .build-linux
-    - .only-schedules
+    - .only-schedules-or-manual
+    - .with-git
   image: $FEDORA_IMAGE
   stage: build
   needs: []
+  variables:
+    MESON_TEST_TIMEOUT_MULTIPLIER: 15
   script:
     - meson setup ${MESON_COMMON_OPTIONS}
             --werror
@@ -307,7 +378,8 @@ G_DISABLE_ASSERT:
             -Dintrospection=enabled
             _build
     - meson compile -C _build
-    - bash -x ./.gitlab-ci/run-tests.sh
+    # Also take the opportunity to run the thorough tests (which are slow)
+    - bash -x ./.gitlab-ci/run-tests.sh --setup thorough
   artifacts:
     reports:
       junit:
@@ -324,7 +396,8 @@ G_DISABLE_ASSERT:
 valgrind:
   extends:
     - .build-linux
-    - .only-schedules
+    - .only-schedules-or-manual
+    - .with-git
   image: $FEDORA_IMAGE
   stage: analysis
   needs: []
@@ -405,7 +478,7 @@ cross-mingw64:
       - _build/gobject/libgobject-2.0-0.dll
 
 msys2-mingw32:
-  extends: .only-default
+  extends: .only-default-and-merges
   stage: build
   tags:
     - win32-ps
@@ -430,9 +503,10 @@ msys2-mingw32:
     paths:
       - _build/meson-logs
       - _coverage/
+      - _reference/
 
 msys2-clang64:
-  extends: .only-schedules
+  extends: .only-schedules-or-manual
   stage: build
   tags:
     - win32-ps
@@ -513,7 +587,7 @@ vs2017-x64-static:
       - _build/meson-logs
 
 freebsd-13-x86_64:
-  extends: .only-origin
+  extends: .only-origin-or-manual
   stage: build
   tags:
     - freebsd-13
@@ -549,7 +623,7 @@ freebsd-13-x86_64:
       - "_build/meson-logs"
 
 macos-x86_64:
-  extends: .only-origin
+  extends: .only-origin-or-manual
   stage: build
   tags:
     - macosintel
@@ -631,7 +705,7 @@ coverage:
 scan-build:
   extends:
     - .build-linux
-    - .only-schedules
+    - .only-schedules-or-manual
   image: $FEDORA_IMAGE
   stage: analysis
   needs: []
@@ -658,7 +732,7 @@ scan-build:
 .coverity:
   extends:
     - .build-linux
-    - .only-schedules
+    - .only-schedules-or-manual-in-default-branch
   image: $COVERITY_IMAGE
   stage: analysis
   needs: []
@@ -719,12 +793,14 @@ dist-job:
     - tar -c -J -f "gmodule-docs-$CI_COMMIT_TAG.tar.xz" -C _build/docs/reference/gmodule gmodule
     - tar -c -J -f "gobject-docs-$CI_COMMIT_TAG.tar.xz" -C _build/docs/reference/gobject gobject
     - tar -c -J -f "gio-docs-$CI_COMMIT_TAG.tar.xz" -C _build/docs/reference/gio gio
+    - tar -c -J -f "girepository-docs-$CI_COMMIT_TAG.tar.xz" -C _build/docs/reference/girepository girepository
   artifacts:
     paths:
       - "${CI_PROJECT_DIR}/_build/glib-docs-$CI_COMMIT_TAG.tar.xz"
       - "${CI_PROJECT_DIR}/_build/gmodule-docs-$CI_COMMIT_TAG.tar.xz"
       - "${CI_PROJECT_DIR}/_build/gobject-docs-$CI_COMMIT_TAG.tar.xz"
       - "${CI_PROJECT_DIR}/_build/gio-docs-$CI_COMMIT_TAG.tar.xz"
+      - "${CI_PROJECT_DIR}/_build/girepository-docs-$CI_COMMIT_TAG.tar.xz"
       - "${CI_PROJECT_DIR}/_build/meson-dist/glib-*.tar.xz"
 
 issue-bot:
diff --git a/.gitlab-ci/run-black.sh b/.gitlab-ci/run-black.sh
deleted file mode 100755 (executable)
index fdeaf16..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# shellcheck disable=SC2046
-black --diff --check $(git ls-files '*.py')
diff --git a/.gitlab-ci/run-flake8.sh b/.gitlab-ci/run-flake8.sh
deleted file mode 100755 (executable)
index 5675a01..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# Disable formatting warnings in flake8, as we use `black` to handle that.
-formatting_warnings=E101,E111,E114,E115,E116,E117,E12,E13,E2,E3,E401,E5,E70,W1,W2,W3,W5
-
-# shellcheck disable=SC2046
-flake8 --max-line-length=88 --ignore="$formatting_warnings" $(git ls-files '*.py')
diff --git a/.gitlab-ci/run-shellcheck.sh b/.gitlab-ci/run-shellcheck.sh
deleted file mode 100755 (executable)
index abf2e5e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# Ignoring third-party directories that we don't want to parse
-# shellcheck disable=SC2046
-shellcheck $(git ls-files '*.sh' | grep -Ev "glib/libcharset|glib/dirent")
index 1189493..6293850 100755 (executable)
@@ -2,8 +2,6 @@
 
 set -ex
 
-./.gitlab-ci/check-missing-install-tag.py _build
-
 meson test -v \
         -C _build \
         --timeout-multiplier "${MESON_TEST_TIMEOUT_MULTIPLIER}" \
index 73f972a..02f8b07 100644 (file)
@@ -14,23 +14,13 @@ set args=%args:~1%
 :: FIXME: make warnings fatal
 pip3 install --upgrade --user meson==1.2.3 packaging==23.2  || goto :error
 meson setup %args% _build || goto :error
-python .gitlab-ci/check-missing-install-tag.py _build || goto :error
 meson compile -C _build || goto :error
 
 meson test -v -C _build --timeout-multiplier %MESON_TEST_TIMEOUT_MULTIPLIER% || goto :error
 meson test -v -C _build --timeout-multiplier %MESON_TEST_TIMEOUT_MULTIPLIER% --setup=unstable_tests --suite=failing --suite=flaky
 
-:: Workaround meson issue https://github.com/mesonbuild/meson/issues/9894
-python -c "n = '_build/meson-logs/testlog.junit.xml'; c = open(n, 'rb').read().replace(b'\x1b', b''); open(n, 'wb').write(c)" || goto :error
-python -c "n = '_build/meson-logs/testlog-unstable_tests.junit.xml'; c = open(n, 'rb').read().replace(b'\x1b', b''); open(n, 'wb').write(c)"
-
 :: FIXME: can we get code coverage support?
 
 goto :EOF
 :error
-
-:: Workaround meson issue https://github.com/mesonbuild/meson/issues/9894
-python -c "n = '_build/meson-logs/testlog.junit.xml'; c = open(n, 'rb').read().replace(b'\x1b', b''); open(n, 'wb').write(c)"
-python -c "n = '_build/meson-logs/testlog-unstable_tests.junit.xml'; c = open(n, 'rb').read().replace(b'\x1b', b''); open(n, 'wb').write(c)"
-
 exit /b 1
index 6ee6f12..b5a6d9a 100755 (executable)
@@ -11,10 +11,13 @@ pacman --noconfirm -S --needed \
     lcov \
     "${MINGW_PACKAGE_PREFIX}"-ccache \
     "${MINGW_PACKAGE_PREFIX}"-gettext \
+    "${MINGW_PACKAGE_PREFIX}"-gi-docgen \
+    "${MINGW_PACKAGE_PREFIX}"-gobject-introspection \
     "${MINGW_PACKAGE_PREFIX}"-libffi \
     "${MINGW_PACKAGE_PREFIX}"-meson \
     "${MINGW_PACKAGE_PREFIX}"-pcre2 \
     "${MINGW_PACKAGE_PREFIX}"-python3 \
+    "${MINGW_PACKAGE_PREFIX}"-python-docutils \
     "${MINGW_PACKAGE_PREFIX}"-python-pip \
     "${MINGW_PACKAGE_PREFIX}"-toolchain \
     "${MINGW_PACKAGE_PREFIX}"-zlib \
@@ -33,7 +36,12 @@ DIR="$(pwd)"
 export PATH CFLAGS
 
 # shellcheck disable=SC2086
-meson setup ${MESON_COMMON_OPTIONS} --werror _build
+meson setup ${MESON_COMMON_OPTIONS} \
+    --werror \
+    -Ddocumentation=true \
+    -Dintrospection=enabled \
+    -Dman-pages=enabled \
+    _build
 
 meson compile -C _build
 
@@ -59,3 +67,9 @@ if [[ "$CFLAGS" == *"-coverage"* ]]; then
         --capture \
         --output-file "${DIR}/_coverage/${CI_JOB_NAME}.lcov"
 fi
+
+# Copy the built documentation to an artifact directory. The build for docs.gtk.org
+# can then pull it from there — see https://gitlab.gnome.org/GNOME/gtk/-/blob/docs-gtk-org/README.md
+mkdir -p _reference/
+mv _build/docs/reference/glib/glib-win32/ _reference/glib-win32/
+mv _build/docs/reference/gio/gio-win32/ _reference/gio-win32/
\ No newline at end of file
diff --git a/.gitlab-ci/thorough-test-wrapper.sh b/.gitlab-ci/thorough-test-wrapper.sh
new file mode 100644 (file)
index 0000000..2bb149e
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2024 GNOME Foundation, Inc.
+#
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Original author: Philip Withnall
+
+set -e
+
+# If the test is run under Python (e.g. the first argument to this script is
+# /usr/bin/python3) or if it’s the special xmllint test in GLib, then don’t
+# pass the GTest `-m thorough` argument to it.
+if [[ "$1" == *"python"* ||
+      "$1" == *"xmllint" ]]; then
+  args=()
+else
+  # See the documentation for g_test_init()
+  args=("-m" "thorough")
+fi
+
+exec "$@" "${args[@]}"
\ No newline at end of file
index 40fa0f8..72a4790 100644 (file)
@@ -47,4 +47,16 @@ License: CC-BY-SA-3.0
 # girepository/cmph/README-CMPH-IMPORT.txt.
 Files: girepository/cmph/*
 Copyright: CMPH contributors
-License: LGPL-2.1-only or MPL-1.1
\ No newline at end of file
+License: LGPL-2.1-only or MPL-1.1
+
+# gnulib is a copylib. Adding copyright/license data to the files there would
+# cause divergence from upstream. See glib/gnulib/README.
+Files: glib/gnulib/*
+Copyright: gnulib contributors
+License: LGPL-2.1-or-later
+
+# libcharset is a copylib. Adding copyright/license data to the files there
+# would cause divergence from upstream. See glib/libcharset/README.
+Files: glib/libcharset/*
+Copyright: libcharset contributors
+License: LGPL-2.1-or-later
\ No newline at end of file
diff --git a/NEWS b/NEWS
index 105212a..cdb2044 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,124 @@
+Overview of changes in GLib 2.79.2, 2024-02-12
+==============================================
+
+* More work to reduce lock contention and improve performance in GObject (#743,
+  !3869, !3873, work by Thomas Haller)
+
+* More API changes to libgirepository, which is now stable as of this release
+  (#3155, #3217, #3218, #3231, #3234, #3243, #3244, #3245, #3246, work by
+  Philip Chimento, Evan Welsh, Philip Withnall)
+
+* Import `g-ir-compiler`, `g-ir-generate` and `g-ir-inspect` from
+  gobject-introspection.git and update them to work with girepository-2.0,
+  renaming them to `gi-compile-repository`, `gi-decompile-typelib` and
+  `gi-inspect-typelib` (see docs/reference/girepository/migrating-gi.md) (!3853,
+  !3909, work by Evan Welsh, Philip Withnall)
+
+* Add new `GLibUnix-2.0.gir`, `GLibWin32-2.0.gir`, `GioUnix-2.0.gir` and
+  `GioWin32-2.0.gir` GIRs which contain platform specific APIs, and are the
+  preferred way for third parties to access those APIs in future; although
+  platform specific APIs which were already exposed in `GLib-2.0.gir` and
+  `Gio-2.0.gir` continue to be listed there; the underlying `.so` files have not
+  changed (!3892, work by Philip Withnall)
+
+* Bugs fixed:
+  - #743 GLib weak refs depend on cascade of locks, including global ones, which
+    makes them non-scalable
+  - #2887 memory-monitor-dbus.test fails in installed-tests suite (Philip
+    Withnall)
+  - #3198 Support --version in standard GApplication command line arguments
+    (Maxim Moskalets)
+  - #3217 Feedback on gobject-introspection: APIs for stack-allocated
+    introspection info (Philip Withnall)
+  - #3218 Segfault in gi_function_info_prep_invoker (Philip Withnall)
+  - #3231 New functions in the glib introspection data (Philip Withnall)
+  - #3234 Reference cycle between GIRepository and GIBaseInfos cached by it
+    (Philip Withnall)
+  - #3236 threaded_resolver_worker_cb leaks memory when lookup fails and
+    connection is already canceled (Philip Withnall)
+  - #3238 Python packaging module is missing on Hurd CI runner (Philip Withnall)
+  - #3240 Missing preconditions checks in GArray (Tobias Stoeckmann)
+  - #3242 Memory leak in gresources over libelf (Maxim Moskalets)
+  - #3243 Feedback on girepository 2.0: Naming of get_type_info vs load_type
+    (Philip Withnall)
+  - #3244 Feedback on girepository 2.0: Where to find uninstalled typelibs
+    (Philip Withnall)
+  - #3245 Feedback on girepository 2.0: GIBoxedInfo's place in the type
+    hierarchy (Philip Withnall)
+  - #3246 Wrong out parameter type in
+    gi_object_info_find_method_using_interfaces (Philip Withnall)
+  - #3247 safe_closefrom(), safe_fdwalk_set_cloexec() as public API (Simon
+    McVittie)
+  - !3797 Refactor GIRepository GIR generation to avoid cyclical dependency
+  - !3807 gprintf/gstrfuncs: Improve and port doc comments to gi-docgen
+  - !3824 gconvert: match GNU iconv behaviour on FreeBSD
+  - !3838 build: Add thorough test setup
+  - !3843 Add more test coverage for girepository
+  - !3845 gunixmounts: Use libmnt_monitor API for monitoring
+  - !3847 ci: Add ability to run manually some specific jobs
+  - !3848 Fix build with introspection on Windows
+  - !3849 girepository: Remove GI_FUNCTION_THROWS and GI_VFUNC_THROWS flags
+  - !3850 [th/strdup-in-ascii-strdown] glib: use g_strdup() in
+    g_ascii_strdown(),g_ascii_strup()
+  - !3851 ci: Fix post-merge CI pipelines
+  - !3853 girepository: Update gir-compiler and use it to compile GIRs
+  - !3854 girnode: Document ownership and element types of internal structs
+  - !3855 gitypelib: Replace multiple constructors with
+    gi_typelib_new_from_bytes()
+  - !3856 girepository: Drop gi_repository_get_default()
+  - !3859 [th/glib-private-const] glib: return const pointer from
+    glib__private__()
+  - !3860 tests: Fix typo in memory-monitor-portal.py.in
+  - !3861 girepository: Fix a memory leak of a mapped file
+  - !3865 [th/test-weak-notify] gobject/tests: add test checking that GWeakRef
+    is cleared in GWeakNotify
+  - !3866 [th/gobject-carray-comment] gobject: remove obsolete code comment
+    about CArray
+  - !3868 Link to the main context tutorial from the main loop docs
+  - !3869 [th/optimize-weak-ref-list] rework GObject's `WeakRefData` to track
+    references in an array instead of GSList
+  - !3870 Revert "Don't skip dbus-codegen tests on Win32"
+  - !3871 docs: Fix include path for the build
+  - !3872 gio: tests: Use slightly more explicit assert functions
+  - !3873 [th/datalist-shrink] shrink the interal buffer of `GData`
+  - !3874 Don't skip dbus-codegen tests on Win32
+  - !3876 build: Only override g-ir-compiler when GIR generation is enabled
+  - !3877 Various girepository fixes
+  - !3879 [th/gdataset-comment] gdataset: add code comment to
+    g_datalist_get_data()
+  - !3881 docs: Add migration guide for libgirepository
+  - !3886 codegen: Use `-` instead of `stdout` for output to stdout
+  - !3887 gtestutils: Ensure test_data is freed even if a test is skipped
+  - !3888 gitypes: Fix integer values of GIInfoType and add unit tests for
+    GIUnionInfo
+  - !3892 introspection: Generate separate GIR files and documentation for
+    platform specific APIs
+  - !3893 glocalfile: Support statvfs.f_type
+  - !3894 Minor fixes/docs changes to GFileDescriptorBased and GTask
+  - !3895 [th/meson-werror-fixes] some fixes for meson detection failure with
+    -Werror
+  - !3896 reuse: Add dep5 lines for gnulib and libcharset
+  - !3897 reuse: Fix screen-scraping expression for version 2.x
+  - !3898 Incorporate some lint checks into `meson test`
+  - !3900 gitypelib: Switch to refcounting
+  - !3901 girepository: Add length ‘out’ arguments to several getter methods
+  - !3902 gicallableinfo: Clarify docs for callables with no return type
+  - !3903 gibaseinfo: Rename gi_info_new() to gi_base_info_new()
+  - !3904 [th/meson-werror-fixes-2] more workarounds for compiler warnings in
+    meson compiler checks
+  - !3909 Rename g-ir-generate and g-ir-inspect and update to girepository-2.0
+  - !3911 glib/tests/unix: Mostly pass O_CLOEXEC to g_unix_pipe_open()
+  - !3912 glib-unix: Fix reference to FD_CLOEXEC in docs for g_unix_pipe_open()
+  - !3913 cmph: Fix a typo
+  - !3914 Revert "ci: Remove not-printable chars from generated junit file"
+  - !3916 tests: Skip lint tests if bash is not available
+  - !3917 ci: Build and tar libgirepository documentation
+
+* Translation updates:
+  - Georgian (Ekaterine Papava)
+  - Russian (Artur S0)
+
+
 Overview of changes in GLib 2.79.1, 2024-01-22
 ==============================================
 
index 4a4c07f..2f97365 100644 (file)
@@ -296,6 +296,22 @@ The following options are supported:
   is not allowed along with ``--output``, because the latter is used to generate
   only one file.
 
+  Since GLib 2.80, if *OUTFILE* is the literal string ``-``, the header
+  or source code will be written to standard output.
+
+  For ``--body`` and ``--interface-info-body``, the generated code will not
+  automatically ``#include`` a corresponding header file when writing to
+  standard output, because there is no obvious name for that header file.
+  This might make it necessary to use ``cc -include foo.h``, or generate a
+  filename like ``foo-impl.h`` and ``#include`` it into a wrapper ``.c`` file.
+
+  For ``--header`` and ``--interface-info-header``, there is no obvious
+  name for a traditional multiple-inclusion guard when writing to standard
+  output, so using the ``--pragma-once`` option is recommended.
+
+  In the rare situation that the intended output filename starts with ``-``,
+  it should be prefixed with ``./``.
+
 ``--annotate`` *ELEMENT* *KEY* *VALUE*
 
   Used to inject D-Bus annotations into the given XML files. It can be used with
@@ -802,4 +818,4 @@ Please send bug reports to either the distribution bug tracker or the
 SEE ALSO
 --------
 
-`gdbus(1) <man:gdbus(1)>`_
\ No newline at end of file
+`gdbus(1) <man:gdbus(1)>`_
diff --git a/docs/reference/gio/gio-unix.toml.in b/docs/reference/gio/gio-unix.toml.in
new file mode 100644 (file)
index 0000000..3a9deb3
--- /dev/null
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# Copyright 2023 Matthias Clasen
+# Copyright 2023 Philip Withnall
+
+[library]
+name = "GioUnix"
+version = "@VERSION@"
+browse_url = "https://gitlab.gnome.org/GNOME/glib/"
+repository_url = "https://gitlab.gnome.org/GNOME/glib.git"
+website_url = "https://www.gtk.org"
+docs_urls = "https://docs.gtk.org/gio-unix/"
+authors = "GLib Development Team"
+license = "LGPL-2.1-or-later"
+description = "Unix-specific APIs in Gio"
+dependencies = [ "GLib-2.0", "GModule-2.0", "GObject-2.0", "Gio-2.0" ]
+devhelp = true
+search_index = true
+
+  [dependencies."GLib-2.0"]
+  name = "GLib"
+  description = "The base utility library"
+  docs_url = "https://docs.gtk.org/glib/"
+
+  [dependencies."GModule-2.0"]
+  name = "GModule"
+  description = "Portable API for dynamically loading modules"
+  docs_url = "https://docs.gtk.org/gmodule/"
+
+  [dependencies."GObject-2.0"]
+  name = "GObject"
+  description = "The base type system library"
+  docs_url = "https://docs.gtk.org/gobject/"
+
+  [dependencies."Gio-2.0"]
+  name = "Gio"
+  description = "A library of useful classes for I/O, networking and IPC"
+  docs_url = "https://docs.gtk.org/gio/"
+
+[theme]
+name = "basic"
+show_index_summary = true
+show_class_hierarchy = true
+
+[extra]
+urlmap_file = "../urlmap.js"
+# The same order will be used when generating the index
+content_files = [
+  "unix-mounts.md",
+]
+content_images = []
diff --git a/docs/reference/gio/gio-win32.toml.in b/docs/reference/gio/gio-win32.toml.in
new file mode 100644 (file)
index 0000000..84e75ce
--- /dev/null
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# Copyright 2023 Matthias Clasen
+# Copyright 2023 Philip Withnall
+
+[library]
+name = "GioWin32"
+version = "@VERSION@"
+browse_url = "https://gitlab.gnome.org/GNOME/glib/"
+repository_url = "https://gitlab.gnome.org/GNOME/glib.git"
+website_url = "https://www.gtk.org"
+docs_urls = "https://docs.gtk.org/gio-win32/"
+authors = "GLib Development Team"
+license = "LGPL-2.1-or-later"
+description = "Windows-specific APIs in Gio"
+dependencies = [ "GLib-2.0", "GModule-2.0", "GObject-2.0", "Gio-2.0" ]
+devhelp = true
+search_index = true
+
+  [dependencies."GLib-2.0"]
+  name = "GLib"
+  description = "The base utility library"
+  docs_url = "https://docs.gtk.org/glib/"
+
+  [dependencies."GModule-2.0"]
+  name = "GModule"
+  description = "Portable API for dynamically loading modules"
+  docs_url = "https://docs.gtk.org/gmodule/"
+
+  [dependencies."GObject-2.0"]
+  name = "GObject"
+  description = "The base type system library"
+  docs_url = "https://docs.gtk.org/gobject/"
+
+  [dependencies."Gio-2.0"]
+  name = "Gio"
+  description = "A library of useful classes for I/O, networking and IPC"
+  docs_url = "https://docs.gtk.org/gio/"
+
+[theme]
+name = "basic"
+show_index_summary = true
+show_class_hierarchy = true
+
+[extra]
+urlmap_file = "../urlmap.js"
+# The same order will be used when generating the index
+content_files = []
+content_images = []
index 40559fa..d9c8a83 100644 (file)
@@ -37,7 +37,7 @@ show_index_summary = true
 show_class_hierarchy = true
 
 [extra]
-urlmap_file = "urlmap.js"
+urlmap_file = "../urlmap.js"
 # The same order will be used when generating the index
 content_files = [
   "overview.md",
@@ -47,7 +47,6 @@ content_files = [
   "error.md",
 
   "pollable-utils.md",
-  "unix-mounts.md",
 
   "dbus-error.md",
   "dbus-introspection.md",
index ecef666..3bb321e 100644 (file)
@@ -38,8 +38,11 @@ if get_option('documentation') and enable_gir
     'overview.md',
     'pollable-utils.md',
     'tls-overview.md',
+  ]
+  expand_content_unix_files = [
     'unix-mounts.md',
   ]
+  expand_content_win32_files = []
 
   gio_toml = configure_file(input: 'gio.toml.in', output: 'gio.toml', configuration: toml_conf)
 
@@ -61,4 +64,48 @@ if get_option('documentation') and enable_gir
     install_dir: docs_dir,
     install_tag: 'doc',
   )
+
+  if host_system == 'windows'
+    gio_win32_toml = configure_file(input: 'gio-win32.toml.in', output: 'gio-win32.toml', configuration: toml_conf)
+
+    custom_target('gio-win32-docs',
+      input: [ gio_win32_toml, gio_win32_gir[0] ],
+      output: 'gio-win32',
+      command: [
+        gidocgen,
+        'generate',
+        gidocgen_common_args,
+        '--config=@INPUT0@',
+        '--output-dir=@OUTPUT@',
+        '--content-dir=@0@'.format(meson.current_source_dir()),
+        '@INPUT1@',
+      ],
+      build_by_default: true,
+      depend_files: expand_content_win32_files,
+      install: true,
+      install_dir: docs_dir,
+      install_tag: 'doc',
+    )
+  else
+    gio_unix_toml = configure_file(input: 'gio-unix.toml.in', output: 'gio-unix.toml', configuration: toml_conf)
+
+    custom_target('gio-unix-docs',
+      input: [ gio_unix_toml, gio_unix_gir[0] ],
+      output: 'gio-unix',
+      command: [
+        gidocgen,
+        'generate',
+        gidocgen_common_args,
+        '--config=@INPUT0@',
+        '--output-dir=@OUTPUT@',
+        '--content-dir=@0@'.format(meson.current_source_dir()),
+        '@INPUT1@',
+      ],
+      build_by_default: true,
+      depend_files: expand_content_unix_files,
+      install: true,
+      install_dir: docs_dir,
+      install_tag: 'doc',
+    )
+  endif
 endif
index 14efa1e..ded78f1 100644 (file)
@@ -6,12 +6,16 @@ SPDX-FileCopyrightText: 2007 Matthias Clasen
 
 ## Comparison of POSIX and GIO concepts
 
-| POSIX                 | GIO                                                 |
-|-----------------------|-----------------------------------------------------|
-| `char *path`          | [iface@Gio.File]                                    |
-| `struct stat *buf`    | [class@Gio.FileInfo]                                |
-| `struct statvfs *buf` | [class@Gio.FileInfo]                                |
-| `int fd`              | [class@Gio.InputStream] or [class@Gio.OutputStream] |
-| `DIR *`               | [class@Gio.FileEnumerator]                          |
-| `fstab entry`         | [struct@Gio.UnixMountPoint]                         |
-| `mtab entry`          | [struct@Gio.UnixMountEntry]                         |
+The final two entries in this table require including `gio-unix-2.0.pc` as well
+as `gio-2.0.pc` in your build (or, in GIR namespace terms, `GioUnix-2.0` as well
+as `Gio-2.0`).
+
+| POSIX                 | GIO                                                         |
+|-----------------------|-------------------------------------------------------------|
+| `char *path`          | [iface@Gio.File]                                            |
+| `struct stat *buf`    | [class@Gio.FileInfo]                                        |
+| `struct statvfs *buf` | [class@Gio.FileInfo]                                        |
+| `int fd`              | [class@Gio.InputStream] or [class@Gio.OutputStream]         |
+| `DIR *`               | [class@Gio.FileEnumerator]                                  |
+| `fstab entry`         | [`GUnixMountPoint`](../gio-unix/struct.UnixMountPoint.html) |
+| `mtab entry`          | [`GUnixMountEntry`](../gio-unix/struct.UnixMountEntry.html) |
index 2e496e6..824a2a2 100644 (file)
@@ -234,8 +234,8 @@ information on how to use pkg-config to compile your application.
 
 If you are using GIO on UNIX-like systems, you may want to use UNIX-specific
 GIO interfaces such as `GUnixInputStream`, `GUnixOutputStream`, `GUnixMount`
-or `GDesktopAppInfo`. To do so, use the `gio-unix-2.0.pc` file instead of
-`gio-2.0.pc`.
+or `GDesktopAppInfo`. To do so, use the `gio-unix-2.0.pc` file as well as
+`gio-2.0.pc` (or, in GIR namespace terms, `GioUnix-2.0` as well as `Gio-2.0`).
 
 ## Running GIO applications
 
index dc45010..404df3e 100644 (file)
@@ -9,27 +9,27 @@ Routines for managing mounted UNIX mount points and paths.
 
 Note that `<gio/gunixmounts.h>` belongs to the UNIX-specific GIO
 interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
-file when using it.
+file or the `GioUnix-2.0` GIR namespace when using it.
 
 There are three main classes:
 
- * [struct@Gio.UnixMountEntry]
- * [struct@Gio.UnixMountPoint]
- * [class@Gio.UnixMountMonitor]
+ * [struct@GioUnix.MountEntry]
+ * [struct@GioUnix.MountPoint]
+ * [class@GioUnix.MountMonitor]
 
 Various helper functions for querying mounts:
 
- * [func@Gio.unix_mount_points_get]
- * [func@Gio.UnixMountPoint.at]
- * [func@Gio.unix_mounts_get]
- * [func@Gio.unix_mount_at]
- * [func@Gio.unix_mount_for]
- * [func@Gio.unix_mounts_changed_since]
- * [func@Gio.unix_mount_points_changed_since]
+ * [func@GioUnix.mount_points_get]
+ * [func@GioUnix.MountPoint.at]
+ * [func@GioUnix.mounts_get]
+ * [func@GioUnix.mount_at]
+ * [func@GioUnix.mount_for]
+ * [func@GioUnix.mounts_changed_since]
+ * [func@GioUnix.mount_points_changed_since]
 
 And several helper functions for checking the type of a mount or path:
 
- * [func@Gio.unix_is_mount_path_system_internal]
- * [func@Gio.unix_is_system_fs_type]
- * [func@Gio.unix_is_system_device_path]
+ * [func@GioUnix.is_mount_path_system_internal]
+ * [func@GioUnix.is_system_fs_type]
+ * [func@GioUnix.is_system_device_path]
 
index a8102e3..4c16c11 100644 (file)
@@ -42,9 +42,10 @@ show_index_summary = true
 show_class_hierarchy = true
 
 [extra]
-urlmap_file = "urlmap.js"
+urlmap_file = "../urlmap.js"
 # The same order will be used when generating the index
 content_files = [
+  "migrating-gi.md",
 ]
 content_images = [
 ]
index ea30b8f..f6e0e04 100644 (file)
@@ -1,4 +1,8 @@
 if get_option('documentation') and enable_gir
+  expand_content_files = [
+    'migrating-gi.md',
+  ]
+
   girepository_toml = configure_file(
     input: 'girepository.toml.in',
     output: 'girepository.toml',
@@ -21,5 +25,9 @@ if get_option('documentation') and enable_gir
       '@INPUT1@',
     ],
     build_by_default: true,
+    depend_files: expand_content_files,
+    install: true,
+    install_dir: docs_dir,
+    install_tag: 'doc',
   )
 endif
diff --git a/docs/reference/girepository/migrating-gi.md b/docs/reference/girepository/migrating-gi.md
new file mode 100644 (file)
index 0000000..8e81755
--- /dev/null
@@ -0,0 +1,96 @@
+Title: Migrating from girepository-1.0 to girepository-2.0
+SPDX-License-Identifier: LGPL-2.1-or-later
+SPDX-FileCopyrightText: 2014 GNOME Foundation
+
+# Migrating from girepository-1.0 to girepository-2.0
+
+libgirepository was originally part of
+[gobject-introspection](https://gitlab.gnome.org/GNOME/gobject-introspection/),
+where it was prototyped for a number of years.
+
+Now that it’s considered stable (for a long time), it’s been moved to
+[glib](https://gitlab.gnome.org/GNOME/glib/) in order to simplify the build
+process of the two modules.
+
+As part of this move, the API version has been bumped from 1.0 to 2.0, and some
+fairly straightforward API changes have been made. Note that the GIR version
+was previously 2.0, and is now 3.0 — so the new version of libgirepository uses
+`girepository-2.0.pc` and `GIRepository-3.0.typelib`.
+
+The main change between the two versions of libgirepository is that it now uses
+[type@GObject.TypeInstance] as the basis of its type system, rather than simple
+C struct aliasing. This means that [type@GIRepository.BaseInfo] instances are
+now reference counted using [method@GIRepository.BaseInfo.ref] and
+[method@GIRepository.BaseInfo.unref].
+
+It also means that runtime cast macros such as [func@GIRepository.CALLABLE_INFO]
+are now available. Using these instead of simple C casts can improve the runtime
+type safety of your code.
+
+Stack allocation of some [type@GIRepository.BaseInfo] subtypes is still
+possible, but they must now be cleared using
+[method@GIRepository.BaseInfo.clear] once finished with. Previously this wasn’t
+necessary.
+
+As part of moving the code over, the symbol prefix has changed from `g_` to
+`gi_` — this has affected every API in the library, but trivially.
+
+The types of various function arguments have changed — for example from
+`guint32` to `size_t` for most offsets. This will require minor adjustments in
+your code if integer type warnings are enabled.
+
+## API replacements from version 1.0 to 2.0
+
+| girepository-1.0 | girepository-2.0 |
+|------------------|------------------|
+| `GI_CHECK_VERSION` | [func@GLib.CHECK_VERSION] |
+| `g_arg_info_get_closure` | [method@GIRepository.ArgInfo.get_closure_index] |
+| `g_arg_info_get_destroy` | [method@GIRepository.ArgInfo.get_destroy_index] |
+| `g_arg_info_get_type` | [method@GIRepository.ArgInfo.get_type_info] |
+| `g_arg_info_load_type` | [method@GIRepository.ArgInfo.load_type_info] |
+| - | [method@GIRepository.BaseInfo.ref] and [method@GIRepository.BaseInfo.unref] |
+| `g_base_info_get_type` | Use type checking macros like [func@GIRepository.IS_OBJECT_INFO], or raw [type@GObject.Type]s with [func@GObject.TYPE_FROM_INSTANCE] |
+| `g_info_new` | Removed with no replacement, use [method@GIRepository.find_by_name] and related APIs |
+| `g_callable_info_invoke` arguments | `is_method` and `throws` dropped in [method@GIRepository.CallableInfo.invoke] |
+| `g_constant_info_get_type` | [method@GIRepository.ConstantInfo.get_type_info] |
+| `g_field_info_get_type` | [method@GIRepository.FieldInfo.get_type_info] |
+| `g_object_info_find_method_using_interfaces` and `g_object_info_find_vfunc_using_interfaces` | The `implementor` out argument has been renamed to `declarer` and is now of type [type@GIRepository.BaseInfo] |
+| `g_object_info_get_type_init` | [method@GIRepository.ObjectInfo.get_type_init_function_name] |
+| `g_object_info_get_ref_function` | [method@GIRepository.ObjectInfo.get_ref_function_name] |
+| `g_object_info_get_unref_function` | [method@GIRepository.ObjectInfo.get_unref_function_name] |
+| `g_object_info_get_set_value_function` | [method@GIRepository.ObjectInfo.get_set_value_function_name] |
+| `g_object_info_get_get_value_function` | [method@GIRepository.ObjectInfo.get_get_value_function_name] |
+| `g_property_info_get_type` | [method@GIRepository.PropertyInfo.get_type_info] |
+| `g_registered_type_info_get_type_init` | [method@GIRepository.RegisteredTypeInfo.get_type_init_function_name] |
+| `g_irepository_*` | `gi_repository_*` |
+| `g_irepository_get_default` | Singleton object removed; create separate [class@GIRepository.Repository] instances instead |
+| `g_irepository_get_search_path` and `g_irepository_get_library_path` | Now return arrays rather than linked lists |
+| `g_irepository_enumerate_versions` | Now returns an array rather than a linked list |
+| `g_irepository_get_immediate_dependencies`, `g_irepository_get_dependencies` and `g_irepository_get_loaded_namespaces` | Now additionally return a length argument |
+| `g_irepository_get_shared_library` | [method@GIRepository.get_shared_libraries] |
+| `g_irepository_dump` | Takes structured `input_filename` and `output_filename` arguments rather than a single formatted string |
+| `g_function_invoker_destroy` | `gi_function_invoker_clear()` |
+| `g_struct_info_get_copy_function` | [method@GIRepository.StructInfo.get_copy_function_name] |
+| `g_struct_info_get_free_function` | [method@GIRepository.StructInfo.get_free_function_name] |
+| `g_type_info_get_array_length` and `g_type_info_get_array_fixed_size` | Split success and failure return values out into a new out-argument and return value |
+| `g_type_info_get_array_length` | [method@GIRepository.TypeInfo.get_array_length_index] |
+| `g_typelib_new_from_*` | All replaced with `gi_typelib_new_from_bytes()` |
+| `g_typelib_free` | [type@GIRepository.Typelib] is now a refcounted and boxed type, so use [method@GIRepository.Typelib.unref] |
+| `GI_FUNCTION_THROWS` and `GI_VFUNC_THROWS` | [method@GIRepository.CallableInfo.can_throw_gerror] |
+| `g_union_info_get_discriminator_offset` | Split success and failure return values out into a new out-argument and return value |
+| `g_union_info_get_copy_function` | [method@GIRepository.UnionInfo.get_copy_function_name] |
+| `g_union_info_get_free_function` | [method@GIRepository.UnionInfo.get_free_function_name] |
+| `GIInfoType` | Use [type@GObject.Type] directly |
+| `GI_INFO_TYPE_BOXED` | Dropped in favour of [method@GIRepository.RegisteredTypeInfo.is_boxed] |
+
+## Utility program renames from version 1.0 to 2.0
+
+| girepository-1.0 | girepository-2.0        |
+|------------------|-------------------------|
+| `g-ir-compiler`  | `gi-compile-repository` |
+| `g-ir-generate`  | `gi-decompile-typelib`  |
+| `g-ir-inspect`   | `gi-inspect-typelib`    |
+
+In addition, the `--version` option for `g-ir-inspect` has been renamed to
+`--typelib-version` in `gi-inspect-typelib`.
+
diff --git a/docs/reference/girepository/urlmap.js b/docs/reference/girepository/urlmap.js
deleted file mode 100644 (file)
index 3224300..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1-or-later
-// SPDX-FileCopyrightText: 2023 Matthias Clasen
-var baseURLs = [
-    [ 'GdkPixbuf', 'https://docs.gtk.org/gdk-pixbuf/' ],
-    [ 'GLib', 'https://docs.gtk.org/glib/' ],
-    [ 'GModule', 'https://docs.gtk.org/gmodule/' ],
-    [ 'GObject', 'https://docs.gtk.org/gobject/' ],
-    [ 'Gio', 'https://docs.gtk.org/gio/' ],
-    [ 'Gtk', 'https://docs.gtk.org/gtk4/' ],
-];
diff --git a/docs/reference/glib/glib-unix.toml.in b/docs/reference/glib/glib-unix.toml.in
new file mode 100644 (file)
index 0000000..9280c1a
--- /dev/null
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# Copyright 2023 Matthias Clasen
+# Copyright 2023 Philip Withnall
+
+[library]
+name = "GLibUnix"
+version = "@VERSION@"
+browse_url = "https://gitlab.gnome.org/GNOME/glib/"
+repository_url = "https://gitlab.gnome.org/GNOME/glib.git"
+website_url = "https://www.gtk.org"
+docs_url = "https://docs.gtk.org/glib-unix/"
+authors = "GLib Development Team"
+license = "LGPL-2.1-or-later"
+description = "Unix-specific APIs in GLib"
+dependencies = [ "GLib-2.0" ]
+related = [ "GModule-2.0", "GObject-2.0", "Gio-2.0" ]
+devhelp = true
+search_index = true
+
+  [dependencies."GLib-2.0"]
+  name = "GLib"
+  description = "The base utility library"
+  docs_url = "https://docs.gtk.org/glib/"
+
+  [related."GModule-2.0"]
+  name = "GModule"
+  description = "Portable API for dynamically loading modules"
+  docs_url = "https://docs.gtk.org/gmodule/"
+
+  [related."GObject-2.0"]
+  name = "GObject"
+  description = "The base type system library"
+  docs_url = "https://docs.gtk.org/gobject/"
+
+  [related."Gio-2.0"]
+  name = "GIO"
+  description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
+  docs_url = "https://docs.gtk.org/gio/"
+
+[theme]
+name = "basic"
+show_index_summary = true
+show_class_hierarchy = true
+
+[extra]
+urlmap_file = "../urlmap.js"
+# The same order will be used when generating the index
+content_files = [
+  "unix.md",
+]
+content_images = []
diff --git a/docs/reference/glib/glib-win32.toml.in b/docs/reference/glib/glib-win32.toml.in
new file mode 100644 (file)
index 0000000..a9f624f
--- /dev/null
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# Copyright 2023 Matthias Clasen
+# Copyright 2023 Philip Withnall
+
+[library]
+name = "GLibWin32"
+version = "@VERSION@"
+browse_url = "https://gitlab.gnome.org/GNOME/glib/"
+repository_url = "https://gitlab.gnome.org/GNOME/glib.git"
+website_url = "https://www.gtk.org"
+docs_url = "https://docs.gtk.org/glib-win32/"
+authors = "GLib Development Team"
+license = "LGPL-2.1-or-later"
+description = "Windows-specific APIs in GLib"
+dependencies = [ "GLib-2.0" ]
+related = [ "GModule-2.0", "GObject-2.0", "Gio-2.0" ]
+devhelp = true
+search_index = true
+
+  [dependencies."GLib-2.0"]
+  name = "GLib"
+  description = "The base utility library"
+  docs_url = "https://docs.gtk.org/glib/"
+
+  [related."GModule-2.0"]
+  name = "GModule"
+  description = "Portable API for dynamically loading modules"
+  docs_url = "https://docs.gtk.org/gmodule/"
+
+  [related."GObject-2.0"]
+  name = "GObject"
+  description = "The base type system library"
+  docs_url = "https://docs.gtk.org/gobject/"
+
+  [related."Gio-2.0"]
+  name = "GIO"
+  description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
+  docs_url = "https://docs.gtk.org/gio/"
+
+[theme]
+name = "basic"
+show_index_summary = true
+show_class_hierarchy = true
+
+[extra]
+urlmap_file = "../urlmap.js"
+# The same order will be used when generating the index
+content_files = [
+  "windows.md",
+]
+content_images = []
index 9691479..83acea5 100644 (file)
@@ -37,7 +37,7 @@ show_index_summary = true
 show_class_hierarchy = true
 
 [extra]
-urlmap_file = "urlmap.js"
+urlmap_file = "../urlmap.js"
 # The same order will be used when generating the index
 content_files = [
   "building.md",
@@ -75,8 +75,6 @@ content_files = [
   "checked-math.md",
   "threads.md",
   "spawn.md",
-  "unix.md",
-  "windows.md",
   "random.md",
   "numerical.md",
   "markup.md",
index bd63b20..b89bc07 100644 (file)
@@ -105,3 +105,14 @@ the source is finalized, and is designed for releasing references like this.
 One important caveat of this second approach is that it will keep the object
 alive indefinitely if the main loop is stopped before the `GSource` is
 invoked, which may be undesirable.
+
+## Tutorial
+
+`GMainContext` is complicated, and can be particularly intimidating for
+developers new to working with GLib. Unfortunately, improper use of
+`GMainContext` often results in bugs that may be difficult to debug. The
+[Main Contexts tutorial](https://developer.gnome.org/documentation/tutorials/main-contexts.html)
+provides valuable guidance for developers working with `GMainContext`
+and is highly recommended reading. In particular, the section
+[Using GMainContext in a Library](https://developer.gnome.org/documentation/tutorials/main-contexts.html#using-gmaincontext-in-a-library)
+documents several pitfalls that library authors should avoid.
index c958963..1777e53 100644 (file)
@@ -91,10 +91,14 @@ if get_option('documentation') and enable_gir
     'string-utils.md',
     'types.md',
     'unicode.md',
-    'unix.md',
     'uuid.md',
     'version.md',
     'warnings.md',
+  ]
+  expand_content_unix_files = [
+    'unix.md',
+  ]
+  expand_content_win32_files = [
     'windows.md',
   ]
 
@@ -118,4 +122,48 @@ if get_option('documentation') and enable_gir
     install_dir: docs_dir,
     install_tag: 'doc',
   )
+
+  if host_system == 'windows'
+    glib_win32_toml = configure_file(input: 'glib-win32.toml.in', output: 'glib-win32.toml', configuration: toml_conf)
+
+    custom_target('glib-win32-docs',
+      input: [ glib_win32_toml, glib_win32_gir[0] ],
+      output: 'glib-win32',
+      command: [
+        gidocgen,
+        'generate',
+        gidocgen_common_args,
+        '--config=@INPUT0@',
+        '--output-dir=@OUTPUT@',
+        '--content-dir=@0@'.format(meson.current_source_dir()),
+        '@INPUT1@',
+      ],
+      build_by_default: true,
+      depend_files: expand_content_win32_files,
+      install: true,
+      install_dir: docs_dir,
+      install_tag: 'doc',
+    )
+  else
+    glib_unix_toml = configure_file(input: 'glib-unix.toml.in', output: 'glib-unix.toml', configuration: toml_conf)
+
+    custom_target('glib-unix-docs',
+      input: [ glib_unix_toml, glib_unix_gir[0] ],
+      output: 'glib-unix',
+      command: [
+        gidocgen,
+        'generate',
+        gidocgen_common_args,
+        '--config=@INPUT0@',
+        '--output-dir=@OUTPUT@',
+        '--content-dir=@0@'.format(meson.current_source_dir()),
+        '@INPUT1@',
+      ],
+      build_by_default: true,
+      depend_files: expand_content_unix_files,
+      install: true,
+      install_dir: docs_dir,
+      install_tag: 'doc',
+    )
+  endif
 endif
index fa446c5..4dad673 100644 (file)
@@ -14,12 +14,12 @@ To use these functions, you must explicitly include the
 
 ## File Descriptors
 
- * [func@GLib.unix_open_pipe]
- * [func@GLib.unix_set_fd_nonblocking]
+ * [func@GLibUnix.open_pipe]
+ * [func@GLibUnix.set_fd_nonblocking]
 
 ## Pipes
 
-The [struct@GLib.UnixPipe] structure can be used to conveniently open and
+The [struct@GLibUnix.Pipe] structure can be used to conveniently open and
 manipulate a Unix pipe.
 
 <!-- FIXME: https://gitlab.gnome.org/GNOME/gi-docgen/-/issues/173 -->
@@ -33,16 +33,16 @@ The methods for it are all static inline for efficiency. They are:
 
 ## Signals
 
- * [func@GLib.unix_signal_add]
- * [func@GLib.unix_signal_add_full]
- * [func@GLib.unix_signal_source_new]
+ * [func@GLibUnix.signal_add]
+ * [func@GLibUnix.signal_add_full]
+ * [func@GLibUnix.signal_source_new]
 
 ## Polling
 
- * [func@GLib.unix_fd_add]
- * [func@GLib.unix_fd_add_full]
- * [func@GLib.unix_fd_source_new]
+ * [func@GLibUnix.fd_add]
+ * [func@GLibUnix.fd_add_full]
+ * [func@GLibUnix.fd_source_new]
 
 ## User Database
 
- * [func@GLib.unix_get_passwd_entry]
+ * [func@GLibUnix.get_passwd_entry]
diff --git a/docs/reference/glib/urlmap.js b/docs/reference/glib/urlmap.js
deleted file mode 100644 (file)
index b1f0962..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1-or-later
-// SPDX-FileCopyrightText: 2023 Matthias Clasen
-var baseURLs = [
-    [ 'GLib', 'https://docs.gtk.org/glib/' ],
-    [ 'GModule', 'https://docs.gtk.org/gmodule/' ],
-    [ 'GObject', 'https://docs.gtk.org/gobject/' ],
-    [ 'Gio', 'https://docs.gtk.org/gio/' ],
-    [ 'Gtk', 'https://docs.gtk.org/gtk4/' ],
-];
index c6a1080..bdef6da 100644 (file)
@@ -8,18 +8,18 @@ These functions provide some level of Unix emulation on the
 Windows platform. If your application really needs the POSIX
 APIs, we suggest you try the [Cygwin project](https://cygwin.com/).
 
- * [type@GLib.Win32OSType]
- * [func@GLib.win32_check_windows_version]
- * [func@GLib.win32_get_command_line]
- * [func@GLib.win32_error_message]
- * [func@GLib.win32_getlocale]
- * [func@GLib.win32_get_package_installation_directory]
- * [func@GLib.win32_get_package_installation_directory_of_module]
- * [func@GLib.win32_get_package_installation_subdirectory]
- * [func@GLib.win32_get_windows_version]
- * [func@GLib.win32_locale_filename_from_utf8]
- * [func@GLib.WIN32_HAVE_WIDECHAR_API]
- * [func@GLib.WIN32_IS_NT_BASED]
+ * [type@GLibWin32.OSType]
+ * [func@GLibWin32.check_windows_version]
+ * [func@GLibWin32.get_command_line]
+ * [func@GLibWin32.error_message]
+ * [func@GLibWin32.getlocale]
+ * [func@GLibWin32.get_package_installation_directory]
+ * [func@GLibWin32.get_package_installation_directory_of_module]
+ * [func@GLibWin32.get_package_installation_subdirectory]
+ * [func@GLibWin32.get_windows_version]
+ * [func@GLibWin32.locale_filename_from_utf8]
+ * [func@GLibWin32.HAVE_WIDECHAR_API]
+ * [func@GLibWin32.IS_NT_BASED]
 
 ## Deprecated API
 
index 46f1efd..20a0a9d 100644 (file)
@@ -40,7 +40,7 @@ show_class_hierarchy = true
 base_url = "https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/"
 
 [extra]
-urlmap_file = "urlmap.js"
+urlmap_file = "../urlmap.js"
 # The same order will be used when generating the index
 content_files = [
   "modules.md",
diff --git a/docs/reference/gmodule/urlmap.js b/docs/reference/gmodule/urlmap.js
deleted file mode 100644 (file)
index b1f0962..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1-or-later
-// SPDX-FileCopyrightText: 2023 Matthias Clasen
-var baseURLs = [
-    [ 'GLib', 'https://docs.gtk.org/glib/' ],
-    [ 'GModule', 'https://docs.gtk.org/gmodule/' ],
-    [ 'GObject', 'https://docs.gtk.org/gobject/' ],
-    [ 'Gio', 'https://docs.gtk.org/gio/' ],
-    [ 'Gtk', 'https://docs.gtk.org/gtk4/' ],
-];
index de39bc4..8c26672 100644 (file)
@@ -39,7 +39,7 @@ show_index_summary = true
 show_class_hierarchy = true
 
 [extra]
-urlmap_file = "urlmap.js"
+urlmap_file = "../urlmap.js"
 # The same order will be used when generating the index
 content_files = [
   "concepts.md",
diff --git a/docs/reference/gobject/urlmap.js b/docs/reference/gobject/urlmap.js
deleted file mode 100644 (file)
index b1f0962..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1-or-later
-// SPDX-FileCopyrightText: 2023 Matthias Clasen
-var baseURLs = [
-    [ 'GLib', 'https://docs.gtk.org/glib/' ],
-    [ 'GModule', 'https://docs.gtk.org/gmodule/' ],
-    [ 'GObject', 'https://docs.gtk.org/gobject/' ],
-    [ 'Gio', 'https://docs.gtk.org/gio/' ],
-    [ 'Gtk', 'https://docs.gtk.org/gtk4/' ],
-];
index 6f9dd42..c990b97 100644 (file)
@@ -12,7 +12,7 @@ if get_option('documentation') and enable_gir
     '--quiet',
     '--no-namespace-dir',
     '--fatal-warnings',
-    '--add-include-path=@0@'.format(meson.current_build_dir() / '../../introspection'),
+    '--add-include-path=@0@'.format(meson.current_build_dir() / '../../girepository/introspection'),
   ]
 
   docs_dir = glib_datadir / 'doc' / 'glib-2.0'
similarity index 61%
rename from docs/reference/gio/urlmap.js
rename to docs/reference/urlmap.js
index 3224300..05ca767 100644 (file)
@@ -1,10 +1,13 @@
 // SPDX-License-Identifier: LGPL-2.1-or-later
 // SPDX-FileCopyrightText: 2023 Matthias Clasen
 var baseURLs = [
-    [ 'GdkPixbuf', 'https://docs.gtk.org/gdk-pixbuf/' ],
     [ 'GLib', 'https://docs.gtk.org/glib/' ],
+    [ 'GLibUnix', 'https://docs.gtk.org/glib-unix/' ],
+    [ 'GLibWin32', 'https://docs.gtk.org/glib-win32/' ],
     [ 'GModule', 'https://docs.gtk.org/gmodule/' ],
     [ 'GObject', 'https://docs.gtk.org/gobject/' ],
     [ 'Gio', 'https://docs.gtk.org/gio/' ],
+    [ 'GioUnix', 'https://docs.gtk.org/gio-unix/' ],
+    [ 'GioWin32', 'https://docs.gtk.org/gio-win32/' ],
     [ 'Gtk', 'https://docs.gtk.org/gtk4/' ],
 ];
index 90119a5..877024a 100644 (file)
@@ -21,6 +21,7 @@
 #include "gio/gnetworking.h"
 
 #include "../gio/gthreadedresolver.h"
+#include "../gio/gthreadedresolver-private.h"
 
 static void
 test_for_rrtype (const guint8 *data,
index 264de69..652cae6 100644 (file)
@@ -1674,11 +1674,15 @@ g_app_launch_context_launch_failed (GAppLaunchContext *context,
  *  - [func@Gio.AppInfo.get_default_for_type]
  *  - [func@Gio.AppInfo.get_fallback_for_type]
  *  - [func@Gio.AppInfo.get_recommended_for_type]
- *  - [func@Gio.DesktopAppInfo.get_implementations]
- *  - [ctor@Gio.DesktopAppInfo.new]
- *  - [ctor@Gio.DesktopAppInfo.new_from_filename]
- *  - [ctor@Gio.DesktopAppInfo.new_from_keyfile]
- *  - [func@Gio.DesktopAppInfo.search]
+ *  - [`g_desktop_app_info_get_implementations()`](../gio-unix/type_func.DesktopAppInfo.get_implementation.html)
+ *  - [`g_desktop_app_info_new()`](../gio-unix/ctor.DesktopAppInfo.new.html)
+ *  - [`g_desktop_app_info_new_from_filename()`](../gio-unix/ctor.DesktopAppInfo.new_from_filename.html)
+ *  - [`g_desktop_app_info_new_from_keyfile()`](../gio-unix/ctor.DesktopAppInfo.new_from_keyfile.html)
+ *  - [`g_desktop_app_info_search()`](../gio-unix/type_func.DesktopAppInfo.search.html)
+ *
+ * The latter functions are available if using
+ * [`GDesktopAppInfo`](../gio-unix/class.DesktopAppInfo.html) from
+ * `gio-unix-2.0.pc` (GIR namespace `GioUnix-2.0`).
  *
  * In the usual case, applications should try to make note of the change
  * (doing things like invalidating caches) but not act on it. In
index 55e7f29..b7d2870 100644 (file)
@@ -227,6 +227,7 @@ struct _GApplicationPrivate
 {
   GApplicationFlags  flags;
   gchar             *id;
+  gchar             *version;
   gchar             *resource_path;
 
   GActionGroup      *actions;
@@ -264,6 +265,7 @@ enum
 {
   PROP_NONE,
   PROP_APPLICATION_ID,
+  PROP_VERSION,
   PROP_FLAGS,
   PROP_RESOURCE_BASE_PATH,
   PROP_IS_REGISTERED,
@@ -478,11 +480,13 @@ g_application_pack_option_entries (GApplication *application,
 static GVariantDict *
 g_application_parse_command_line (GApplication   *application,
                                   gchar        ***arguments,
+                                  gboolean       *print_version,
                                   GError        **error)
 {
   gboolean become_service = FALSE;
   gchar *app_id = NULL;
   gboolean replace = FALSE;
+  gboolean version = FALSE;
   GVariantDict *dict = NULL;
   GOptionContext *context;
   GOptionGroup *gapplication_group;
@@ -564,6 +568,17 @@ g_application_parse_command_line (GApplication   *application,
       g_option_group_add_entries (gapplication_group, entries);
     }
 
+  if (application->priv->version)
+    {
+      GOptionEntry entries[] = {
+        { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
+          N_("Print the application version"), NULL },
+        G_OPTION_ENTRY_NULL
+      };
+
+      g_option_group_add_entries (gapplication_group, entries);
+    }
+
   /* Allow replacing if the application allows it */
   if (application->priv->flags & G_APPLICATION_ALLOW_REPLACEMENT)
     {
@@ -580,6 +595,8 @@ g_application_parse_command_line (GApplication   *application,
   if (!g_option_context_parse_strv (context, arguments, error))
     goto out;
 
+  *print_version = version;
+
   /* Check for --gapplication-service */
   if (become_service)
     application->priv->flags |= G_APPLICATION_IS_SERVICE;
@@ -1101,8 +1118,9 @@ g_application_real_local_command_line (GApplication   *application,
   GError *error = NULL;
   GVariantDict *options;
   gint n_args;
+  gboolean print_version = FALSE;
 
-  options = g_application_parse_command_line (application, arguments, &error);
+  options = g_application_parse_command_line (application, arguments, &print_version, &error);
   if (!options)
     {
       g_printerr ("%s\n", error->message);
@@ -1111,6 +1129,21 @@ g_application_real_local_command_line (GApplication   *application,
       return TRUE;
     }
 
+  /* Exit quickly with --version? */
+  if (print_version)
+    {
+      const char *prgname = g_get_prgname ();
+
+      g_assert (application->priv->version != NULL);
+
+      if (prgname != NULL)
+        g_print ("%s %s\n", prgname, application->priv->version);
+      else
+        g_print ("%s\n", application->priv->version);
+      *exit_status = EXIT_SUCCESS;
+      return TRUE;
+    }
+
   g_signal_emit (application, g_application_signals[SIGNAL_HANDLE_LOCAL_OPTIONS], 0, options, exit_status);
 
   if (*exit_status >= 0)
@@ -1236,6 +1269,10 @@ g_application_set_property (GObject      *object,
                                         g_value_get_string (value));
       break;
 
+    case PROP_VERSION:
+      g_application_set_version (application, g_value_get_string (value));
+      break;
+
     case PROP_FLAGS:
       g_application_set_flags (application, g_value_get_flags (value));
       break;
@@ -1306,6 +1343,11 @@ g_application_get_property (GObject    *object,
                           g_application_get_application_id (application));
       break;
 
+    case PROP_VERSION:
+      g_value_set_string (value,
+                          g_application_get_version (application));
+      break;
+
     case PROP_FLAGS:
       g_value_set_flags (value,
                          g_application_get_flags (application));
@@ -1401,6 +1443,7 @@ g_application_finalize (GObject *object)
   g_free (application->priv->parameter_string);
   g_free (application->priv->summary);
   g_free (application->priv->description);
+  g_free (application->priv->version);
 
   g_slist_free_full (application->priv->option_strings, g_free);
 
@@ -1497,6 +1540,18 @@ g_application_class_init (GApplicationClass *class)
                          G_PARAM_STATIC_STRINGS));
 
   /**
+   * GApplication:version:
+   *
+   * The human-readable version number of the application.
+   *
+   * Since: 2.80
+   */
+  g_object_class_install_property (object_class, PROP_VERSION,
+    g_param_spec_string ("version", NULL, NULL,
+                         NULL, G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
+
+  /**
    * GApplication:flags:
    *
    * Flags specifying the behaviour of the application.
@@ -1888,6 +1943,50 @@ g_application_set_application_id (GApplication *application,
 }
 
 /**
+ * g_application_get_version:
+ * @application: a #GApplication
+ *
+ * Gets the version of @application.
+ *
+ * Returns: (nullable): the version of @application
+ *
+ * Since: 2.80
+ **/
+const gchar *
+g_application_get_version (GApplication *application)
+{
+  g_return_val_if_fail (G_IS_APPLICATION (application), NULL);
+
+  return application->priv->version;
+}
+
+/**
+ * g_application_set_version
+ * @application: a #GApplication
+ * @version: the version of @application
+ *
+ * Sets the version number of @application. This will be used to implement
+ * a `--version` command line argument
+ *
+ * The application version can only be modified if @application has not yet
+ * been registered.
+ *
+ * Since: 2.80
+ **/
+void
+g_application_set_version (GApplication *application,
+                           const gchar  *version)
+{
+  g_return_if_fail (G_IS_APPLICATION (application));
+  g_return_if_fail (version != NULL);
+  g_return_if_fail (!application->priv->is_registered);
+
+  if (g_set_str (&application->priv->version, version))
+    g_object_notify (G_OBJECT (application), "version");
+}
+
+
+/**
  * g_application_get_flags:
  * @application: a #GApplication
  *
index cb6b908..6df0bb4 100644 (file)
@@ -139,6 +139,12 @@ GIO_AVAILABLE_IN_ALL
 void                    g_application_set_application_id                (GApplication             *application,
                                                                          const gchar              *application_id);
 
+GIO_AVAILABLE_IN_2_80
+const gchar *           g_application_get_version                       (GApplication             *application);
+GIO_AVAILABLE_IN_2_80
+void                    g_application_set_version                       (GApplication             *application,
+                                                                         const gchar              *version);
+
 GIO_AVAILABLE_IN_2_34
 GDBusConnection *       g_application_get_dbus_connection               (GApplication             *application);
 GIO_AVAILABLE_IN_2_34
index 9bdcf18..2e8ef8e 100644 (file)
@@ -57,6 +57,9 @@ def generate_namespace(namespace):
 
 
 def generate_header_guard(header_name):
+    if header_name == "-":
+        return "STDOUT"
+
     # There might be more characters that are safe to use than these, but lets
     # stay conservative.
     safe_valid_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -1121,15 +1124,13 @@ class InterfaceInfoBodyCodeGenerator:
 
         self.outfile.write("\n")
         self.outfile.write(
-            "#ifdef HAVE_CONFIG_H\n"
-            '#  include "config.h"\n'
-            "#endif\n"
-            "\n"
-            '#include "%s"\n'
-            "\n"
-            "#include <string.h>\n" % (self.header_name)
+            "#ifdef HAVE_CONFIG_H\n" '#  include "config.h"\n' "#endif\n" "\n"
         )
-        self.outfile.write("\n")
+
+        if self.header_name:
+            self.outfile.write('#include "%s"\n\n' % (self.header_name))
+
+        self.outfile.write("#include <string.h>\n\n")
 
     # ----------------------------------------------------------------------------------------------------
 
@@ -1469,20 +1470,21 @@ class CodeGenerator:
     def generate_body_preamble(self):
         basenames = ", ".join(self.input_files_basenames)
         self.outfile.write(LICENSE_STR.format(config.VERSION, basenames))
+
         if self.symbol_decoration_define is not None:
             self.outfile.write("\n")
             self.outfile.write("#define %s\n" % self.symbol_decoration_define)
+
         self.outfile.write("\n")
         self.outfile.write(
-            "#ifdef HAVE_CONFIG_H\n"
-            '#  include "config.h"\n'
-            "#endif\n"
-            "\n"
-            '#include "%s"\n'
-            "\n"
-            "#include <string.h>\n" % (self.header_name)
+            "#ifdef HAVE_CONFIG_H\n" '#  include "config.h"\n' "#endif\n" "\n"
         )
 
+        if self.header_name:
+            self.outfile.write('#include "%s"\n\n' % (self.header_name))
+
+        self.outfile.write("#include <string.h>\n")
+
         self.outfile.write(
             "#ifdef G_OS_UNIX\n" "#  include <gio/gunixfdlist.h>\n" "#endif\n" "\n"
         )
index c0f44d5..d5353f5 100644 (file)
@@ -24,6 +24,7 @@
 import argparse
 import os
 import sys
+from contextlib import contextmanager
 
 from . import config
 from . import dbustypes
@@ -63,6 +64,15 @@ def find_prop(iface, prop):
     return None
 
 
+@contextmanager
+def file_or_stdout(filename):
+    if filename is None or filename == "-":
+        yield sys.stdout
+    else:
+        with open(filename, "w") as outfile:
+            yield outfile
+
+
 def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
     iface_obj = None
     for i in iface_list:
@@ -326,7 +336,11 @@ def codegen_main():
             print_error("Using --body requires --output")
 
         c_file = args.output
-        header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
+
+        if c_file == "-":
+            header_name = ""
+        else:
+            header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
     elif args.interface_info_header:
         if args.output is None:
             print_error("Using --interface-info-header requires --output")
@@ -348,7 +362,11 @@ def codegen_main():
             )
 
         c_file = args.output
-        header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
+
+        if c_file == "-":
+            header_name = ""
+        else:
+            header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
 
     # Check the minimum GLib version. The minimum --glib-min-required is 2.30,
     # because that’s when gdbus-codegen was introduced. Support 1, 2 or 3
@@ -446,7 +464,7 @@ def codegen_main():
         rst_gen.generate(rst, args.output_directory)
 
     if args.header:
-        with open(h_file, "w") as outfile:
+        with file_or_stdout(h_file) as outfile:
             gen = codegen.HeaderCodeGenerator(
                 all_ifaces,
                 args.c_namespace,
@@ -463,7 +481,7 @@ def codegen_main():
             gen.generate()
 
     if args.body:
-        with open(c_file, "w") as outfile:
+        with file_or_stdout(c_file) as outfile:
             gen = codegen.CodeGenerator(
                 all_ifaces,
                 args.c_namespace,
@@ -478,7 +496,7 @@ def codegen_main():
             gen.generate()
 
     if args.interface_info_header:
-        with open(h_file, "w") as outfile:
+        with file_or_stdout(h_file) as outfile:
             gen = codegen.InterfaceInfoHeaderCodeGenerator(
                 all_ifaces,
                 args.c_namespace,
@@ -493,7 +511,7 @@ def codegen_main():
             gen.generate()
 
     if args.interface_info_body:
-        with open(c_file, "w") as outfile:
+        with file_or_stdout(c_file) as outfile:
             gen = codegen.InterfaceInfoBodyCodeGenerator(
                 all_ifaces,
                 args.c_namespace,
index bde0793..87db7a9 100644 (file)
@@ -70,7 +70,7 @@
  *
  * Note that `<gio/gdesktopappinfo.h>` belongs to the UNIX-specific
  * GIO interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
- * file when using it.
+ * file or the `GioUnix-2.0` GIR namespace when using it.
  */
 
 #define DEFAULT_APPLICATIONS_GROUP  "Default Applications"
index 5ebdf6e..8a09632 100644 (file)
@@ -35,7 +35,7 @@
  *
  * Note that `<gio/gfiledescriptorbased.h>` belongs to the UNIX-specific
  * GIO interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
- * file when using it.
+ * file or the `GioUnix-2.0` GIR namespace when using it.
  *
  * Since: 2.24
  **/
@@ -63,7 +63,7 @@ g_file_descriptor_based_get_fd (GFileDescriptorBased *fd_based)
 {
   GFileDescriptorBasedIface *iface;
 
-  g_return_val_if_fail (G_IS_FILE_DESCRIPTOR_BASED (fd_based), 0);
+  g_return_val_if_fail (G_IS_FILE_DESCRIPTOR_BASED (fd_based), -1);
 
   iface = G_FILE_DESCRIPTOR_BASED_GET_IFACE (fd_based);
 
index a512feb..7c7c78a 100644 (file)
 
 G_BEGIN_DECLS
 
+typedef struct _GFileDescriptorBased        GFileDescriptorBased;
+typedef struct _GFileDescriptorBasedIface   GFileDescriptorBasedIface;
+
 #define G_TYPE_FILE_DESCRIPTOR_BASED            (g_file_descriptor_based_get_type ())
 #define G_FILE_DESCRIPTOR_BASED(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_FILE_DESCRIPTOR_BASED, GFileDescriptorBased))
 #define G_IS_FILE_DESCRIPTOR_BASED(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_FILE_DESCRIPTOR_BASED))
 #define G_FILE_DESCRIPTOR_BASED_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_FILE_DESCRIPTOR_BASED, GFileDescriptorBasedIface))
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileDescriptorBased, g_object_unref)
 
-typedef struct _GFileDescriptorBasedIface   GFileDescriptorBasedIface;
-
 /**
  * GFileDescriptorBasedIface:
  * @g_iface: The parent interface.
index c2c09d1..98143ba 100644 (file)
@@ -82,7 +82,6 @@ typedef struct _GFileInfo                     GFileInfo;
 typedef struct _GFileAttributeMatcher         GFileAttributeMatcher;
 typedef struct _GFileAttributeInfo            GFileAttributeInfo;
 typedef struct _GFileAttributeInfoList        GFileAttributeInfoList;
-typedef struct _GFileDescriptorBased          GFileDescriptorBased;
 typedef struct _GFileInputStream              GFileInputStream;
 typedef struct _GFileOutputStream             GFileOutputStream;
 typedef struct _GFileIOStream                 GFileIOStream;
index b7b9b62..ceb888a 100644 (file)
@@ -1089,6 +1089,8 @@ g_local_file_query_filesystem_info (GFile         *file,
   fstype = statfs_buffer.f_fstypename;
 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
   fstype = statfs_buffer.f_basetype;
+#elif defined(HAVE_STRUCT_STATVFS_F_TYPE)
+  fstype = get_fs_type (statfs_buffer.f_type);
 #else
   fstype = NULL;
 #endif
index fa7bded..44b1f3f 100644 (file)
@@ -165,6 +165,7 @@ get_elf (const gchar *file,
 
   if (elf_kind (elf) != ELF_K_ELF)
     {
+      elf_end (elf);
       g_close (*fd, NULL);
       *fd = -1;
       return NULL;
index f46d6e2..1b13fa2 100644 (file)
@@ -36,7 +36,9 @@
  *
  * This is useful when you obtained a [class@Gio.InputStream] and a
  * [class@Gio.OutputStream] by other means, for instance creating them with
- * platform specific methods as [ctor@Gio.UnixInputStream.new], and you want to
+ * platform specific methods as
+ * [`g_unix_input_stream_new()`](../gio-unix/ctor.UnixInputStream.new.html)
+ * (from `gio-unix-2.0.pc` / `GioUnix-2.0`), and you want to
  * take advantage of the methods provided by [class@Gio.IOStream].
  *
  * Since: 2.44
index dd5730b..e6815e2 100644 (file)
@@ -2454,9 +2454,18 @@ g_task_class_init (GTaskClass *klass)
    * GTask:completed:
    *
    * Whether the task has completed, meaning its callback (if set) has been
-   * invoked. This can only happen after g_task_return_pointer(),
+   * invoked.
+   *
+   * This can only happen after g_task_return_pointer(),
    * g_task_return_error() or one of the other return functions have been called
-   * on the task.
+   * on the task. However, it is not guaranteed to happen immediately after
+   * those functions are called, as the task’s callback may need to be scheduled
+   * to run in a different thread.
+   *
+   * That means it is **not safe** to use this property to track whether a
+   * return function has been called on the #GTask. Callers must do that
+   * tracking themselves, typically by linking the lifetime of the #GTask to the
+   * control flow of their code.
    *
    * This property is guaranteed to change from %FALSE to %TRUE exactly once.
    *
diff --git a/gio/gthreadedresolver-private.h b/gio/gthreadedresolver-private.h
new file mode 100644 (file)
index 0000000..b767267
--- /dev/null
@@ -0,0 +1,46 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __G_THREADED_RESOLVER_PRIVATE_H__
+#define __G_THREADED_RESOLVER_PRIVATE_H__
+
+#include <gio/gio.h>
+#include <gio/gresolver.h>
+
+G_BEGIN_DECLS
+
+/* Used for a private test API */
+#ifdef G_OS_UNIX
+/*< private >*/
+GIO_AVAILABLE_IN_ALL
+GList *g_resolver_records_from_res_query (const gchar      *rrname,
+                                          gint              rrtype,
+                                          const guint8     *answer,
+                                          gssize            len,
+                                          gint              herr,
+                                          GError          **error);
+/*< private >*/
+GIO_AVAILABLE_IN_ALL
+gint g_resolver_record_type_to_rrtype (GResolverRecordType type);
+#endif
+
+G_END_DECLS
+
+#endif /* __G_RESOLVER_PRIVATE_H__ */
index f75c934..b8588e7 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "glib/glib-private.h"
 #include "gthreadedresolver.h"
+#include "gthreadedresolver-private.h"
 #include "gnetworkingprivate.h"
 
 #include "gcancellable.h"
@@ -1592,6 +1593,7 @@ threaded_resolver_worker_cb (gpointer task_data,
         }
 
       g_clear_pointer (&addresses, g_resolver_free_addresses);
+      g_clear_error (&local_error);
     }
     break;
   case LOOKUP_BY_ADDRESS:
@@ -1613,6 +1615,7 @@ threaded_resolver_worker_cb (gpointer task_data,
         }
 
       g_clear_pointer (&name, g_free);
+      g_clear_error (&local_error);
     }
     break;
   case LOOKUP_RECORDS:
@@ -1635,6 +1638,7 @@ threaded_resolver_worker_cb (gpointer task_data,
         }
 
       g_clear_pointer (&records, free_records);
+      g_clear_error (&local_error);
     }
     break;
   default:
index b5556d1..5d84c5d 100644 (file)
@@ -39,21 +39,6 @@ G_BEGIN_DECLS
 GIO_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (GThreadedResolver, g_threaded_resolver, G, THREADED_RESOLVER, GResolver)
 
-/* Used for a private test API */
-#ifdef G_OS_UNIX
-/*< private >*/
-GIO_AVAILABLE_IN_ALL
-GList *g_resolver_records_from_res_query (const gchar      *rrname,
-                                          gint              rrtype,
-                                          const guint8     *answer,
-                                          gssize            len,
-                                          gint              herr,
-                                          GError          **error);
-/*< private >*/
-GIO_AVAILABLE_IN_ALL
-gint g_resolver_record_type_to_rrtype (GResolverRecordType type);
-#endif
-
 G_END_DECLS
 
 #endif /* __G_RESOLVER_H__ */
index f17bb8d..88a5a5f 100644 (file)
@@ -20,7 +20,8 @@
  * A `GUnixFDList` contains a list of file descriptors.  It owns the file
  * descriptors that it contains, closing them when finalized.
  *
- * It may be wrapped in a [class@Gio.UnixFDMessage] and sent over a
+ * It may be wrapped in a
+ * [`GUnixFDMessage`](../gio-unix/class.UnixFDMessage.html) and sent over a
  * [class@Gio.Socket] in the `G_SOCKET_FAMILY_UNIX` family by using
  * [method@Gio.Socket.send_message] and received using
  * [method@Gio.Socket.receive_message].
index 2d11a35..f800e95 100644 (file)
@@ -29,7 +29,7 @@
  *
  * Note that `<gio/gunixfdmessage.h>` belongs to the UNIX-specific GIO
  * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
- * file when using it.
+ * file or the `GioUnix-2.0` GIR namespace when using it.
  */
 
 #include "config.h"
index 1ace44a..210decd 100644 (file)
@@ -49,7 +49,7 @@
  *
  * Note that `<gio/gunixinputstream.h>` belongs to the UNIX-specific GIO
  * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
- * file when using it.
+ * file or the `GioUnix-2.0` GIR namespace when using it.
  */
 
 enum {
index 626f349..a7a9363 100644 (file)
@@ -201,6 +201,11 @@ static GSource *proc_mounts_watch_source;
 #define endmntent(f) fclose(f)
 #endif
 
+#ifdef HAVE_LIBMOUNT
+/* Protected by proc_mounts_source lock */
+static struct libmnt_monitor *proc_mounts_monitor = NULL;
+#endif
+
 static gboolean
 is_in (const char *value, const char *set[])
 {
@@ -1893,7 +1898,36 @@ proc_mounts_changed (GIOChannel   *channel,
                      GIOCondition  cond,
                      gpointer      user_data)
 {
+  gboolean has_changed = FALSE;
+
+#ifdef HAVE_LIBMOUNT
+  if (cond & G_IO_IN)
+    {
+      G_LOCK (proc_mounts_source);
+      if (proc_mounts_monitor != NULL)
+        {
+          int ret;
+
+          /* The mnt_monitor_next_change function needs to be used to avoid false-positives. */
+          ret = mnt_monitor_next_change (proc_mounts_monitor, NULL, NULL);
+          if (ret == 0)
+            {
+              has_changed = TRUE;
+              ret = mnt_monitor_event_cleanup (proc_mounts_monitor);
+            }
+
+          if (ret < 0)
+            g_debug ("mnt_monitor_next_change failed: %s", g_strerror (-ret));
+        }
+      G_UNLOCK (proc_mounts_source);
+    }
+
+#else
   if (cond & G_IO_ERR)
+    has_changed = TRUE;
+#endif
+
+  if (has_changed)
     {
       G_LOCK (proc_mounts_source);
       mount_poller_time = (guint64) g_get_monotonic_time ();
@@ -1958,6 +1992,10 @@ mount_monitor_stop (void)
       g_source_destroy (proc_mounts_watch_source);
       proc_mounts_watch_source = NULL;
     }
+
+#ifdef HAVE_LIBMOUNT
+  g_clear_pointer (&proc_mounts_monitor, mnt_unref_monitor);
+#endif
   G_UNLOCK (proc_mounts_source);
 
   if (mtab_monitor)
@@ -1999,9 +2037,43 @@ mount_monitor_start (void)
        */
       if (g_str_has_prefix (mtab_path, "/proc/"))
         {
-          GIOChannel *proc_mounts_channel;
+          GIOChannel *proc_mounts_channel = NULL;
           GError *error = NULL;
+#ifdef HAVE_LIBMOUNT
+          int ret;
+
+          G_LOCK (proc_mounts_source);
+
+          proc_mounts_monitor = mnt_new_monitor ();
+          ret = mnt_monitor_enable_kernel (proc_mounts_monitor, TRUE);
+          if (ret < 0)
+            g_warning ("mnt_monitor_enable_kernel failed: %s", g_strerror (-ret));
+
+          ret = mnt_monitor_enable_userspace (proc_mounts_monitor, TRUE, NULL);
+          if (ret < 0)
+            g_warning ("mnt_monitor_enable_userspace failed: %s", g_strerror (-ret));
+
+#ifdef HAVE_MNT_MONITOR_VEIL_KERNEL
+          ret = mnt_monitor_veil_kernel (proc_mounts_monitor, TRUE);
+          if (ret < 0)
+            g_warning ("mnt_monitor_veil_kernel failed: %s", g_strerror (-ret));
+#endif
+
+          ret = mnt_monitor_get_fd (proc_mounts_monitor);
+          if (ret >= 0)
+            {
+              proc_mounts_channel = g_io_channel_unix_new (ret);
+            }
+          else
+            {
+              g_set_error_literal (&error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                                   g_strerror (-ret));
+            }
+
+          G_UNLOCK (proc_mounts_source);
+#else
           proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
+#endif
           if (proc_mounts_channel == NULL)
             {
               g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
@@ -2012,7 +2084,11 @@ mount_monitor_start (void)
             {
               G_LOCK (proc_mounts_source);
 
+#ifdef HAVE_LIBMOUNT
+              proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_IN);
+#else
               proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
+#endif
               mount_poller_time = (guint64) g_get_monotonic_time ();
               g_source_set_callback (proc_mounts_watch_source,
                                      (GSourceFunc) proc_mounts_changed,
index e0782ca..96d9e95 100644 (file)
@@ -51,7 +51,7 @@
  *
  * Note that `<gio/gunixoutputstream.h>` belongs to the UNIX-specific GIO
  * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config file
- * when using it.
+ * file or the `GioUnix-2.0` GIR namespace when using it.
  */
 
 enum {
index 39d2f4d..59c2b0f 100644 (file)
@@ -341,7 +341,6 @@ local_sources = files(
 
 platform_deps = []
 internal_deps = []
-appinfo_sources = []
 contenttype_sources = []
 portal_sources = []
 unix_sources = []
@@ -394,7 +393,7 @@ if host_system != 'windows'
   if glib_have_cocoa
     settings_sources += files('gnextstepsettingsbackend.m')
     contenttype_sources += files('gosxcontenttype.m')
-    appinfo_sources += files('gosxappinfo.m')
+    unix_sources += files('gosxappinfo.m')
     framework_dep = dependency('appleframeworks', modules : ['Foundation', 'CoreFoundation', 'AppKit'])
     platform_deps += [framework_dep]
     if glib_have_os_x_9_or_later
@@ -403,7 +402,7 @@ if host_system != 'windows'
     application_headers += files('gosxappinfo.h')
   else
     contenttype_sources += files('gcontenttype.c')
-    appinfo_sources += files('gdesktopappinfo.c')
+    unix_sources += files('gdesktopappinfo.c')
     gio_unix_include_headers += files('gdesktopappinfo.h')
     launch_desktop_sources = files('gio-launch-desktop.c')
 
@@ -433,7 +432,7 @@ if host_system != 'windows'
     )
   endif
 else
-  appinfo_sources += files('gwin32appinfo.c')
+  win32_sources += files('gwin32appinfo.c')
   contenttype_sources += files('gcontenttype-win32.c')
   platform_deps += [cc.find_library('shlwapi'),
                     cc.find_library('dnsapi'),
@@ -613,7 +612,6 @@ if glib_build_shared
   gio_sources += files ('../glib/gtrace.c')
 endif
 
-gio_sources += appinfo_sources
 gio_sources += contenttype_sources
 gio_sources += gdbus_daemon_sources
 gio_sources += unix_sources
index 0681bb4..a18dcb1 100644 (file)
@@ -41,10 +41,6 @@ import taptestrunner
 Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
 
 
-def on_win32():
-    return sys.platform.find("win") != -1
-
-
 class TestCodegen(unittest.TestCase):
     """Integration test for running gdbus-codegen.
 
@@ -149,6 +145,7 @@ class TestCodegen(unittest.TestCase):
             "#ifdef G_OS_UNIX\n"
             "#  include <gio/gunixfdlist.h>\n"
             "#endif",
+            "interface_info_header_includes": "#include <string.h>",
             "private_gvalues_getters": """#ifdef G_ENABLE_DEBUG
 #define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
 #define g_marshal_value_peek_char(v)     g_value_get_schar (v)
@@ -355,10 +352,34 @@ class TestCodegen(unittest.TestCase):
         with self.assertRaises(subprocess.CalledProcessError):
             self.runCodegen()
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_empty_interface_header(self):
         """Test generating a header with an empty interface file."""
-        result = self.runCodegenWithInterface("", "--output", "/dev/stdout", "--header")
+        result = self.runCodegenWithInterface("", "--output", "-", "--header")
+        self.assertEqual("", result.err)
+        self.assertEqual(
+            """{standard_top_comment}
+
+#ifndef __STDOUT__
+#define __STDOUT__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+
+G_END_DECLS
+
+#endif /* __STDOUT__ */""".format(
+                **result.subs
+            ),
+            result.out.strip(),
+        )
+
+    def test_empty_interface_info_header(self):
+        """Test generating a header with an empty interface file."""
+        result = self.runCodegenWithInterface(
+            "", "--output", "-", "--interface-info-header"
+        )
         self.assertEqual("", result.err)
         self.assertEqual(
             """{standard_top_comment}
@@ -379,18 +400,15 @@ G_END_DECLS
             result.out.strip(),
         )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_empty_interface_body(self):
         """Test generating a body with an empty interface file."""
-        result = self.runCodegenWithInterface("", "--output", "/dev/stdout", "--body")
+        result = self.runCodegenWithInterface("", "--output", "-", "--body")
         self.assertEqual("", result.err)
         self.assertEqual(
             """{standard_top_comment}
 
 {standard_config_h_include}
 
-#include "stdout.h"
-
 {standard_header_includes}
 
 {private_gvalues_getters}
@@ -401,7 +419,23 @@ G_END_DECLS
             result.out.strip(),
         )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
+    def test_empty_interface_info_body(self):
+        """Test generating a body with an empty interface file."""
+        result = self.runCodegenWithInterface(
+            "", "--output", "-", "--interface-info-body"
+        )
+        self.assertEqual("", result.err)
+        self.assertEqual(
+            """{standard_top_comment}
+
+{standard_config_h_include}
+
+{interface_info_header_includes}""".format(
+                **result.subs
+            ),
+            result.out.strip(),
+        )
+
     def test_reproducible(self):
         """Test builds are reproducible regardless of file ordering."""
         xml_contents1 = """
@@ -444,7 +478,7 @@ G_END_DECLS
                     xml_file1.name,
                     xml_file2.name,
                     "--output",
-                    "/dev/stdout",
+                    "-",
                     header_or_body,
                 )
                 self.assertEqual("", result1.err)
@@ -453,7 +487,7 @@ G_END_DECLS
                     xml_file2.name,
                     xml_file1.name,
                     "--output",
-                    "/dev/stdout",
+                    "-",
                     header_or_body,
                 )
                 self.assertEqual("", result2.err)
@@ -479,7 +513,7 @@ G_END_DECLS
         self.assertEqual("", res.out)
         with open("test-org.project.Bar.Frobnicator.xml", "r") as f:
             xml_data = f.readlines()
-            self.assertTrue(len(xml_data) != 0)
+            self.assertNotEqual(len(xml_data), 0)
 
     def test_generate_md(self):
         """Test the basic functionality of the markdown generator."""
@@ -499,7 +533,7 @@ G_END_DECLS
         self.assertEqual("", res.out)
         with open("test-org.project.Bar.Frobnicator.md", "r") as f:
             rst = f.readlines()
-            self.assertTrue(len(rst) != 0)
+            self.assertNotEqual(len(rst), 0)
 
     def test_generate_rst(self):
         """Test the basic functionality of the rst generator."""
@@ -519,7 +553,7 @@ G_END_DECLS
         self.assertEqual("", res.out)
         with open("test-org.project.Bar.Frobnicator.rst", "r") as f:
             rst = f.readlines()
-            self.assertTrue(len(rst) != 0)
+            self.assertNotEqual(len(rst), 0)
 
     def test_generate_rst_method(self):
         """Test generating a method documentation with the rst generator."""
@@ -653,35 +687,32 @@ G_END_DECLS
                 rst,
             )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_min_required_invalid(self):
         """Test running with an invalid --glib-min-required."""
         with self.assertRaises(subprocess.CalledProcessError):
             self.runCodegenWithInterface(
                 "",
                 "--output",
-                "/dev/stdout",
+                "-",
                 "--body",
                 "--glib-min-required",
                 "hello mum",
             )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_min_required_too_low(self):
         """Test running with a --glib-min-required which is too low (and hence
         probably a typo)."""
         with self.assertRaises(subprocess.CalledProcessError):
             self.runCodegenWithInterface(
-                "", "--output", "/dev/stdout", "--body", "--glib-min-required", "2.6"
+                "", "--output", "-", "--body", "--glib-min-required", "2.6"
             )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_min_required_major_only(self):
         """Test running with a --glib-min-required which contains only a major version."""
         result = self.runCodegenWithInterface(
             "",
             "--output",
-            "/dev/stdout",
+            "-",
             "--header",
             "--glib-min-required",
             "3",
@@ -691,43 +722,38 @@ G_END_DECLS
         self.assertEqual("", result.err)
         self.assertNotEqual("", result.out.strip())
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_min_required_with_micro(self):
         """Test running with a --glib-min-required which contains a micro version."""
         result = self.runCodegenWithInterface(
-            "", "--output", "/dev/stdout", "--header", "--glib-min-required", "2.46.2"
+            "", "--output", "-", "--header", "--glib-min-required", "2.46.2"
         )
         self.assertEqual("", result.err)
         self.assertNotEqual("", result.out.strip())
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_max_allowed_too_low(self):
         """Test running with a --glib-max-allowed which is too low (and hence
         probably a typo)."""
         with self.assertRaises(subprocess.CalledProcessError):
             self.runCodegenWithInterface(
-                "", "--output", "/dev/stdout", "--body", "--glib-max-allowed", "2.6"
+                "", "--output", "-", "--body", "--glib-max-allowed", "2.6"
             )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_max_allowed_major_only(self):
         """Test running with a --glib-max-allowed which contains only a major version."""
         result = self.runCodegenWithInterface(
-            "", "--output", "/dev/stdout", "--header", "--glib-max-allowed", "3"
+            "", "--output", "-", "--header", "--glib-max-allowed", "3"
         )
         self.assertEqual("", result.err)
         self.assertNotEqual("", result.out.strip())
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_max_allowed_with_micro(self):
         """Test running with a --glib-max-allowed which contains a micro version."""
         result = self.runCodegenWithInterface(
-            "", "--output", "/dev/stdout", "--header", "--glib-max-allowed", "2.46.2"
+            "", "--output", "-", "--header", "--glib-max-allowed", "2.46.2"
         )
         self.assertEqual("", result.err)
         self.assertNotEqual("", result.out.strip())
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_max_allowed_unstable(self):
         """Test running with a --glib-max-allowed which is unstable. It should
         be rounded up to the next stable version number, and hence should not
@@ -735,7 +761,7 @@ G_END_DECLS
         result = self.runCodegenWithInterface(
             "",
             "--output",
-            "/dev/stdout",
+            "-",
             "--header",
             "--glib-max-allowed",
             "2.63",
@@ -745,7 +771,6 @@ G_END_DECLS
         self.assertEqual("", result.err)
         self.assertNotEqual("", result.out.strip())
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_glib_max_allowed_less_than_min_required(self):
         """Test running with a --glib-max-allowed which is less than
         --glib-min-required."""
@@ -753,7 +778,7 @@ G_END_DECLS
             self.runCodegenWithInterface(
                 "",
                 "--output",
-                "/dev/stdout",
+                "-",
                 "--body",
                 "--glib-max-allowed",
                 "2.62",
@@ -761,7 +786,6 @@ G_END_DECLS
                 "2.64",
             )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_dbus_types(self):
         bad_types = [
             "{vs}",  # Bad dictionary key type
@@ -797,9 +821,7 @@ G_END_DECLS
                   </interface>
                 </node>"""
             with self.assertRaises(subprocess.CalledProcessError):
-                self.runCodegenWithInterface(
-                    interface_xml, "--output", "/dev/stdout", "--body"
-                )
+                self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         good_types = [
             "si{s{b(ybnqiuxtdh)}}{yv}{nv}{dv}",
             "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@@ -815,11 +837,10 @@ G_END_DECLS
                   </interface>
                 </node>"""
             result = self.runCodegenWithInterface(
-                interface_xml, "--output", "/dev/stdout", "--body"
+                interface_xml, "--output", "-", "--body"
             )
             self.assertEqual("", result.err)
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_unix_fd_types_and_annotations(self):
         """Test an interface with `h` arguments, no annotation, and GLib < 2.64.
 
@@ -847,7 +868,7 @@ G_END_DECLS
 
         # Try without specifying --glib-min-required.
         result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--header"
+            interface_xml, "--output", "-", "--header"
         )
         self.assertEqual("", result.err)
         self.assertEqual(result.out.strip().count("GUnixFDList"), 6)
@@ -856,7 +877,7 @@ G_END_DECLS
         result = self.runCodegenWithInterface(
             interface_xml,
             "--output",
-            "/dev/stdout",
+            "-",
             "--header",
             "--glib-min-required",
             "2.32",
@@ -870,7 +891,7 @@ G_END_DECLS
         result = self.runCodegenWithInterface(
             interface_xml,
             "--output",
-            "/dev/stdout",
+            "-",
             "--header",
             "--glib-min-required",
             "2.64",
@@ -878,7 +899,6 @@ G_END_DECLS
         self.assertEqual("", result.err)
         self.assertEqual(result.out.strip().count("GUnixFDList"), 18)
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_call_flags_and_timeout_method_args(self):
         """Test that generated method call functions have @call_flags and
         @timeout_msec args if and only if GLib >= 2.64.
@@ -892,7 +912,7 @@ G_END_DECLS
 
         # Try without specifying --glib-min-required.
         result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--header"
+            interface_xml, "--output", "-", "--header"
         )
         self.assertEqual("", result.err)
         self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 0)
@@ -902,7 +922,7 @@ G_END_DECLS
         result = self.runCodegenWithInterface(
             interface_xml,
             "--output",
-            "/dev/stdout",
+            "-",
             "--header",
             "--glib-min-required",
             "2.32",
@@ -916,7 +936,7 @@ G_END_DECLS
         result = self.runCodegenWithInterface(
             interface_xml,
             "--output",
-            "/dev/stdout",
+            "-",
             "--header",
             "--glib-min-required",
             "2.64",
@@ -925,7 +945,6 @@ G_END_DECLS
         self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 2)
         self.assertEqual(result.out.strip().count("gint timeout_msec,"), 2)
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_signal_id_simple_signal(self):
         """Test that signals IDs are used to emit signals"""
         interface_xml = """
@@ -938,9 +957,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
@@ -957,7 +974,6 @@ G_END_DECLS
                 1,
             )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_signal_id_multiple_signals_types(self):
         """Test that signals IDs are used to emit signals for all types"""
 
@@ -977,9 +993,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
@@ -997,7 +1011,6 @@ G_END_DECLS
                 1,
             )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_signal_id_multiple_signal_args_types(self):
         """Test that signals IDs are used to emit signals for all types"""
 
@@ -1015,9 +1028,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
@@ -1036,7 +1047,6 @@ G_END_DECLS
             1,
         )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_signals_marshaller_simple_signal(self):
         """Test that signals marshaller is generated for simple signal"""
         interface_xml = """
@@ -1049,9 +1059,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@@ -1066,7 +1074,6 @@ G_END_DECLS
         self.assertIs(stripped_out.count(f"{func_name} ("), 1)
         self.assertIs(stripped_out.count("g_cclosure_marshal_VOID__VOID (closure"), 2)
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_signals_marshaller_single_typed_args(self):
         """Test that signals marshaller is generated for each known type"""
         for t, props in self.ARGUMENTS_TYPES.items():
@@ -1082,7 +1089,7 @@ G_END_DECLS
             </node>"""
 
             result = self.runCodegenWithInterface(
-                interface_xml, "--output", "/dev/stdout", "--body"
+                interface_xml, "--output", "-", "--body"
             )
             stripped_out = result.out.strip()
             self.assertFalse(result.err)
@@ -1113,7 +1120,6 @@ G_END_DECLS
                     1,
                 )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_signals_marshallers_multiple_args(self):
         """Test that signals marshallers are generated"""
         generated_args = [
@@ -1131,9 +1137,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@@ -1157,7 +1161,6 @@ G_END_DECLS
             )
             index += 1
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_methods_marshaller_simple_method(self):
         """Test that methods marshaller is generated for simple method"""
         interface_xml = """
@@ -1170,9 +1173,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@@ -1202,7 +1203,6 @@ G_END_DECLS
             stripped_out.count("_g_dbus_codegen_marshal_BOOLEAN__OBJECT (closure"), 2
         )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_methods_marshaller_single_typed_in_args(self):
         """Test that methods marshallers are generated for each known type"""
         for t, props in self.ARGUMENTS_TYPES.items():
@@ -1217,7 +1217,7 @@ G_END_DECLS
             </node>"""
 
             result = self.runCodegenWithInterface(
-                interface_xml, "--output", "/dev/stdout", "--body"
+                interface_xml, "--output", "-", "--body"
             )
             stripped_out = result.out.strip()
             self.assertFalse(result.err)
@@ -1241,7 +1241,6 @@ G_END_DECLS
                 1,
             )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_methods_marshaller_single_typed_out_args(self):
         """Test that methods marshallers are generated for each known type"""
         for t, props in self.ARGUMENTS_TYPES.items():
@@ -1256,7 +1255,7 @@ G_END_DECLS
             </node>"""
 
             result = self.runCodegenWithInterface(
-                interface_xml, "--output", "/dev/stdout", "--body"
+                interface_xml, "--output", "-", "--body"
             )
             stripped_out = result.out.strip()
             self.assertFalse(result.err)
@@ -1275,7 +1274,6 @@ G_END_DECLS
             )
             self.assertIs(stripped_out.count("(param_values + 2)"), 0)
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_methods_marshallers_multiple_in_args(self):
         """Test that methods marshallers are generated"""
         generated_args = [
@@ -1300,9 +1298,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@@ -1344,7 +1340,6 @@ G_END_DECLS
         self.assertIs(stripped_out.count(f"{func_name},"), 1)
         self.assertIs(stripped_out.count(f"{func_name} ("), 1)
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_methods_marshallers_multiple_out_args(self):
         """Test that methods marshallers are generated"""
         generated_args = [
@@ -1361,9 +1356,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@@ -1392,7 +1385,6 @@ G_END_DECLS
             1,
         )
 
-    @unittest.skipIf(on_win32(), "requires /dev/stdout")
     def test_generate_methods_marshallers_with_unix_fds(self):
         """Test an interface with `h` arguments"""
         interface_xml = """
@@ -1406,9 +1398,7 @@ G_END_DECLS
               </interface>
             </node>"""
 
-        result = self.runCodegenWithInterface(
-            interface_xml, "--output", "/dev/stdout", "--body"
-        )
+        result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
         stripped_out = result.out.strip()
         self.assertFalse(result.err)
         self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@@ -1472,7 +1462,7 @@ G_END_DECLS
         self.assertEqual("", res.err)
         self.assertEqual("", res.out)
         with open("test-org.project.Bar.Frobnicator.xml", "r") as f:
-            self.assertTrue(ET.parse(f) is not None)
+            self.assertIsNotNone(ET.parse(f))
 
     def test_indentation_preservation_in_comments(self):
         """Test if the parser preserves relative indentation in XML comments"""
index f99f98b..bbc8b3e 100644 (file)
  * along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "config.h"
+
 #include <string.h>
 #include <gio/gio.h>
 
+#ifdef HAVE_COCOA
+#include <gio/gosxappinfo.h>
+#endif
+
 static void
 check_property (const char *output,
                GParamSpec *pspec,
index 99380cd..cbec9a8 100644 (file)
@@ -34,6 +34,7 @@ main (int argc, char **argv)
                            G_APPLICATION_HANDLES_COMMAND_LINE);
   g_signal_connect (app, "command-line", G_CALLBACK (command_line), NULL);
   g_application_set_inactivity_timeout (app, 10000);
+  g_application_set_version (app, "2.3");
 
   status = g_application_run (app, argc, argv);
 
index 29c6989..8c1dac1 100644 (file)
@@ -369,6 +369,7 @@ properties (void)
   GDBusConnection *c;
   GObject *app;
   gchar *id;
+  gchar *version;
   GApplicationFlags flags;
   gboolean registered;
   guint timeout;
@@ -381,20 +382,25 @@ properties (void)
 
   app = g_object_new (G_TYPE_APPLICATION,
                       "application-id", "org.gtk.TestApplication",
+                      "version", "1.0",
                       NULL);
 
   g_object_get (app,
                 "application-id", &id,
+                "version", &version,
                 "flags", &flags,
                 "is-registered", &registered,
                 "inactivity-timeout", &timeout,
                 NULL);
 
   g_assert_cmpstr (id, ==, "org.gtk.TestApplication");
+  g_assert_cmpstr (version, ==, "1.0");
   g_assert_cmpint (flags, ==, G_APPLICATION_DEFAULT_FLAGS);
   g_assert (!registered);
   g_assert_cmpint (timeout, ==, 0);
 
+  g_clear_pointer (&version, g_free);
+
   ret = g_application_register (G_APPLICATION (app), NULL, &error);
   g_assert (ret);
   g_assert_no_error (error);
@@ -1085,6 +1091,37 @@ test_api (void)
   g_object_unref (app);
 }
 
+static void
+test_version (void)
+{
+  GApplication *app;
+  gchar *version = NULL;
+  gchar *version_orig = NULL;
+
+  app = g_application_new ("org.gtk.TestApplication", 0);
+
+  version_orig = "1.2";
+  g_object_set (G_OBJECT (app), "version", version_orig, NULL);
+  g_object_get (app, "version", &version, NULL);
+  g_assert_cmpstr (version, ==, version_orig);
+  g_free (version);
+
+  /* test attempting to set the same version again */
+  version_orig = "1.2";
+  g_object_set (G_OBJECT (app), "version", version_orig, NULL);
+  g_object_get (app, "version", &version, NULL);
+  g_assert_cmpstr (version, ==, version_orig);
+  g_free (version);
+
+  version_orig = "2.4";
+  g_object_set (G_OBJECT (app), "version", version_orig, NULL);
+  g_object_get (app, "version", &version, NULL);
+  g_assert_cmpstr (version, ==, version_orig);
+  g_free (version);
+
+  g_object_unref (app);
+}
+
 /* Check that G_APPLICATION_ALLOW_REPLACEMENT works. To do so, we launch
  * a GApplication in this process that allows replacement, and then
  * launch a subprocess with --gapplication-replace. We have to do our
@@ -1830,6 +1867,7 @@ main (int argc, char **argv)
   g_test_add_func ("/gapplication/test-handle-local-options2", test_handle_local_options_failure);
   g_test_add_func ("/gapplication/test-handle-local-options3", test_handle_local_options_passthrough);
   g_test_add_func ("/gapplication/api", test_api);
+  g_test_add_func ("/gapplication/version", test_version);
   g_test_add_data_func ("/gapplication/replace", GINT_TO_POINTER (TRUE), test_replace);
   g_test_add_data_func ("/gapplication/no-replace", GINT_TO_POINTER (FALSE), test_replace);
   g_test_add_func ("/gapplication/dbus/activate", test_dbus_activate);
index bf32918..7aae01e 100755 (executable)
@@ -16,7 +16,6 @@ import sys
 import subprocess
 import fcntl
 import os
-import time
 
 import taptestrunner
 
@@ -57,53 +56,74 @@ try:
             fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
             self.last_warning = -1
             self.dbusmock = dbus.Interface(self.obj_lmm, dbusmock.MOCK_IFACE)
+
+            try:
+                self.wait_for_bus_object('org.freedesktop.LowMemoryMonitor',
+                                        '/org/freedesktop/LowMemoryMonitor',
+                                        system_bus=True)
+            except:
+                raise
+
             self.memory_monitor = Gio.MemoryMonitor.dup_default()
+            assert("GMemoryMonitorDBus" in str(self.memory_monitor))
             self.memory_monitor.connect("low-memory-warning", self.memory_warning_cb)
             self.mainloop = GLib.MainLoop()
             self.main_context = self.mainloop.get_context()
 
+            # The LowMemoryMonitor API is stateless: it doesn’t expose any
+            # properties, just a warning signal. Emit the signal in a loop until
+            # the GMemoryMonitor instance has initialised and synchronised to
+            # the right state.
+            def emit_warning(level):
+                self.dbusmock.EmitWarning(level)
+                return GLib.SOURCE_CONTINUE
+
+            idle_id = GLib.idle_add(emit_warning, 0)
+            while self.last_warning != 0:
+                self.main_context.iteration(True)
+            GLib.source_remove(idle_id)
+
         def tearDown(self):
             self.p_mock.terminate()
             self.p_mock.wait()
 
-        def assertEventually(self, condition, message=None, timeout=50):
+        def assertEventually(self, condition, message=None, timeout=5):
             '''Assert that condition function eventually returns True.
 
-            Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
+            Timeout is in seconds, defaulting to 5 seconds. message is
             printed on failure.
             '''
-            while timeout >= 0:
-                context = GLib.MainContext.default()
-                while context.iteration(False):
-                    pass
-                if condition():
-                    break
-                timeout -= 1
-                time.sleep(0.1)
-            else:
-                self.fail(message or 'timed out waiting for ' + str(condition))
+            if not message:
+                message = 'timed out waiting for ' + str(condition)
+
+            def timed_out_cb(message):
+                self.fail(message)
+                return GLib.SOURCE_REMOVE
+
+            timeout_source = GLib.timeout_source_new_seconds(timeout)
+            timeout_source.set_callback(timed_out_cb, message)
+            timeout_source.attach(self.main_context)
+
+            while not condition():
+                self.main_context.iteration(True)
+
+            timeout_source.destroy()
 
         def memory_warning_cb(self, monitor, level):
+            print("Received memory warning signal, level", level)
             self.last_warning = level
             self.main_context.wakeup()
 
         def test_low_memory_warning_signal(self):
             '''LowMemoryWarning signal'''
 
-            # Wait 2 seconds
-            timeout = 2
-            while timeout > 0:
-                time.sleep(0.5)
-                timeout -= 0.5
-                self.main_context.iteration(False)
-
             self.dbusmock.EmitWarning(100)
             # Wait 2 seconds or until warning
-            self.assertEventually(lambda: self.last_warning == 100, "'100' low-memory warning not received", 20)
+            self.assertEventually(lambda: self.last_warning == 100, "'100' low-memory warning not received", 2)
 
             self.dbusmock.EmitWarning(255)
             # Wait 2 seconds or until warning
-            self.assertEventually(lambda: self.last_warning == 255, "'255' low-memory warning not received", 20)
+            self.assertEventually(lambda: self.last_warning == 255, "'255' low-memory warning not received", 2)
 
 except ImportError as e:
     @unittest.skip("Cannot import %s" % e.name)
index 7fdf3c7..5bcc830 100755 (executable)
@@ -16,7 +16,6 @@ import sys
 import subprocess
 import fcntl
 import os
-import time
 
 import taptestrunner
 
@@ -80,26 +79,44 @@ try:
             self.mainloop = GLib.MainLoop()
             self.main_context = self.mainloop.get_context()
 
+            # The LowMemoryMonitor API is stateless: it doesn’t expose any
+            # properties, just a warning signal. Emit the signal in a loop until
+            # the GMemoryMonitor instance has initialised and synchronised to
+            # the right state.
+            def emit_warning(level):
+                self.dbusmock.EmitWarning(level)
+                return GLib.SOURCE_CONTINUE
+
+            idle_id = GLib.idle_add(emit_warning, 0)
+            while self.last_warning != 0:
+                self.main_context.iteration(True)
+            GLib.source_remove(idle_id)
+
         def tearDown(self):
             self.p_mock.terminate()
             self.p_mock.wait()
 
-        def assertEventually(self, condition, message=None, timeout=50):
+        def assertEventually(self, condition, message=None, timeout=5):
             '''Assert that condition function eventually returns True.
 
-            Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
+            Timeout is in seconds, defaulting to 5 seconds. message is
             printed on failure.
             '''
-            while timeout >= 0:
-                context = GLib.MainContext.default()
-                while context.iteration(False):
-                    pass
-                if condition():
-                    break
-                timeout -= 1
-                time.sleep(0.1)
-            else:
-                self.fail(message or 'timed out waiting for ' + str(condition))
+            if not message:
+                message = 'timed out waiting for ' + str(condition)
+
+            def timed_out_cb(message):
+                self.fail(message)
+                return GLib.SOURCE_REMOVE
+
+            timeout_source = GLib.timeout_source_new_seconds(timeout)
+            timeout_source.set_callback(timed_out_cb, message)
+            timeout_source.attach(self.main_context)
+
+            while not condition():
+                self.main_context.iteration(True)
+
+            timeout_source.destroy()
 
         def portal_memory_warning_cb(self, monitor, level):
             self.last_warning = level
@@ -108,20 +125,13 @@ try:
         def test_low_memory_warning_portal_signal(self):
             '''LowMemoryWarning signal'''
 
-            # Wait 2 seconds
-            timeout = 2
-            while timeout > 0:
-                time.sleep(0.5)
-                timeout -= 0.5
-                self.main_context.iteration(False)
-
             self.dbusmock.EmitWarning(100)
             # Wait 2 seconds or until warning
-            self.assertEventually(lambda: self.last_warning == 100, "'100' low-memory warning not received", 20)
+            self.assertEventually(lambda: self.last_warning == 100, "'100' low-memory warning not received", 2)
 
             self.dbusmock.EmitWarning(255)
             # Wait 2 seconds or until warning
-            self.assertEventually(lambda: self.last_warning == 255, "'255' low-memory warning not received", 20)
+            self.assertEventually(lambda: self.last_warning == 255, "'255' low-memory warning not received", 2)
 
 except ImportError as e:
     @unittest.skip("Cannot import %s" % e.name)
index 06e594f..f955afc 100755 (executable)
@@ -16,7 +16,6 @@ import sys
 import subprocess
 import fcntl
 import os
-import time
 
 import taptestrunner
 
@@ -58,6 +57,7 @@ try:
             self.power_saver_enabled = False
             self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE)
             self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default()
+            assert("GPowerProfileMonitorDBus" in str(self.power_profile_monitor))
             self.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb)
             self.mainloop = GLib.MainLoop()
             self.main_context = self.mainloop.get_context()
@@ -66,22 +66,27 @@ try:
             self.p_mock.terminate()
             self.p_mock.wait()
 
-        def assertEventually(self, condition, message=None, timeout=50):
+        def assertEventually(self, condition, message=None, timeout=5):
             '''Assert that condition function eventually returns True.
 
-            Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
+            Timeout is in seconds, defaulting to 5 seconds. message is
             printed on failure.
             '''
-            while timeout >= 0:
-                context = GLib.MainContext.default()
-                while context.iteration(False):
-                    pass
-                if condition():
-                    break
-                timeout -= 1
-                time.sleep(0.1)
-            else:
-                self.fail(message or 'timed out waiting for ' + str(condition))
+            if not message:
+                message = 'timed out waiting for ' + str(condition)
+
+            def timed_out_cb(message):
+                self.fail(message)
+                return GLib.SOURCE_REMOVE
+
+            timeout_source = GLib.timeout_source_new_seconds(timeout)
+            timeout_source.set_callback(timed_out_cb, message)
+            timeout_source.attach(self.main_context)
+
+            while not condition():
+                self.main_context.iteration(True)
+
+            timeout_source.destroy()
 
         def power_saver_enabled_cb(self, spec, data):
             self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled()
@@ -92,10 +97,10 @@ try:
 
             self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False)
             self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1))
-            self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10)
+            self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 1)
 
             self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1))
-            self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10)
+            self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 1)
 
 except ImportError as e:
     @unittest.skip("Cannot import %s" % e.name)
index d094ada..3bb0baf 100755 (executable)
@@ -16,7 +16,6 @@ import sys
 import subprocess
 import fcntl
 import os
-import time
 
 import taptestrunner
 
@@ -90,22 +89,27 @@ try:
             self.p_mock.terminate()
             self.p_mock.wait()
 
-        def assertEventually(self, condition, message=None, timeout=50):
+        def assertEventually(self, condition, message=None, timeout=5):
             '''Assert that condition function eventually returns True.
 
-            Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
+            Timeout is in seconds, defaulting to 5 seconds. message is
             printed on failure.
             '''
-            while timeout >= 0:
-                context = GLib.MainContext.default()
-                while context.iteration(False):
-                    pass
-                if condition():
-                    break
-                timeout -= 1
-                time.sleep(0.1)
-            else:
-                self.fail(message or 'timed out waiting for ' + str(condition))
+            if not message:
+                message = 'timed out waiting for ' + str(condition)
+
+            def timed_out_cb(message):
+                self.fail(message)
+                return GLib.SOURCE_REMOVE
+
+            timeout_source = GLib.timeout_source_new_seconds(timeout)
+            timeout_source.set_callback(timed_out_cb, message)
+            timeout_source.attach(self.main_context)
+
+            while not condition():
+                self.main_context.iteration(True)
+
+            timeout_source.destroy()
 
         def power_saver_enabled_cb(self, spec, data):
             self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled()
@@ -116,10 +120,10 @@ try:
 
             self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False)
             self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1))
-            self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10)
+            self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 1)
 
             self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1))
-            self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10)
+            self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 1)
 
         def test_power_profile_power_saver_enabled_portal_default(self):
             '''power-saver-enabled property default value'''
index 105dc69..488446f 100644 (file)
@@ -26,6 +26,7 @@
 
 #define GIO_COMPILATION
 #include "gthreadedresolver.h"
+#include "gthreadedresolver-private.h"
 #undef GIO_COMPILATION
 
 #ifdef HAVE_DN_COMP
index 9c8db33..bd01789 100644 (file)
@@ -155,7 +155,7 @@ cmph_t *brz_new(cmph_config_t *mph, double c)
        // Clustering the keys by graph id.
        if (mph->verbosity)
        {
-               fprintf(stderr, "Partioning the set of keys.\n");       
+               fprintf(stderr, "Partitioning the set of keys.\n");
        }
                
        while(1)
similarity index 57%
rename from girepository/tools/compiler.c
rename to girepository/compiler/compiler.c
index 182b00b..120b474 100644 (file)
@@ -1,41 +1,44 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- * GObject introspection: Typelib compiler
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GObject introspection: Typelib compiler
+ *
  *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+
  * Copyright (C) 2005 Matthias Clasen
+ * Copyright (C) 2024 GNOME Foundation
  *
  * 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.
+ * version 2.1 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * 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 <errno.h>
 #include <locale.h>
 #include <string.h>
 
+#include <gio/gio.h>
+#include <girepository.h>
 #include <glib.h>
 #include <glib/gstdio.h>
-#include <gio/gio.h>
 
 #ifdef G_OS_WIN32
-#include <io.h>
 #include <fcntl.h>
+#include <io.h>
 #endif
 
-#include "girmodule.h"
-#include "girnode.h"
-#include "girparser.h"
-#include "gitypelib-internal.h"
+#include "girmodule-private.h"
+#include "girnode-private.h"
+#include "girparser-private.h"
 
 gchar **includedirs = NULL;
 gchar **input = NULL;
@@ -48,8 +51,8 @@ gboolean verbose = FALSE;
 gboolean show_version = FALSE;
 
 static gboolean
-write_out_typelib (gchar *prefix,
-                  GITypelib *typelib)
+write_out_typelib (gchar     *prefix,
+                   GITypelib *typelib)
 {
   FILE *file;
   gsize written;
@@ -74,28 +77,33 @@ write_out_typelib (gchar *prefix,
   else
     {
       if (prefix)
-       filename = g_strdup_printf ("%s-%s", prefix, output);  
+        {
+          filename = g_strdup_printf ("%s-%s", prefix, output);
+        }
       else
-       filename = g_strdup (output);
+        {
+          filename = g_strdup (output);
+        }
       file_obj = g_file_new_for_path (filename);
       tmp_filename = g_strdup_printf ("%s.tmp", filename);
       tmp_file_obj = g_file_new_for_path (tmp_filename);
       file = g_fopen (tmp_filename, "wb");
 
       if (file == NULL)
-       {
+        {
           g_fprintf (stderr, "failed to open '%s': %s\n",
                      tmp_filename, g_strerror (errno));
           goto out;
-       }
+        }
     }
 
   written = fwrite (typelib->data, 1, typelib->len, file);
-  if (written < typelib->len) {
-    g_fprintf (stderr, "ERROR: Could not write the whole output: %s",
-              strerror(errno));
-    goto out;
-  }
+  if (written < typelib->len)
+    {
+      g_fprintf (stderr, "ERROR: Could not write the whole output: %s",
+                 strerror (errno));
+      goto out;
+    }
 
   if (output != NULL)
     fclose (file);
@@ -103,9 +111,9 @@ write_out_typelib (gchar *prefix,
     {
       if (!g_file_move (tmp_file_obj, file_obj, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error))
         {
-         g_fprintf (stderr, "ERROR: failed to rename %s to %s: %s", tmp_filename, filename, error->message);
+          g_fprintf (stderr, "ERROR: failed to rename %s to %s: %s", tmp_filename, filename, error->message);
           g_clear_error (&error);
-         goto out;
+          goto out;
         }
     }
   success = TRUE;
@@ -120,38 +128,35 @@ out:
 
 GLogLevelFlags logged_levels;
 
-static void log_handler (const gchar *log_domain,
-                        GLogLevelFlags log_level,
-                        const gchar *message,
-                        gpointer user_data)
+static void
+log_handler (const gchar   *log_domain,
+             GLogLevelFlags log_level,
+             const gchar   *message,
+             gpointer       user_data)
 {
-  
   if (log_level & logged_levels)
     g_log_default_handler (log_domain, log_level, message, user_data);
 }
 
-static GOptionEntry options[] = 
-{
-  { "includedir", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &includedirs, "include directories in GIR search path", NULL }, 
-  { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output, "output file", "FILE" }, 
-  { "module", 'm', 0, G_OPTION_ARG_STRING, &mname, "module to compile", "NAME" }, 
-  { "shared-library", 'l', 0, G_OPTION_ARG_FILENAME_ARRAY, &shlibs, "shared library", "FILE" }, 
-  { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "show debug messages", NULL }, 
-  { "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose, "show verbose messages", NULL }, 
+static GOptionEntry options[] = {
+  { "includedir", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &includedirs, "include directories in GIR search path", NULL },
+  { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output, "output file", "FILE" },
+  { "module", 'm', 0, G_OPTION_ARG_STRING, &mname, "module to compile", "NAME" },
+  { "shared-library", 'l', 0, G_OPTION_ARG_FILENAME_ARRAY, &shlibs, "shared library", "FILE" },
+  { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "show debug messages", NULL },
+  { "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose, "show verbose messages", NULL },
   { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, "show program's version number and exit", NULL },
   { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &input, NULL, NULL },
-  { NULL, }
+  G_OPTION_ENTRY_NULL
 };
 
 int
-main (int argc, char ** argv)
+main (int argc, char **argv)
 {
   GOptionContext *context;
   GError *error = NULL;
-  GIrParser *parser;
-  GIrModule *module;
-  gint i;
-  g_typelib_check_sanity ();
+  GIIrParser *parser;
+  GIIrModule *module;
 
   setlocale (LC_ALL, "");
 
@@ -169,7 +174,7 @@ main (int argc, char ** argv)
       return 1;
     }
 
-  logged_levels = G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_DEBUG);
+  logged_levels = G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_DEBUG);
   if (debug)
     logged_levels = logged_levels | G_LOG_LEVEL_DEBUG;
   if (verbose)
@@ -180,35 +185,31 @@ main (int argc, char ** argv)
 
   if (show_version)
     {
-      g_printf ("g-ir-compiler %u.%u.%u\n",
-                GI_MAJOR_VERSION, GI_MINOR_VERSION, GI_MICRO_VERSION);
+      g_printf ("gi-compile-repository %u.%u.%u\n",
+                GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
       return 0;
     }
 
-  if (!input) 
-    { 
-      g_fprintf (stderr, "no input files\n"); 
+  if (!input)
+    {
+      g_fprintf (stderr, "no input files\n");
 
       return 1;
     }
 
-  g_debug ("[parsing] start, %d includes", 
-          includedirs ? g_strv_length (includedirs) : 0);
+  g_debug ("[parsing] start, %d includes",
+           includedirs ? g_strv_length (includedirs) : 0);
 
-  if (includedirs != NULL)
-    for (i = 0; includedirs[i]; i++)
-      g_irepository_prepend_search_path (includedirs[i]);
+  parser = gi_ir_parser_new ();
 
-  parser = _g_ir_parser_new ();
+  gi_ir_parser_set_includes (parser, (const char *const *) includedirs);
 
-  _g_ir_parser_set_includes (parser, (const char*const*) includedirs);
-
-  module = _g_ir_parser_parse_file (parser, input[0], &error);
-  if (module == NULL) 
+  module = gi_ir_parser_parse_file (parser, input[0], &error);
+  if (module == NULL)
     {
-      g_fprintf (stderr, "error parsing file %s: %s\n", 
-                input[0], error->message);
-      
+      g_fprintf (stderr, "error parsing file %s: %s\n",
+                 input[0], error->message);
+
       return 1;
     }
 
@@ -217,36 +218,36 @@ main (int argc, char ** argv)
   g_debug ("[building] start");
 
   {
-      GITypelib *typelib;
-
-      if (shlibs)
-       {
-          if (module->shared_library)
-           g_free (module->shared_library);
-          module->shared_library = g_strjoinv (",", shlibs);
-       }
-
-      g_debug ("[building] module %s", module->name);
-
-      typelib = _g_ir_module_build_typelib (module);
-      if (typelib == NULL)
-       g_error ("Failed to build typelib for module '%s'\n", module->name);
-      if (!g_typelib_validate (typelib, &error))
-       g_error ("Invalid typelib for module '%s': %s", 
-                module->name, error->message);
-
-      if (!write_out_typelib (NULL, typelib))
-       return 1;
-      g_typelib_free (typelib);
-      typelib = NULL;
-    }
+    GITypelib *typelib = NULL;
+
+    if (shlibs)
+      {
+        if (module->shared_library)
+          g_free (module->shared_library);
+        module->shared_library = g_strjoinv (",", shlibs);
+      }
+
+    g_debug ("[building] module %s", module->name);
+
+    typelib = gi_ir_module_build_typelib (module);
+    if (typelib == NULL)
+      g_error ("Failed to build typelib for module '%s'\n", module->name);
+    if (!gi_typelib_validate (typelib, &error))
+      g_error ("Invalid typelib for module '%s': %s",
+               module->name, error->message);
+
+    if (!write_out_typelib (NULL, typelib))
+      return 1;
+
+    g_clear_pointer (&typelib, gi_typelib_unref);
+  }
 
   g_debug ("[building] done");
 
 #if 0
   /* No point */
-  _g_ir_parser_free (parser);
-#endif  
+  gi_ir_parser_free (parser);
+#endif
 
-  return 0; 
+  return 0;
 }
diff --git a/girepository/compiler/meson.build b/girepository/compiler/meson.build
new file mode 100644 (file)
index 0000000..f724f32
--- /dev/null
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# SPDX-FileCopyrightText: 2024 GNOME Foundation
+
+custom_c_args = [
+  '-DG_LOG_DOMAIN="GLib-GirCompiler"',  
+]
+
+if cc.get_id() != 'msvc'
+  custom_c_args = cc.get_supported_arguments([
+    '-Wno-old-style-definition',
+    '-Wno-cast-align',
+    '-Wno-unused-parameter',
+    '-Wno-duplicated-branches',
+  ])
+endif
+
+gicompilerepository = executable('gi-compile-repository', 'compiler.c',
+  dependencies: [
+    libgirepository_dep,
+    libgirepository_internals_dep,
+    libgio_dep,
+  ],
+  install: true,
+  c_args: custom_c_args,
+)
+
+# Only override the user-installed compiler if we need to generate the GIRs
+# GLib, Gio, and GObject...
+if enable_gir
+  # Replace the default g-ir-compiler target with the version we
+  # just built.
+  meson.override_find_program('g-ir-compiler', gicompilerepository)
+endif
diff --git a/girepository/decompiler/decompiler.c b/girepository/decompiler/decompiler.c
new file mode 100644 (file)
index 0000000..aad3cf5
--- /dev/null
@@ -0,0 +1,136 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ * GObject introspection: IDL generator
+ *
+ * Copyright (C) 2005 Matthias Clasen
+ * Copyright (C) 2008,2009 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gmodule.h>
+#include <girepository.h>
+
+#include "girwriter-private.h"
+#include "gitypelib-internal.h"
+
+int
+main (int argc, char *argv[])
+{
+  GIRepository *repository = NULL;
+  gboolean shlib = FALSE;
+  gchar *output = NULL;
+  gchar **includedirs = NULL;
+  gboolean show_all = FALSE;
+  gchar **input = NULL;
+  GOptionContext *context;
+  GError *error = NULL;
+  gboolean needs_prefix;
+  gboolean show_version = FALSE;
+  gint i;
+  GOptionEntry options[] =
+    {
+      { "shlib", 0, 0, G_OPTION_ARG_NONE, &shlib, "handle typelib embedded in shlib", NULL },
+      { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output, "output file", "FILE" },
+      { "includedir", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &includedirs, "include directories in GIR search path", NULL },
+      { "all", 0, 0, G_OPTION_ARG_NONE, &show_all, "show all available information", NULL, },
+      { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, "show program's version number and exit", NULL },
+      { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &input, NULL, NULL },
+      G_OPTION_ENTRY_NULL
+    };
+
+  g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
+
+  setlocale (LC_ALL, "");
+
+  context = g_option_context_new ("");
+  g_option_context_add_main_entries (context, options, NULL);
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      g_fprintf (stderr, "failed to parse: %s\n", error->message);
+      g_error_free (error);
+      return 1;
+    }
+
+  if (show_version)
+    {
+      g_printf ("gi-decompile-typelib %u.%u.%u\n",
+                GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
+      return 0;
+    }
+
+  if (!input)
+    {
+      g_fprintf (stderr, "no input files\n");
+
+      return 1;
+    }
+
+  repository = gi_repository_new ();
+
+  if (includedirs != NULL)
+    for (i = 0; includedirs[i]; i++)
+      gi_repository_prepend_search_path (repository, includedirs[i]);
+
+  for (i = 0; input[i]; i++)
+    {
+      const char *namespace;
+      GMappedFile *mfile = NULL;
+      GBytes *bytes = NULL;
+      GITypelib *typelib;
+
+      error = NULL;
+      mfile = g_mapped_file_new (input[i], FALSE, &error);
+      if (!mfile)
+       g_error ("failed to read '%s': %s", input[i], error->message);
+
+      bytes = g_mapped_file_get_bytes (mfile);
+      g_clear_pointer (&mfile, g_mapped_file_unref);
+
+      if (input[i + 1] && output)
+       needs_prefix = TRUE;
+      else
+       needs_prefix = FALSE;
+
+      typelib = gi_typelib_new_from_bytes (bytes, &error);
+      if (!typelib)
+       g_error ("failed to create typelib '%s': %s", input[i], error->message);
+
+      namespace = gi_repository_load_typelib (repository, typelib, 0, &error);
+      if (namespace == NULL)
+       g_error ("failed to load typelib: %s", error->message);
+
+      gi_ir_writer_write (output, namespace, needs_prefix, show_all);
+
+      /* when writing to stdout, stop after the first module */
+      if (input[i + 1] && !output)
+       {
+         g_fprintf (stderr, "warning, %d modules omitted\n",
+                    g_strv_length (input) - 1);
+
+         break;
+       }
+    }
+
+  g_clear_object (&repository);
+
+  return 0;
+}
diff --git a/girepository/decompiler/meson.build b/girepository/decompiler/meson.build
new file mode 100644 (file)
index 0000000..cb45532
--- /dev/null
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# SPDX-FileCopyrightText: 2024 GNOME Foundation
+
+custom_c_args = [
+  '-DG_LOG_DOMAIN="GLib-GirDecompiler"',
+]
+
+if cc.get_id() != 'msvc'
+  custom_c_args = cc.get_supported_arguments([
+    '-Wno-old-style-definition',
+    '-Wno-cast-align',
+    '-Wno-unused-parameter',
+    '-Wno-duplicated-branches',
+  ])
+endif
+
+gidecompiletypelib = executable('gi-decompile-typelib', 'decompiler.c',
+  dependencies: [
+    libgirepository_dep,
+    libgirepository_internals_dep,
+    libgio_dep,
+  ],
+  install: true,
+  c_args: custom_c_args,
+)
index 17cdb47..8728fd0 100644 (file)
@@ -344,7 +344,7 @@ gi_arg_info_get_type_info (GIArgInfo *info)
 }
 
 /**
- * gi_arg_info_load_type:
+ * gi_arg_info_load_type_info:
  * @info: a #GIArgInfo
  * @type: (out caller-allocates): Initialized with information about type of @info
  *
@@ -354,18 +354,21 @@ gi_arg_info_get_type_info (GIArgInfo *info)
  *
  * The initialized @type must not be referenced after @info is deallocated.
  *
+ * Once you are done with @type, it must be cleared using
+ * [method@GIRepository.BaseInfo.clear].
+ *
  * Since: 2.80
  */
 void
-gi_arg_info_load_type (GIArgInfo  *info,
-                       GITypeInfo *type)
+gi_arg_info_load_type_info (GIArgInfo  *info,
+                            GITypeInfo *type)
 {
   GIRealInfo *rinfo = (GIRealInfo*) info;
 
   g_return_if_fail (info != NULL);
   g_return_if_fail (GI_IS_ARG_INFO (info));
 
-  gi_type_info_init ((GIBaseInfo *) type, (GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (ArgBlob, arg_type));
+  gi_type_info_init (type, (GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (ArgBlob, arg_type));
 }
 
 void
index 5223f25..e7a61b1 100644 (file)
@@ -95,6 +95,6 @@ GI_AVAILABLE_IN_ALL
 GITypeInfo *           gi_arg_info_get_type_info          (GIArgInfo *info);
 
 GI_AVAILABLE_IN_ALL
-void                   gi_arg_info_load_type              (GIArgInfo *info,
+void                   gi_arg_info_load_type_info         (GIArgInfo *info,
                                                            GITypeInfo *type);
 G_END_DECLS
index aba0e19..96c7715 100644 (file)
@@ -26,6 +26,7 @@
 #include <glib.h>
 #include <glib-object.h>
 
+#include "girepository-private.h"
 #include "gitypes.h"
 
 G_BEGIN_DECLS
@@ -50,4 +51,11 @@ GType           gi_base_info_type_register_static    (const char     *type_name,
                                                       GType           parent_type,
                                                       GTypeFlags      type_flags);
 
+GIInfoType             gi_base_info_get_info_type    (GIBaseInfo   *info);
+
+GIBaseInfo *           gi_base_info_new              (GIInfoType  type,
+                                                      GIBaseInfo *container,
+                                                      GITypelib  *typelib,
+                                                      size_t      offset);
+
 G_END_DECLS
index f656367..d24cd4a 100644 (file)
@@ -123,12 +123,9 @@ value_base_info_lcopy_value (const GValue *value,
 static void
 gi_base_info_finalize (GIBaseInfo *self)
 {
-  if (self->container && self->container->ref_count != INVALID_REFCOUNT)
+  if (self->ref_count != INVALID_REFCOUNT &&
+      self->container && self->container->ref_count != INVALID_REFCOUNT)
     gi_base_info_unref (self->container);
-
-  g_clear_object (&self->repository);
-
-  g_type_free_instance ((GTypeInstance *) self);
 }
 
 static void
@@ -141,6 +138,9 @@ gi_base_info_class_init (GIBaseInfoClass *klass)
 static void
 gi_base_info_init (GIBaseInfo *self)
 {
+  /* Initialise a dynamically allocated #GIBaseInfo’s members.
+   *
+   * This function *must* be kept in sync with gi_info_init(). */
   g_atomic_ref_count_init (&self->ref_count);
 }
 
@@ -257,7 +257,6 @@ GI_DEFINE_BASE_INFO_TYPE (gi_enum_info, GI_INFO_TYPE_ENUM)
 GI_DEFINE_BASE_INFO_TYPE (gi_flags_info, GI_INFO_TYPE_FLAGS)
 GI_DEFINE_BASE_INFO_TYPE (gi_object_info, GI_INFO_TYPE_OBJECT)
 GI_DEFINE_BASE_INFO_TYPE (gi_interface_info, GI_INFO_TYPE_INTERFACE)
-GI_DEFINE_BASE_INFO_TYPE (gi_boxed_info, GI_INFO_TYPE_BOXED)
 GI_DEFINE_BASE_INFO_TYPE (gi_constant_info, GI_INFO_TYPE_CONSTANT)
 GI_DEFINE_BASE_INFO_TYPE (gi_value_info, GI_INFO_TYPE_VALUE)
 GI_DEFINE_BASE_INFO_TYPE (gi_signal_info, GI_INFO_TYPE_SIGNAL)
@@ -296,7 +295,6 @@ gi_base_info_init_types (void)
           { GI_INFO_TYPE_FLAGS, "GIFlagsInfo", sizeof (GIFlagsInfo), gi_flags_info_class_init, GI_INFO_TYPE_ENUM, G_TYPE_FLAG_NONE },
           { GI_INFO_TYPE_OBJECT, "GIObjectInfo", sizeof (GIObjectInfo), gi_object_info_class_init, GI_INFO_TYPE_REGISTERED_TYPE, G_TYPE_FLAG_NONE },
           { GI_INFO_TYPE_INTERFACE, "GIInterfaceInfo", sizeof (GIInterfaceInfo), gi_interface_info_class_init, GI_INFO_TYPE_REGISTERED_TYPE, G_TYPE_FLAG_NONE },
-          { GI_INFO_TYPE_BOXED, "GIBoxedInfo", sizeof (GIBoxedInfo), gi_boxed_info_class_init, GI_INFO_TYPE_REGISTERED_TYPE, G_TYPE_FLAG_NONE },
           { GI_INFO_TYPE_CONSTANT, "GIConstantInfo", sizeof (GIConstantInfo), gi_constant_info_class_init, 0, G_TYPE_FLAG_NONE },
           { GI_INFO_TYPE_VALUE, "GIValueInfo", sizeof (GIValueInfo), gi_value_info_class_init, 0, G_TYPE_FLAG_NONE },
           { GI_INFO_TYPE_SIGNAL, "GISignalInfo", sizeof (GISignalInfo), gi_signal_info_class_init, GI_INFO_TYPE_CALLABLE, G_TYPE_FLAG_NONE },
@@ -353,13 +351,20 @@ gi_info_new_full (GIInfoType    type,
   if (container && container->ref_count != INVALID_REFCOUNT)
     gi_base_info_ref (info->container);
 
-  info->repository = g_object_ref (repository);
+  /* Don’t keep a strong ref, since the repository keeps a cache of #GIBaseInfos
+   * and holds refs on them. If we kept a ref here, there’d be a cycle.
+   * Don’t keep a weak ref either, as that would make creating/destroying a
+   * #GIBaseInfo noticeably more expensive, and infos are performance critical
+   * for bindings.
+   * As stated in the documentation, the mitigation here is to require the user
+   * to keep the #GIRepository alive longer than any of its #GIBaseInfos. */
+  info->repository = repository;
 
   return (GIBaseInfo*)info;
 }
 
 /**
- * gi_info_new:
+ * gi_base_info_new:
  * @type: type of the info to create
  * @container: (nullable): info which contains this one
  * @typelib: typelib containing the info
@@ -373,10 +378,10 @@ gi_info_new_full (GIInfoType    type,
  * Since: 2.80
  */
 GIBaseInfo *
-gi_info_new (GIInfoType     type,
-             GIBaseInfo    *container,
-             GITypelib     *typelib,
-             size_t         offset)
+gi_base_info_new (GIInfoType  type,
+                  GIBaseInfo *container,
+                  GITypelib  *typelib,
+                  size_t      offset)
 {
   return gi_info_new_full (type, ((GIRealInfo*)container)->repository, container, typelib, offset);
 }
@@ -397,7 +402,7 @@ gi_info_new (GIInfoType     type,
  */
 void
 gi_info_init (GIRealInfo   *info,
-              GIInfoType    type,
+              GType         type,
               GIRepository *repository,
               GIBaseInfo   *container,
               GITypelib    *typelib,
@@ -405,6 +410,20 @@ gi_info_init (GIRealInfo   *info,
 {
   memset (info, 0, sizeof (GIRealInfo));
 
+  /* Evil setup of a stack allocated #GTypeInstance. This is not something it’s
+   * really designed to do.
+   *
+   * This function *must* be kept in sync with gi_base_info_init(), which is
+   * the equivalent function for dynamically allocated types. */
+  info->parent_instance.g_class = g_type_class_ref (type);
+
+  /* g_type_create_instance() calls the #GInstanceInitFunc for each of the
+   * parent types, down to (and including) @type. We don’t need to do that, as
+   * #GIBaseInfo is fundamental so doesn’t have a parent type, the instance init
+   * function for #GIBaseInfo is gi_base_info_init() (which only sets the
+   * refcount, which we already do here), and subtypes of #GIBaseInfo don’t have
+   * instance init functions (see gi_base_info_type_register_static()). */
+
   /* Invalid refcount used to flag stack-allocated infos */
   info->ref_count = INVALID_REFCOUNT;
   info->typelib = typelib;
@@ -417,6 +436,34 @@ gi_info_init (GIRealInfo   *info,
   info->repository = repository;
 }
 
+/**
+ * gi_base_info_clear:
+ * @info: (type GIRepository.BaseInfo): a #GIBaseInfo
+ *
+ * Clears memory allocated internally by a stack-allocated
+ * [type@GIRepository.BaseInfo].
+ *
+ * This does not deallocate the [type@GIRepository.BaseInfo] struct itself.
+ *
+ * This must only be called on stack-allocated [type@GIRepository.BaseInfo]s.
+ * Use [method@GIRepository.BaseInfo.unref] for heap-allocated ones.
+ *
+ * Since: 2.80
+ */
+void
+gi_base_info_clear (void *info)
+{
+  GIBaseInfo *rinfo = (GIBaseInfo *) info;
+
+  g_return_if_fail (GI_IS_BASE_INFO (rinfo));
+
+  g_assert (rinfo->ref_count == INVALID_REFCOUNT);
+
+  GI_BASE_INFO_GET_CLASS (info)->finalize (rinfo);
+
+  g_type_class_unref (rinfo->parent_instance.g_class);
+}
+
 GIBaseInfo *
 gi_info_from_entry (GIRepository *repository,
                     GITypelib    *typelib,
@@ -426,7 +473,8 @@ gi_info_from_entry (GIRepository *repository,
   DirEntry *entry = gi_typelib_get_dir_entry (typelib, index);
 
   if (entry->local)
-    result = gi_info_new_full (entry->blob_type, repository, NULL, typelib, entry->offset);
+    result = gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
+                               repository, NULL, typelib, entry->offset);
   else
     {
       const char *namespace = gi_typelib_get_string (typelib, entry->offset);
@@ -461,12 +509,27 @@ gi_type_info_new (GIBaseInfo *container,
 {
   SimpleTypeBlob *type = (SimpleTypeBlob *)&typelib->data[offset];
 
-  return (GITypeInfo *) gi_info_new (GI_INFO_TYPE_TYPE, container, typelib,
-                                     (type->flags.reserved == 0 && type->flags.reserved2 == 0) ? offset : type->offset);
+  return (GITypeInfo *) gi_base_info_new (GI_INFO_TYPE_TYPE, container, typelib,
+                                          (type->flags.reserved == 0 && type->flags.reserved2 == 0) ? offset : type->offset);
 }
 
+/*< private >
+ * gi_type_info_init:
+ * @info: (out caller-allocates): caller-allocated #GITypeInfo to populate
+ * @container: (nullable): info which contains this one
+ * @typelib: typelib containing the info
+ * @offset: offset of the info within @typelib, in bytes
+ *
+ * Initialise a stack-allocated #GITypeInfo representing an object of type
+ * [type@GIRepository.TypeInfo] from @offset of @typelib.
+ *
+ * This is a specialised form of [func@GIRepository.info_init] for type
+ * information.
+ *
+ * Since: 2.80
+ */
 void
-gi_type_info_init (GIBaseInfo *info,
+gi_type_info_init (GITypeInfo *info,
                    GIBaseInfo *container,
                    GITypelib  *typelib,
                    uint32_t    offset)
@@ -474,7 +537,7 @@ gi_type_info_init (GIBaseInfo *info,
   GIRealInfo *rinfo = (GIRealInfo*)container;
   SimpleTypeBlob *type = (SimpleTypeBlob *)&typelib->data[offset];
 
-  gi_info_init ((GIRealInfo*)info, GI_INFO_TYPE_TYPE, rinfo->repository, container, typelib,
+  gi_info_init ((GIRealInfo*)info, GI_TYPE_TYPE_INFO, rinfo->repository, container, typelib,
                 (type->flags.reserved == 0 && type->flags.reserved2 == 0) ? offset : type->offset);
 }
 
@@ -544,6 +607,9 @@ gi_base_info_ref (void *info)
  * Decreases the reference count of @info. When its reference count
  * drops to 0, the info is freed.
  *
+ * This must not be called on stack-allocated [type@GIRepository.BaseInfo]s —
+ * use [method@GIRepository.BaseInfo.clear] for that.
+ *
  * Since: 2.80
  */
 void
@@ -556,7 +622,10 @@ gi_base_info_unref (void *info)
   g_assert (rinfo->ref_count > 0 && rinfo->ref_count != INVALID_REFCOUNT);
 
   if (g_atomic_ref_count_dec (&rinfo->ref_count))
-    GI_BASE_INFO_GET_CLASS (info)->finalize (info);
+    {
+      GI_BASE_INFO_GET_CLASS (info)->finalize (info);
+      g_type_free_instance ((GTypeInstance *) info);
+    }
 }
 
 /**
@@ -580,7 +649,7 @@ gi_base_info_get_info_type (GIBaseInfo *info)
  *
  * Obtain the name of the @info.
  *
- * What the name represents depends on the [type@GIRepository.InfoType] of the
+ * What the name represents depends on the type of the
  * @info. For instance for [class@GIRepository.FunctionInfo] it is the name of
  * the function.
  *
@@ -597,7 +666,6 @@ gi_base_info_get_name (GIBaseInfo *info)
     case GI_INFO_TYPE_FUNCTION:
     case GI_INFO_TYPE_CALLBACK:
     case GI_INFO_TYPE_STRUCT:
-    case GI_INFO_TYPE_BOXED:
     case GI_INFO_TYPE_ENUM:
     case GI_INFO_TYPE_FLAGS:
     case GI_INFO_TYPE_OBJECT:
@@ -721,7 +789,6 @@ gi_base_info_is_deprecated (GIBaseInfo *info)
     case GI_INFO_TYPE_FUNCTION:
     case GI_INFO_TYPE_CALLBACK:
     case GI_INFO_TYPE_STRUCT:
-    case GI_INFO_TYPE_BOXED:
     case GI_INFO_TYPE_ENUM:
     case GI_INFO_TYPE_FLAGS:
     case GI_INFO_TYPE_OBJECT:
index 4e152fc..4f87b9f 100644 (file)
@@ -95,7 +95,7 @@ GI_AVAILABLE_IN_ALL
 void                   gi_base_info_unref            (void         *info);
 
 GI_AVAILABLE_IN_ALL
-GIInfoType             gi_base_info_get_info_type    (GIBaseInfo   *info);
+void                   gi_base_info_clear            (void         *info);
 
 GI_AVAILABLE_IN_ALL
 const char *           gi_base_info_get_name         (GIBaseInfo   *info);
@@ -126,10 +126,4 @@ GI_AVAILABLE_IN_ALL
 gboolean               gi_base_info_equal            (GIBaseInfo *info1,
                                                       GIBaseInfo *info2);
 
-GI_AVAILABLE_IN_ALL
-GIBaseInfo *           gi_info_new                   (GIInfoType  type,
-                                                      GIBaseInfo *container,
-                                                      GITypelib  *typelib,
-                                                      size_t      offset);
-
 G_END_DECLS
diff --git a/girepository/giboxedinfo.c b/girepository/giboxedinfo.c
deleted file mode 100644 (file)
index 36921e3..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- * GObject introspection: Boxed type implementation
- *
- * Copyright 2024 GNOME Foundation, Inc.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- *
- * 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include <glib.h>
-
-#include <girepository/girepository.h>
-#include "gibaseinfo-private.h"
-#include "girepository-private.h"
-#include "gitypelib-internal.h"
-#include "giboxedinfo.h"
-
-/**
- * GIBoxedInfo:
- *
- * A `GIBoxedInfo` represents a boxed type.
- *
- * There isn’t much you can do with a boxed type; `GIBoxedInfo` exists mainly to
- * tag the type.
- *
- * Since: 2.80
- */
-
-void
-gi_boxed_info_class_init (gpointer g_class,
-                          gpointer class_data)
-{
-  GIBaseInfoClass *info_class = g_class;
-
-  info_class->info_type = GI_INFO_TYPE_BOXED;
-}
diff --git a/girepository/giboxedinfo.h b/girepository/giboxedinfo.h
deleted file mode 100644 (file)
index f94987e..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- * GObject introspection: Boxed types
- *
- * Copyright 2024 GNOME Foundation, Inc.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- *
- * 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#pragma once
-
-#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION)
-#error "Only <girepository.h> can be included directly."
-#endif
-
-#include <girepository/gitypes.h>
-
-G_BEGIN_DECLS
-
-#define GI_TYPE_BOXED_INFO (gi_boxed_info_get_type ())
-
-/**
- * GI_BOXED_INFO:
- * @info: Info object which is subject to casting.
- *
- * Casts a [type@GIRepository.BoxedInfo] or derived pointer into a
- * `(GIBoxedInfo*)` pointer.
- *
- * Depending on the current debugging level, this function may invoke
- * certain runtime checks to identify invalid casts.
- *
- * Since: 2.80
- */
-#define GI_BOXED_INFO(info) (G_TYPE_CHECK_INSTANCE_CAST ((info), GI_TYPE_BOXED_INFO, GIBoxedInfo))
-
-/**
- * GI_IS_BOXED_INFO:
- * @info: an info structure
- *
- * Checks if @info is a [class@GIRepository.BoxedInfo] (or a derived type).
- *
- * Since: 2.80
- */
-#define GI_IS_BOXED_INFO(info) (G_TYPE_CHECK_INSTANCE_TYPE ((info), GI_TYPE_BOXED_INFO))
-
-G_END_DECLS
index 098a1d3..49430e9 100644 (file)
@@ -172,6 +172,9 @@ gi_callable_info_is_method (GICallableInfo *info)
  *
  * Obtain the return type of a callable item as a [class@GIRepository.TypeInfo].
  *
+ * If the callable doesn’t return anything, a [class@GIRepository.TypeInfo] of
+ * type [enum@GIRepository.TypeTag.VOID] will be returned.
+ *
  * Returns: (transfer full): the [class@GIRepository.TypeInfo]. Free the struct
  *   by calling [method@GIRepository.BaseInfo.unref] when done.
  * Since: 2.80
@@ -201,6 +204,9 @@ gi_callable_info_get_return_type (GICallableInfo *info)
  *
  * The initialized @type must not be referenced after @info is deallocated.
  *
+ * Once you are done with @type, it must be cleared using
+ * [method@GIRepository.BaseInfo.clear].
+ *
  * Since: 2.80
  */
 void
@@ -215,7 +221,7 @@ gi_callable_info_load_return_type (GICallableInfo *info,
 
   offset = signature_offset (info);
 
-  gi_type_info_init ((GIBaseInfo *) type, (GIBaseInfo*)info, rinfo->typelib, offset);
+  gi_type_info_init (type, (GIBaseInfo*)info, rinfo->typelib, offset);
 }
 
 /**
@@ -373,8 +379,8 @@ gi_callable_info_get_arg (GICallableInfo *info,
   offset = signature_offset (info);
   header = (Header *)rinfo->typelib->data;
 
-  return (GIArgInfo *) gi_info_new (GI_INFO_TYPE_ARG, (GIBaseInfo*)info, rinfo->typelib,
-                                    offset + header->signature_blob_size + n * header->arg_blob_size);
+  return (GIArgInfo *) gi_base_info_new (GI_INFO_TYPE_ARG, (GIBaseInfo*)info, rinfo->typelib,
+                                         offset + header->signature_blob_size + n * header->arg_blob_size);
 }
 
 /**
@@ -389,6 +395,9 @@ gi_callable_info_get_arg (GICallableInfo *info,
  *
  * The initialized @arg must not be referenced after @info is deallocated.
  *
+ * Once you are done with @arg, it must be cleared using
+ * [method@GIRepository.BaseInfo.clear].
+ *
  * Since: 2.80
  */
 void
@@ -407,7 +416,7 @@ gi_callable_info_load_arg (GICallableInfo *info,
   offset = signature_offset (info);
   header = (Header *)rinfo->typelib->data;
 
-  gi_info_init ((GIRealInfo*)arg, GI_INFO_TYPE_ARG, rinfo->repository, (GIBaseInfo*)info, rinfo->typelib,
+  gi_info_init ((GIRealInfo*)arg, GI_TYPE_ARG_INFO, rinfo->repository, (GIBaseInfo*)info, rinfo->typelib,
                 offset + header->signature_blob_size + n * header->arg_blob_size);
 }
 
@@ -493,7 +502,7 @@ gi_callable_info_iterate_return_attributes (GICallableInfo   *info,
 /**
  * gi_type_tag_extract_ffi_return_value:
  * @return_tag: [type@GIRepository.TypeTag] of the return value
- * @interface_type: [type@GIRepository.InfoType] of the underlying interface type
+ * @interface_type: [type@GObject.Type] of the underlying interface type
  * @ffi_value: pointer to [type@GIRepository.FFIReturnValue] union containing
  *   the return value from `ffi_call()`
  * @arg: (out caller-allocates): pointer to an allocated
@@ -514,7 +523,7 @@ gi_callable_info_iterate_return_attributes (GICallableInfo   *info,
  */
 void
 gi_type_tag_extract_ffi_return_value (GITypeTag         return_tag,
-                                      GIInfoType        interface_type,
+                                      GType             interface_type,
                                       GIFFIReturnValue *ffi_value,
                                       GIArgument       *arg)
 {
@@ -552,15 +561,11 @@ gi_type_tag_extract_ffi_return_value (GITypeTag         return_tag,
         arg->v_double = ffi_value->v_double;
         break;
     case GI_TYPE_TAG_INTERFACE:
-        switch(interface_type) {
-        case GI_INFO_TYPE_ENUM:
-        case GI_INFO_TYPE_FLAGS:
-            arg->v_int32 = (int32_t) ffi_value->v_long;
-            break;
-        default:
-            arg->v_pointer = (void *) ffi_value->v_pointer;
-            break;
-        }
+        if (interface_type == GI_TYPE_ENUM_INFO ||
+            interface_type == GI_TYPE_FLAGS_INFO)
+          arg->v_int32 = (int32_t) ffi_value->v_long;
+        else
+          arg->v_pointer = (void *) ffi_value->v_pointer;
         break;
     default:
         arg->v_pointer = (void *) ffi_value->v_pointer;
@@ -592,12 +597,12 @@ gi_type_info_extract_ffi_return_value (GITypeInfo       *return_info,
                                        GIArgument       *arg)
 {
   GITypeTag return_tag = gi_type_info_get_tag (return_info);
-  GIInfoType interface_type = GI_INFO_TYPE_INVALID;
+  GType interface_type = G_TYPE_INVALID;
 
   if (return_tag == GI_TYPE_TAG_INTERFACE)
     {
       GIBaseInfo *interface_info = gi_type_info_get_interface (return_info);
-      interface_type = gi_base_info_get_info_type (interface_info);
+      interface_type = G_TYPE_FROM_INSTANCE (interface_info);
       gi_base_info_unref (interface_info);
     }
 
index 1848e8f..e73b1a6 100644 (file)
@@ -124,7 +124,7 @@ gi_enum_info_get_value (GIEnumInfo *info,
   offset = rinfo->offset + header->enum_blob_size
     + n * header->value_blob_size;
 
-  return (GIValueInfo *) gi_info_new (GI_INFO_TYPE_VALUE, (GIBaseInfo*)info, rinfo->typelib, offset);
+  return (GIValueInfo *) gi_base_info_new (GI_INFO_TYPE_VALUE, (GIBaseInfo*)info, rinfo->typelib, offset);
 }
 
 /**
@@ -181,8 +181,8 @@ gi_enum_info_get_method (GIEnumInfo *info,
     + blob->n_values * header->value_blob_size
     + n * header->function_blob_size;
 
-  return (GIFunctionInfo *) gi_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
-                                         rinfo->typelib, offset);
+  return (GIFunctionInfo *) gi_base_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
+                                              rinfo->typelib, offset);
 }
 
 /**
index 69c0fec..819ccf7 100644 (file)
@@ -158,9 +158,9 @@ gi_field_info_get_type_info (GIFieldInfo *info)
 
   if (blob->has_embedded_type)
     {
-      type_info = (GIRealInfo *) gi_info_new (GI_INFO_TYPE_TYPE,
-                                              (GIBaseInfo*)info, rinfo->typelib,
-                                              rinfo->offset + header->field_blob_size);
+      type_info = (GIRealInfo *) gi_base_info_new (GI_INFO_TYPE_TYPE,
+                                                   (GIBaseInfo*)info, rinfo->typelib,
+                                                   rinfo->offset + header->field_blob_size);
       type_info->type_is_embedded = TRUE;
     }
   else
@@ -255,7 +255,7 @@ gi_field_info_get_field (GIFieldInfo *field_info,
           break;
         case GI_TYPE_TAG_ARRAY:
           /* We don't check the array type and that it is fixed-size,
-             we trust g-ir-compiler to do the right thing */
+             we trust gi-compile-repository to do the right thing */
           value->v_pointer = G_STRUCT_MEMBER_P (mem, offset);
           result = TRUE;
           break;
@@ -278,7 +278,6 @@ gi_field_info_get_field (GIFieldInfo *field_info,
               {
               case GI_INFO_TYPE_STRUCT:
               case GI_INFO_TYPE_UNION:
-              case GI_INFO_TYPE_BOXED:
                 /* Needs to be handled by the language binding directly */
                 break;
               case GI_INFO_TYPE_OBJECT:
@@ -464,7 +463,6 @@ gi_field_info_set_field (GIFieldInfo      *field_info,
               {
               case GI_INFO_TYPE_STRUCT:
               case GI_INFO_TYPE_UNION:
-              case GI_INFO_TYPE_BOXED:
                 /* Needs to be handled by the language binding directly */
                 break;
               case GI_INFO_TYPE_OBJECT:
index 9b3b1e8..e50cc00 100644 (file)
@@ -64,8 +64,8 @@ gi_base_info_find_method (GIBaseInfo  *base,
       const char *fname = (const char *)&rinfo->typelib->data[fblob->name];
 
       if (strcmp (name, fname) == 0)
-        return (GIFunctionInfo *) gi_info_new (GI_INFO_TYPE_FUNCTION, base,
-                                               rinfo->typelib, offset);
+        return (GIFunctionInfo *) gi_base_info_new (GI_INFO_TYPE_FUNCTION, base,
+                                                    rinfo->typelib, offset);
 
       offset += header->function_blob_size;
     }
@@ -140,9 +140,6 @@ gi_function_info_get_flags (GIFunctionInfo *info)
   if (blob->wraps_vfunc)
     flags = flags | GI_FUNCTION_WRAPS_VFUNC;
 
-  if (blob->throws)
-    flags = flags | GI_FUNCTION_THROWS;
-
   return flags;
 }
 
index b207d19..1c5289e 100644 (file)
@@ -152,8 +152,8 @@ gi_interface_info_get_property (GIInterfaceInfo *info,
     + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2
     + n * header->property_blob_size;
 
-  return (GIPropertyInfo *) gi_info_new (GI_INFO_TYPE_PROPERTY, (GIBaseInfo*)info,
-                                         rinfo->typelib, offset);
+  return (GIPropertyInfo *) gi_base_info_new (GI_INFO_TYPE_PROPERTY, (GIBaseInfo*)info,
+                                              rinfo->typelib, offset);
 }
 
 /**
@@ -211,8 +211,8 @@ gi_interface_info_get_method (GIInterfaceInfo *info,
     + blob->n_properties * header->property_blob_size
     + n * header->function_blob_size;
 
-  return (GIFunctionInfo *) gi_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
-                                         rinfo->typelib, offset);
+  return (GIFunctionInfo *) gi_base_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
+                                              rinfo->typelib, offset);
 }
 
 /**
@@ -301,8 +301,8 @@ gi_interface_info_get_signal (GIInterfaceInfo *info,
     + blob->n_methods * header->function_blob_size
     + n * header->signal_blob_size;
 
-  return (GISignalInfo *) gi_info_new (GI_INFO_TYPE_SIGNAL, (GIBaseInfo*)info,
-                                       rinfo->typelib, offset);
+  return (GISignalInfo *) gi_base_info_new (GI_INFO_TYPE_SIGNAL, (GIBaseInfo*)info,
+                                            rinfo->typelib, offset);
 }
 
 /**
@@ -398,8 +398,8 @@ gi_interface_info_get_vfunc (GIInterfaceInfo *info,
     + blob->n_signals * header->signal_blob_size
     + n * header->vfunc_blob_size;
 
-  return (GIVFuncInfo *) gi_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*)info,
-                                      rinfo->typelib, offset);
+  return (GIVFuncInfo *) gi_base_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*)info,
+                                           rinfo->typelib, offset);
 }
 
 /**
@@ -499,8 +499,8 @@ gi_interface_info_get_constant (GIInterfaceInfo *info,
     + blob->n_vfuncs * header->vfunc_blob_size
     + n * header->constant_blob_size;
 
-  return (GIConstantInfo *) gi_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info,
-                                         rinfo->typelib, offset);
+  return (GIConstantInfo *) gi_base_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info,
+                                              rinfo->typelib, offset);
 }
 
 /**
index b55390e..6211b90 100644 (file)
@@ -330,7 +330,7 @@ gi_object_info_get_field (GIObjectInfo *info,
 
   offset = gi_object_info_get_field_offset(info, n);
 
-  return (GIFieldInfo *) gi_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib, offset);
+  return (GIFieldInfo *) gi_base_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib, offset);
 }
 
 /**
@@ -388,8 +388,8 @@ gi_object_info_get_property (GIObjectInfo *info,
     + blob->n_field_callbacks * header->callback_blob_size
     + n * header->property_blob_size;
 
-  return (GIPropertyInfo *) gi_info_new (GI_INFO_TYPE_PROPERTY, (GIBaseInfo*)info,
-                                         rinfo->typelib, offset);
+  return (GIPropertyInfo *) gi_base_info_new (GI_INFO_TYPE_PROPERTY, (GIBaseInfo*)info,
+                                              rinfo->typelib, offset);
 }
 
 /**
@@ -450,8 +450,8 @@ gi_object_info_get_method (GIObjectInfo *info,
     + blob->n_properties * header->property_blob_size
     + n * header->function_blob_size;
 
-    return (GIFunctionInfo *) gi_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
-                                           rinfo->typelib, offset);
+    return (GIFunctionInfo *) gi_base_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
+                                                rinfo->typelib, offset);
 }
 
 /**
@@ -496,9 +496,10 @@ gi_object_info_find_method (GIObjectInfo *info,
  * gi_object_info_find_method_using_interfaces:
  * @info: a #GIObjectInfo
  * @name: name of method to obtain
- * @implementor: (out) (transfer full) (optional) (nullable): The implementor of
- *   the interface, or `NULL` to ignore. If no method is found, this will return
- *   `NULL`.
+ * @declarer: (out) (transfer full) (optional) (nullable): The
+ *   [class@GIRepository.ObjectInfo] or [class@GIRepository.InterfaceInfo] which
+ *   declares the method, or `NULL` to ignore. If no method is found, this will
+ *   return `NULL`.
  *
  * Obtain a method of the object given a @name, searching both the
  * object @info and any interfaces it implements.
@@ -516,14 +517,14 @@ gi_object_info_find_method (GIObjectInfo *info,
 GIFunctionInfo *
 gi_object_info_find_method_using_interfaces (GIObjectInfo  *info,
                                              const char    *name,
-                                             GIObjectInfo **implementor)
+                                             GIBaseInfo   **declarer)
 {
   GIFunctionInfo *result = NULL;
-  GIObjectInfo *implementor_result = NULL;
+  GIBaseInfo *declarer_result = NULL;
 
   result = gi_object_info_find_method (info, name);
   if (result)
-    implementor_result = (GIObjectInfo *) gi_base_info_ref ((GIBaseInfo*) info);
+    declarer_result = gi_base_info_ref (info);
 
   if (result == NULL)
     {
@@ -541,17 +542,19 @@ gi_object_info_find_method_using_interfaces (GIObjectInfo  *info,
 
           if (result != NULL)
             {
-              implementor_result = (GIObjectInfo *) iface_info;
+              declarer_result = GI_BASE_INFO (g_steal_pointer (&iface_info));
               break;
             }
           gi_base_info_unref ((GIBaseInfo*) iface_info);
         }
     }
-  if (implementor)
-    *implementor = implementor_result;
-  else if (implementor_result != NULL)
-    gi_base_info_unref ((GIBaseInfo*) implementor_result);
-  return result;
+
+  if (declarer)
+    *declarer = g_steal_pointer (&declarer_result);
+
+  g_clear_pointer (&declarer_result, gi_base_info_unref);
+
+  return g_steal_pointer (&result);
 }
 
 /**
@@ -612,8 +615,8 @@ gi_object_info_get_signal (GIObjectInfo *info,
     + blob->n_methods * header->function_blob_size
     + n * header->signal_blob_size;
 
-  return (GISignalInfo *) gi_info_new (GI_INFO_TYPE_SIGNAL, (GIBaseInfo*)info,
-                                       rinfo->typelib, offset);
+  return (GISignalInfo *) gi_base_info_new (GI_INFO_TYPE_SIGNAL, (GIBaseInfo*)info,
+                                            rinfo->typelib, offset);
 }
 
 /**
@@ -712,8 +715,8 @@ gi_object_info_get_vfunc (GIObjectInfo *info,
     + blob->n_signals * header->signal_blob_size
     + n * header->vfunc_blob_size;
 
-  return (GIVFuncInfo *) gi_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*)info,
-                                      rinfo->typelib, offset);
+  return (GIVFuncInfo *) gi_base_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*)info,
+                                           rinfo->typelib, offset);
 }
 
 /**
@@ -766,9 +769,10 @@ gi_object_info_find_vfunc (GIObjectInfo *info,
  * gi_object_info_find_vfunc_using_interfaces:
  * @info: a #GIObjectInfo
  * @name: name of vfunc to obtain
- * @implementor: (out) (transfer full) (optional) (nullable): The implementor of
- *   the interface, or `NULL` to ignore. If no vfunc is found, this will return
- *   `NULL`.
+ * @declarer: (out) (transfer full) (optional) (nullable): The
+ *   [class@GIRepository.ObjectInfo] or [class@GIRepository.InterfaceInfo] which
+ *   declares the vfunc, or `NULL` to ignore. If no vfunc is found, this will
+ *   return `NULL`.
  *
  * Locate a virtual function slot with name @name, searching both the object
  * @info and any interfaces it implements.
@@ -791,14 +795,14 @@ gi_object_info_find_vfunc (GIObjectInfo *info,
 GIVFuncInfo *
 gi_object_info_find_vfunc_using_interfaces (GIObjectInfo  *info,
                                             const char    *name,
-                                            GIObjectInfo **implementor)
+                                            GIBaseInfo   **declarer)
 {
   GIVFuncInfo *result = NULL;
-  GIObjectInfo *implementor_result = NULL;
+  GIBaseInfo *declarer_result = NULL;
 
   result = gi_object_info_find_vfunc (info, name);
   if (result)
-    implementor_result = (GIObjectInfo *) gi_base_info_ref ((GIBaseInfo*) info);
+    declarer_result = gi_base_info_ref (info);
 
   if (result == NULL)
     {
@@ -816,17 +820,19 @@ gi_object_info_find_vfunc_using_interfaces (GIObjectInfo  *info,
 
           if (result != NULL)
             {
-              implementor_result = (GIObjectInfo *) iface_info;
+              declarer_result = GI_BASE_INFO (g_steal_pointer (&iface_info));
               break;
             }
           gi_base_info_unref ((GIBaseInfo*) iface_info);
         }
     }
-  if (implementor)
-    *implementor = implementor_result;
-  else if (implementor_result != NULL)
-    gi_base_info_unref ((GIBaseInfo*) implementor_result);
-  return result;
+
+  if (declarer)
+    *declarer = g_steal_pointer (&declarer_result);
+
+  g_clear_pointer (&declarer_result, gi_base_info_unref);
+
+  return g_steal_pointer (&result);
 }
 
 /**
@@ -889,8 +895,8 @@ gi_object_info_get_constant (GIObjectInfo *info,
     + blob->n_vfuncs * header->vfunc_blob_size
     + n * header->constant_blob_size;
 
-  return (GIConstantInfo *) gi_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info,
-                                         rinfo->typelib, offset);
+  return (GIConstantInfo *) gi_base_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info,
+                                              rinfo->typelib, offset);
 }
 
 /**
index 8065612..cb65270 100644 (file)
@@ -156,7 +156,7 @@ GIFunctionInfo *  gi_object_info_find_method      (GIObjectInfo *info,
 GI_AVAILABLE_IN_ALL
 GIFunctionInfo *  gi_object_info_find_method_using_interfaces (GIObjectInfo  *info,
                                                                const char    *name,
-                                                               GIObjectInfo **implementor);
+                                                               GIBaseInfo   **declarer);
 
 
 GI_AVAILABLE_IN_ALL
@@ -186,7 +186,7 @@ GIVFuncInfo *     gi_object_info_find_vfunc       (GIObjectInfo *info,
 GI_AVAILABLE_IN_ALL
 GIVFuncInfo *     gi_object_info_find_vfunc_using_interfaces (GIObjectInfo  *info,
                                                               const char    *name,
-                                                              GIObjectInfo **implementor);
+                                                              GIBaseInfo   **declarer);
 
 GI_AVAILABLE_IN_ALL
 unsigned int      gi_object_info_get_n_constants  (GIObjectInfo *info);
index cb20a31..4312cba 100644 (file)
  * Most users want to call [method@GIRepository.RegisteredTypeInfo.get_g_type]
  * and don’t worry about the rest of the details.
  *
+ * If the registered type is a subtype of `G_TYPE_BOXED`,
+ * [method@GIRepository.RegisteredTypeInfo.is_boxed] will return true, and
+ * [method@GIRepository.RegisteredTypeInfo.get_type_name] is guaranteed to
+ * return a non-`NULL` value. This is relevant for the
+ * [class@GIRepository.StructInfo] and [class@GIRepository.UnionInfo]
+ * subclasses.
+ *
  * Since: 2.80
  */
 
@@ -155,6 +162,63 @@ gi_registered_type_info_get_g_type (GIRegisteredTypeInfo *info)
   return (* get_type_func) ();
 }
 
+/**
+ * gi_registered_type_info_is_boxed:
+ * @info: a #GIRegisteredTypeInfo
+ *
+ * Get whether the registered type is a boxed type.
+ *
+ * A boxed type is a subtype of the fundamental `G_TYPE_BOXED` type.
+ * It’s a type which has registered a [type@GObject.Type], and which has
+ * associated copy and free functions.
+ *
+ * Most boxed types are `struct`s; some are `union`s; and it’s possible for a
+ * boxed type to be neither, but that is currently unsupported by
+ * libgirepository. It’s also possible for a `struct` or `union` to have
+ * associated copy and/or free functions *without* being a boxed type, by virtue
+ * of not having registered a [type@GObject.Type].
+ *
+ * This function will return false for [type@GObject.Type]s which are not boxed,
+ * such as classes or interfaces. It will also return false for the `struct`s
+ * associated with a class or interface, which return true from
+ * [method@GIRepository.StructInfo.is_gtype_struct].
+ *
+ * Returns: true if @info is a boxed type
+ * Since: 2.80
+ */
+gboolean
+gi_registered_type_info_is_boxed (GIRegisteredTypeInfo *info)
+{
+  GIBaseInfo *base_info = GI_BASE_INFO (info);
+  const RegisteredTypeBlob *blob;
+
+  g_return_val_if_fail (GI_IS_REGISTERED_TYPE_INFO (info), G_TYPE_INVALID);
+
+  blob = (const RegisteredTypeBlob *) &base_info->typelib->data[base_info->offset];
+
+  if (blob->blob_type == BLOB_TYPE_BOXED)
+    {
+      return TRUE;
+    }
+  else if (blob->blob_type == BLOB_TYPE_STRUCT)
+    {
+      const StructBlob *struct_blob = (const StructBlob *) &base_info->typelib->data[base_info->offset];
+
+      return !struct_blob->unregistered;
+    }
+  else if (blob->blob_type == BLOB_TYPE_UNION)
+    {
+      const UnionBlob *union_blob = (const UnionBlob *) &base_info->typelib->data[base_info->offset];
+
+      return !union_blob->unregistered;
+    }
+
+  /* We don’t currently support boxed ‘other’ types (boxed types which aren’t
+   * a struct or union. */
+
+  return FALSE;
+}
+
 void
 gi_registered_type_info_class_init (gpointer g_class,
                                     gpointer class_data)
index 4ee6455..a6fe2dd 100644 (file)
@@ -58,7 +58,7 @@ G_BEGIN_DECLS
  *
  * Since: 2.80
  */
-#define GI_IS_REGISTERED_TYPE_INFO(info) (G_TYPE_CHECK_INSTANCE_TYPE ((info), GI_TYPE_OBJECT_INFO))
+#define GI_IS_REGISTERED_TYPE_INFO(info) (G_TYPE_CHECK_INSTANCE_TYPE ((info), GI_TYPE_REGISTERED_TYPE_INFO))
 
 GI_AVAILABLE_IN_ALL
 const char *           gi_registered_type_info_get_type_name (GIRegisteredTypeInfo *info);
@@ -69,4 +69,7 @@ const char *           gi_registered_type_info_get_type_init_function_name (GIRe
 GI_AVAILABLE_IN_ALL
 GType                  gi_registered_type_info_get_g_type    (GIRegisteredTypeInfo *info);
 
+GI_AVAILABLE_IN_ALL
+gboolean               gi_registered_type_info_is_boxed (GIRegisteredTypeInfo *info);
+
 G_END_DECLS
index c1e16eb..0433bac 100644 (file)
@@ -23,7 +23,6 @@
 
 #pragma once
 
-#include <ffi.h>
 #include <glib.h>
 
 #define __GIREPOSITORY_H_INSIDE__
@@ -48,15 +47,103 @@ struct _GIBaseInfo
   GTypeInstance parent_instance;
   gatomicrefcount ref_count;
 
+  /* @repository is never reffed, as that would lead to a refcount cycle with the repository */
   GIRepository *repository;
+  /* @container is reffed if the GIBaseInfo is heap-allocated, but not reffed if it’s stack-allocated */
   GIBaseInfo *container;
 
   GITypelib *typelib;
   uint32_t offset;
 
   uint32_t type_is_embedded : 1; /* Used by GITypeInfo */
+
+  /* A copy of GIBaseInfo is exposed publicly for stack-allocated derivatives
+   * such as GITypeInfo, so its size is now ABI. */
+  void *padding[6];
 };
 
+G_STATIC_ASSERT (sizeof (GIBaseInfo) == sizeof (GIBaseInfoStack));
+G_STATIC_ASSERT (G_ALIGNOF (GIBaseInfo) == G_ALIGNOF (GIBaseInfoStack));
+
+/**
+ * GIInfoType:
+ * @GI_INFO_TYPE_INVALID: invalid type
+ * @GI_INFO_TYPE_FUNCTION: function, see [class@GIRepository.FunctionInfo]
+ * @GI_INFO_TYPE_CALLBACK: callback, see [class@GIRepository.FunctionInfo]
+ * @GI_INFO_TYPE_STRUCT: struct, see [class@GIRepository.StructInfo]
+ * @GI_INFO_TYPE_ENUM: enum, see [class@GIRepository.EnumInfo]
+ * @GI_INFO_TYPE_FLAGS: flags, see [class@GIRepository.EnumInfo]
+ * @GI_INFO_TYPE_OBJECT: object, see [class@GIRepository.ObjectInfo]
+ * @GI_INFO_TYPE_INTERFACE: interface, see [class@GIRepository.InterfaceInfo]
+ * @GI_INFO_TYPE_CONSTANT: constant, see [class@GIRepository.ConstantInfo]
+ * @GI_INFO_TYPE_UNION: union, see [class@GIRepository.UnionInfo]
+ * @GI_INFO_TYPE_VALUE: enum value, see [class@GIRepository.ValueInfo]
+ * @GI_INFO_TYPE_SIGNAL: signal, see [class@GIRepository.SignalInfo]
+ * @GI_INFO_TYPE_VFUNC: virtual function, see [class@GIRepository.VFuncInfo]
+ * @GI_INFO_TYPE_PROPERTY: [class@GObject.Object] property, see
+ *   [class@GIRepository.PropertyInfo]
+ * @GI_INFO_TYPE_FIELD: struct or union field, see
+ *   [class@GIRepository.FieldInfo]
+ * @GI_INFO_TYPE_ARG: argument of a function or callback, see
+ *   [class@GIRepository.ArgInfo]
+ * @GI_INFO_TYPE_TYPE: type information, see [class@GIRepository.TypeInfo]
+ * @GI_INFO_TYPE_UNRESOLVED: unresolved type, a type which is not present in
+ *   the typelib, or any of its dependencies, see
+ *   [class@GIRepository.UnresolvedInfo]
+ * @GI_INFO_TYPE_CALLABLE: an abstract type representing any callable (function,
+ *   callback, vfunc), see [class@GIRepository.CallableInfo]
+ * @GI_INFO_TYPE_REGISTERED_TYPE: an abstract type representing any registered
+ *   type (enum, interface, object, struct, union), see
+ *   [class@GIRepository.RegisteredTypeInfo]
+ *
+ * The type of a [class@GIRepository.BaseInfo] struct.
+ *
+ * See [const@GIRepository.INFO_TYPE_N_TYPES] for the total number of elements
+ * in this enum.
+ *
+ * Since: 2.80
+ */
+typedef enum
+{
+  /* The values here must be kept in sync with GITypelibBlobType */
+  GI_INFO_TYPE_INVALID,
+  GI_INFO_TYPE_FUNCTION,
+  GI_INFO_TYPE_CALLBACK,
+  GI_INFO_TYPE_STRUCT,
+  /* 4 is skipped, it used to be BOXED, but was removed in girepository 2.80.
+   * It is still part of the binary format in GITypelibBlobType. */
+  GI_INFO_TYPE_ENUM = 5,             /*  5 */
+  GI_INFO_TYPE_FLAGS = 6,
+  GI_INFO_TYPE_OBJECT = 7,
+  GI_INFO_TYPE_INTERFACE = 8,
+  GI_INFO_TYPE_CONSTANT = 9,
+  /* 10 is skipped, it used to be used, but was removed before girepository-2.0
+   * It is, however, part of the binary format in GITypelibBlobType */
+  GI_INFO_TYPE_UNION = 11,
+  GI_INFO_TYPE_VALUE = 12,
+  GI_INFO_TYPE_SIGNAL = 13,
+  GI_INFO_TYPE_VFUNC = 14,
+  GI_INFO_TYPE_PROPERTY = 15,
+  GI_INFO_TYPE_FIELD = 16,
+  GI_INFO_TYPE_ARG = 17,
+  GI_INFO_TYPE_TYPE = 18,
+  GI_INFO_TYPE_UNRESOLVED = 19,
+  GI_INFO_TYPE_CALLABLE = 20,
+  GI_INFO_TYPE_REGISTERED_TYPE = 21,
+  /* keep GI_INFO_TYPE_N_TYPES in sync with this */
+} GIInfoType;
+
+/**
+ * GI_INFO_TYPE_N_TYPES:
+ *
+ * Number of entries in [enum@GIRepository.InfoType].
+ *
+ * Since: 2.80
+ */
+#define GI_INFO_TYPE_N_TYPES (GI_INFO_TYPE_REGISTERED_TYPE + 1)
+
+const char *           gi_info_type_to_string           (GIInfoType  type);
+
 /* Subtypes */
 struct _GICallableInfo
 {
@@ -138,14 +225,6 @@ struct _GIInterfaceInfo
 void gi_interface_info_class_init (gpointer g_class,
                                    gpointer class_data);
 
-struct _GIBoxedInfo
-{
-  GIRegisteredTypeInfo parent;
-};
-
-void gi_boxed_info_class_init (gpointer g_class,
-                               gpointer class_data);
-
 struct _GIConstantInfo
 {
   GIBaseInfo parent;
@@ -194,18 +273,16 @@ struct _GIFieldInfo
 void gi_field_info_class_init (gpointer g_class,
                                gpointer class_data);
 
-struct _GIArgInfo
-{
-  GIBaseInfo parent;
-};
+/* GIArgInfo is stack-allocatable so it can be used with
+ * gi_callable_info_load_return_type() and gi_callable_info_load_arg(), so its
+ * definition is actually public in gitypes.h. */
 
 void gi_arg_info_class_init (gpointer g_class,
                              gpointer class_data);
 
-struct _GITypeInfo
-{
-  GIBaseInfo parent;
-};
+/* GITypeInfo is stack-allocatable so it can be used with
+ * gi_arg_info_load_type_info(), so its definition is actually public in
+ * gitypes.h. */
 
 void gi_type_info_class_init (gpointer g_class,
                               gpointer class_data);
@@ -222,7 +299,7 @@ void gi_unresolved_info_class_init (gpointer g_class,
                                     gpointer class_data);
 
 void         gi_info_init       (GIRealInfo   *info,
-                                 GIInfoType    type,
+                                 GType         type,
                                  GIRepository *repository,
                                  GIBaseInfo   *container,
                                  GITypelib    *typelib,
@@ -242,7 +319,7 @@ GITypeInfo * gi_type_info_new   (GIBaseInfo *container,
                                  GITypelib  *typelib,
                                  uint32_t    offset);
 
-void         gi_type_info_init  (GIBaseInfo *info,
+void         gi_type_info_init  (GITypeInfo *info,
                                  GIBaseInfo *container,
                                  GITypelib  *typelib,
                                  uint32_t    offset);
index 5beb830..4da671b 100644 (file)
@@ -32,6 +32,7 @@
 #include <glib.h>
 #include <glib/gprintf.h>
 #include <gmodule.h>
+#include "gibaseinfo-private.h"
 #include "girepository.h"
 #include "gitypelib-internal.h"
 #include "girepository-private.h"
  * `GIRepository` is used to manage repositories of namespaces. Namespaces
  * are represented on disk by type libraries (`.typelib` files).
  *
+ * The individual pieces of API within a type library are represented by
+ * subclasses of [class@GIRepository.BaseInfo]. These can be found using
+ * methods like [method@GIRepository.Repository.find_by_name] or
+ * [method@GIRepository.Repository.get_info].
+ *
+ * You are responsible for ensuring that the lifetime of the
+ * [class@GIRepository.Repository] exceeds that of the lifetime of any of its
+ * [class@GIRepository.BaseInfo]s. This cannot be guaranteed by using internal
+ * references within libgirepository as that would affect performance.
+ *
  * ### Discovery of type libraries
  *
  * `GIRepository` will typically look for a `girepository-1.0` directory
  * standard Linux system this will end up being `/usr/lib/girepository-1.0`.
  *
  * It is possible to control the search paths programmatically, using
- * [func@GIRepository.Repository.prepend_search_path]. It is also possible to
+ * [method@GIRepository.Repository.prepend_search_path]. It is also possible to
  * modify the search paths by using the `GI_TYPELIB_PATH` environment variable.
  * The environment variable takes precedence over the default search path
- * and the [func@GIRepository.Repository.prepend_search_path] calls.
+ * and the [method@GIRepository.Repository.prepend_search_path] calls.
  *
  * Since: 2.80
  */
@@ -65,9 +76,6 @@
 #define GIREPOSITORY_TYPELIB_FILENAME \
   GIREPOSITORY_TYPELIB_NAME "-" GIREPOSITORY_TYPELIB_VERSION ".typelib"
 
-static GIRepository *default_repository = NULL;
-static GPtrArray *typelib_search_path = NULL;
-
 typedef struct {
   size_t n_interfaces;
   GIBaseInfo *interfaces[];
@@ -83,8 +91,13 @@ gtype_interface_cache_free (gpointer data)
   g_free (cache);
 }
 
-struct _GIRepositoryPrivate
+struct _GIRepository
 {
+  GObject parent;
+
+  GPtrArray *typelib_search_path;  /* (element-type filename) (owned) */
+  GPtrArray *library_paths;  /* (element-type filename) (owned) */
+
   GHashTable *typelibs; /* (string) namespace -> GITypelib */
   GHashTable *lazy_typelibs; /* (string) namespace-version -> GITypelib */
   GHashTable *info_by_gtype; /* GType -> GIBaseInfo */
@@ -96,7 +109,7 @@ struct _GIRepositoryPrivate
   size_t cached_n_shared_libraries;  /* length of @cached_shared_libraries, not including NULL terminator */
 };
 
-G_DEFINE_TYPE_WITH_CODE (GIRepository, gi_repository, G_TYPE_OBJECT, G_ADD_PRIVATE (GIRepository));
+G_DEFINE_TYPE (GIRepository, gi_repository, G_TYPE_OBJECT);
 
 #ifdef G_PLATFORM_WIN32
 
@@ -136,28 +149,61 @@ DllMain (HINSTANCE hinstDLL,
 static void
 gi_repository_init (GIRepository *repository)
 {
-  repository->priv = gi_repository_get_instance_private (repository);
-  repository->priv->typelibs
+  /* typelib search path */
+    {
+      const char *libdir;
+      char *typelib_dir;
+      const char *type_lib_path_env;
+
+      /* This variable is intended to take precedence over both:
+       *   - the default search path;
+       *   - all gi_repository_prepend_search_path() calls.
+       */
+      type_lib_path_env = g_getenv ("GI_TYPELIB_PATH");
+
+      if (type_lib_path_env)
+        {
+          char **custom_dirs;
+
+          custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0);
+          repository->typelib_search_path =
+            g_ptr_array_new_take_null_terminated ((gpointer) g_steal_pointer (&custom_dirs), g_free);
+        }
+      else
+        {
+          repository->typelib_search_path = g_ptr_array_new_null_terminated (1, g_free, TRUE);
+        }
+
+      libdir = GOBJECT_INTROSPECTION_LIBDIR;
+
+      typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL);
+
+      g_ptr_array_add (repository->typelib_search_path, g_steal_pointer (&typelib_dir));
+    }
+
+  repository->library_paths = g_ptr_array_new_null_terminated (1, g_free, TRUE);
+
+  repository->typelibs
     = g_hash_table_new_full (g_str_hash, g_str_equal,
                              (GDestroyNotify) g_free,
-                             (GDestroyNotify) gi_typelib_free);
-  repository->priv->lazy_typelibs
+                             (GDestroyNotify) gi_typelib_unref);
+  repository->lazy_typelibs
     = g_hash_table_new_full (g_str_hash, g_str_equal,
                              (GDestroyNotify) g_free,
-                             (GDestroyNotify) NULL);
-  repository->priv->info_by_gtype
+                             (GDestroyNotify) gi_typelib_unref);
+  repository->info_by_gtype
     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                              (GDestroyNotify) NULL,
                              (GDestroyNotify) gi_base_info_unref);
-  repository->priv->info_by_error_domain
+  repository->info_by_error_domain
     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                              (GDestroyNotify) NULL,
                              (GDestroyNotify) gi_base_info_unref);
-  repository->priv->interfaces_for_gtype
+  repository->interfaces_for_gtype
     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                              (GDestroyNotify) NULL,
                              (GDestroyNotify) gtype_interface_cache_free);
-  repository->priv->unknown_gtypes = g_hash_table_new (NULL, NULL);
+  repository->unknown_gtypes = g_hash_table_new (NULL, NULL);
 }
 
 static void
@@ -165,14 +211,17 @@ gi_repository_finalize (GObject *object)
 {
   GIRepository *repository = GI_REPOSITORY (object);
 
-  g_hash_table_destroy (repository->priv->typelibs);
-  g_hash_table_destroy (repository->priv->lazy_typelibs);
-  g_hash_table_destroy (repository->priv->info_by_gtype);
-  g_hash_table_destroy (repository->priv->info_by_error_domain);
-  g_hash_table_destroy (repository->priv->interfaces_for_gtype);
-  g_hash_table_destroy (repository->priv->unknown_gtypes);
+  g_hash_table_destroy (repository->typelibs);
+  g_hash_table_destroy (repository->lazy_typelibs);
+  g_hash_table_destroy (repository->info_by_gtype);
+  g_hash_table_destroy (repository->info_by_error_domain);
+  g_hash_table_destroy (repository->interfaces_for_gtype);
+  g_hash_table_destroy (repository->unknown_gtypes);
+
+  g_clear_pointer (&repository->cached_shared_libraries, g_strfreev);
 
-  g_clear_pointer (&repository->priv->cached_shared_libraries, g_strfreev);
+  g_clear_pointer (&repository->library_paths, g_ptr_array_unref);
+  g_clear_pointer (&repository->typelib_search_path, g_ptr_array_unref);
 
   (* G_OBJECT_CLASS (gi_repository_parent_class)->finalize) (G_OBJECT (repository));
 }
@@ -187,88 +236,124 @@ gi_repository_class_init (GIRepositoryClass *class)
   gobject_class->finalize = gi_repository_finalize;
 }
 
-static void
-init_globals (void)
+/**
+ * gi_repository_prepend_search_path:
+ * @repository: A #GIRepository
+ * @directory: (type filename): directory name to prepend to the typelib
+ *   search path
+ *
+ * Prepends @directory to the typelib search path.
+ *
+ * See also: gi_repository_get_search_path().
+ *
+ * Since: 2.80
+ */
+void
+gi_repository_prepend_search_path (GIRepository *repository,
+                                   const char   *directory)
 {
-  static gsize initialized = 0;
+  g_return_if_fail (GI_IS_REPOSITORY (repository));
 
-  if (!g_once_init_enter (&initialized))
-    return;
+  g_ptr_array_insert (repository->typelib_search_path, 0, g_strdup (directory));
+}
 
-  if (default_repository == NULL)
-    default_repository = gi_repository_new ();
+/**
+ * gi_repository_get_search_path:
+ * @repository: A #GIRepository
+ * @n_paths_out: (optional) (out): The number of search paths returned.
+ *
+ * Returns the current search path [class@GIRepository.Repository] will use when
+ * loading typelib files.
+ *
+ * The list is internal to [class@GIRepository.Repository] and should not be
+ * freed, nor should its string elements.
+ *
+ * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
+ * counted in @n_paths_out.
+ *
+ * Returns: (element-type filename) (transfer none) (array length=n_paths_out): list of search paths, most
+ *   important first
+ * Since: 2.80
+ */
+const char * const *
+gi_repository_get_search_path (GIRepository *repository,
+                               size_t       *n_paths_out)
+{
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 
-  if (typelib_search_path == NULL)
+  if G_UNLIKELY (!repository->typelib_search_path ||
+                 !repository->typelib_search_path->pdata)
     {
-      const char *libdir;
-      char *typelib_dir;
-      const char *type_lib_path_env;
-
-      /* This variable is intended to take precedence over both:
-       *   - the default search path;
-       *   - all gi_repository_prepend_search_path() calls.
-       */
-      type_lib_path_env = g_getenv ("GI_TYPELIB_PATH");
-
-      if (type_lib_path_env)
-        {
-          char **custom_dirs;
-
-          custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0);
-          typelib_search_path =
-            g_ptr_array_new_take_null_terminated ((gpointer) g_steal_pointer (&custom_dirs), g_free);
-        }
-      else
-        {
-          typelib_search_path = g_ptr_array_new_null_terminated (1, g_free, TRUE);
-        }
-
-      libdir = GOBJECT_INTROSPECTION_LIBDIR;
+      static const char * const empty_search_path[] = {NULL};
 
-      typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL);
+      if (n_paths_out)
+        *n_paths_out = 0;
 
-      g_ptr_array_add (typelib_search_path, g_steal_pointer (&typelib_dir));
+      return empty_search_path;
     }
 
-  g_once_init_leave (&initialized, 1);
+  if (n_paths_out)
+    *n_paths_out = repository->typelib_search_path->len;
+
+  return (const char * const *) repository->typelib_search_path->pdata;
 }
 
 /**
- * gi_repository_prepend_search_path:
- * @directory: (type filename): directory name to prepend to the typelib
- *   search path
+ * gi_repository_prepend_library_path:
+ * @repository: A #GIRepository
+ * @directory: (type filename): a single directory to scan for shared libraries
  *
- * Prepends @directory to the typelib search path.
+ * Prepends @directory to the search path that is used to
+ * search shared libraries referenced by imported namespaces.
  *
- * See also: gi_repository_get_search_path().
+ * Multiple calls to this function all contribute to the final
+ * list of paths.
+ *
+ * The list of paths is unique to @repository. When a typelib is loaded by the
+ * repository, the list of paths from the @repository at that instant is used
+ * by the typelib for loading its modules.
+ *
+ * If the library is not found in the directories configured
+ * in this way, loading will fall back to the system library
+ * path (i.e. `LD_LIBRARY_PATH` and `DT_RPATH` in ELF systems).
+ * See the documentation of your dynamic linker for full details.
  *
  * Since: 2.80
  */
 void
-gi_repository_prepend_search_path (const char *directory)
+gi_repository_prepend_library_path (GIRepository *repository,
+                                    const char   *directory)
 {
-  init_globals ();
-  g_ptr_array_insert (typelib_search_path, 0, g_strdup (directory));
+  g_return_if_fail (GI_IS_REPOSITORY (repository));
+
+  g_ptr_array_insert (repository->library_paths, 0, g_strdup (directory));
 }
 
 /**
- * gi_repository_get_search_path:
- * @n_paths_out: (optional) (out): The number of search paths returned.
+ * gi_repository_get_library_path:
+ * @repository: A #GIRepository
+ * @n_paths_out: (optional) (out): The number of library paths returned.
  *
  * Returns the current search path [class@GIRepository.Repository] will use when
- * loading typelib files.
+ * loading shared libraries referenced by imported namespaces.
  *
  * The list is internal to [class@GIRepository.Repository] and should not be
  * freed, nor should its string elements.
  *
+ * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
+ * counted in @n_paths_out.
+ *
  * Returns: (element-type filename) (transfer none) (array length=n_paths_out): list of search paths, most
  *   important first
  * Since: 2.80
  */
 const char * const *
-gi_repository_get_search_path (size_t *n_paths_out)
+gi_repository_get_library_path (GIRepository *repository,
+                                size_t       *n_paths_out)
 {
-  if G_UNLIKELY (!typelib_search_path || !typelib_search_path->pdata)
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
+
+  if G_UNLIKELY (!repository->library_paths || !repository->library_paths->pdata)
     {
       static const char * const empty_search_path[] = {NULL};
 
@@ -279,9 +364,9 @@ gi_repository_get_search_path (size_t *n_paths_out)
     }
 
   if (n_paths_out)
-    *n_paths_out = typelib_search_path->len;
+    *n_paths_out = repository->library_paths->len;
 
-  return (const char * const *) typelib_search_path->pdata;
+  return (const char * const *) repository->library_paths->pdata;
 }
 
 static char *
@@ -310,17 +395,6 @@ get_typelib_dependencies (GITypelib *typelib)
   return g_strsplit (dependencies_glob, "|", 0);
 }
 
-static GIRepository *
-get_repository (GIRepository *repository)
-{
-  init_globals ();
-
-  if (repository != NULL)
-    return repository;
-  else
-    return default_repository;
-}
-
 static GITypelib *
 check_version_conflict (GITypelib *typelib,
                         const char  *namespace,
@@ -361,13 +435,13 @@ get_registered_status (GIRepository *repository,
                        char        **version_conflict)
 {
   GITypelib *typelib;
-  repository = get_repository (repository);
+
   if (lazy_status)
     *lazy_status = FALSE;
-  typelib = g_hash_table_lookup (repository->priv->typelibs, namespace);
+  typelib = g_hash_table_lookup (repository->typelibs, namespace);
   if (typelib)
     return check_version_conflict (typelib, namespace, version, version_conflict);
-  typelib = g_hash_table_lookup (repository->priv->lazy_typelibs, namespace);
+  typelib = g_hash_table_lookup (repository->lazy_typelibs, namespace);
   if (!typelib)
     return NULL;
   if (lazy_status)
@@ -443,10 +517,10 @@ register_internal (GIRepository *repository,
 
   if (lazy)
     {
-      g_assert (!g_hash_table_lookup (repository->priv->lazy_typelibs,
+      g_assert (!g_hash_table_lookup (repository->lazy_typelibs,
                                       namespace));
-      g_hash_table_insert (repository->priv->lazy_typelibs,
-                           build_typelib_key (namespace, source), (void *)typelib);
+      g_hash_table_insert (repository->lazy_typelibs,
+                           build_typelib_key (namespace, source), gi_typelib_ref (typelib));
     }
   else
     {
@@ -458,29 +532,30 @@ register_internal (GIRepository *repository,
         return NULL;
 
       /* Check if we are transitioning from lazily loaded state */
-      if (g_hash_table_lookup_extended (repository->priv->lazy_typelibs,
+      if (g_hash_table_lookup_extended (repository->lazy_typelibs,
                                         namespace,
                                         (gpointer)&key, &value))
-        g_hash_table_remove (repository->priv->lazy_typelibs, key);
+        g_hash_table_remove (repository->lazy_typelibs, key);
       else
         key = build_typelib_key (namespace, source);
 
-      g_hash_table_insert (repository->priv->typelibs,
+      g_hash_table_insert (repository->typelibs,
                            g_steal_pointer (&key),
-                           (void *)typelib);
+                           gi_typelib_ref (typelib));
     }
 
   /* These types might be resolved now, clear the cache */
-  g_hash_table_remove_all (repository->priv->unknown_gtypes);
+  g_hash_table_remove_all (repository->unknown_gtypes);
 
   return namespace;
 }
 
 /**
  * gi_repository_get_immediate_dependencies:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace of interest
+ * @n_dependencies_out: (optional) (out): Return location for the number of
+ *   dependencies
  *
  * Return an array of the immediate versioned dependencies for @namespace_.
  * Returned strings are of the form `namespace-version`.
@@ -492,21 +567,24 @@ register_internal (GIRepository *repository,
  * To get the transitive closure of dependencies for @namespace_, use
  * [method@GIRepository.Repository.get_dependencies].
  *
- * Returns: (transfer full) (array zero-terminated=1): `NULL`-terminated string
- *   array of immediate versioned dependencies
+ * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
+ * counted in @n_dependencies_out.
+ *
+ * Returns: (transfer full) (array length=n_dependencies_out): String array of
+ *   immediate versioned dependencies
  * Since: 2.80
  */
 char **
 gi_repository_get_immediate_dependencies (GIRepository *repository,
-                                          const char   *namespace)
+                                          const char   *namespace,
+                                          size_t       *n_dependencies_out)
 {
   GITypelib *typelib;
   char **deps;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (namespace != NULL, NULL);
 
-  repository = get_repository (repository);
-
   typelib = get_registered (repository, namespace, NULL);
   g_return_val_if_fail (typelib != NULL, NULL);
 
@@ -515,6 +593,9 @@ gi_repository_get_immediate_dependencies (GIRepository *repository,
   if (deps == NULL)
       deps = g_strsplit ("", "|", 0);
 
+  if (n_dependencies_out != NULL)
+    *n_dependencies_out = g_strv_length (deps);
+
   return deps;
 }
 
@@ -560,9 +641,10 @@ get_typelib_dependencies_transitive (GIRepository *repository,
 
 /**
  * gi_repository_get_dependencies:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace of interest
+ * @n_dependencies_out: (optional) (out): Return location for the number of
+ *   dependencies
  *
  * Retrieves all (transitive) versioned dependencies for
  * @namespace_.
@@ -576,13 +658,17 @@ get_typelib_dependencies_transitive (GIRepository *repository,
  * To get only the immediate dependencies for @namespace_, use
  * [method@GIRepository.Repository.get_immediate_dependencies].
  *
- * Returns: (transfer full) (array zero-terminated=1): `NULL`-terminated string
- *   array of all versioned dependencies
+ * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
+ * counted in @n_dependencies_out.
+ *
+ * Returns: (transfer full) (array length=n_dependencies_out): String array of
+ *   all versioned dependencies
  * Since: 2.80
  */
 char **
 gi_repository_get_dependencies (GIRepository *repository,
-                                const char *namespace)
+                                const char   *namespace,
+                                size_t       *n_dependencies_out)
 {
   GITypelib *typelib;
   GHashTable *transitive_dependencies;  /* set of owned utf8 */
@@ -590,10 +676,9 @@ gi_repository_get_dependencies (GIRepository *repository,
   char *dependency;
   GPtrArray *out;  /* owned utf8 elements */
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (namespace != NULL, NULL);
 
-  repository = get_repository (repository);
-
   typelib = get_registered (repository, namespace, NULL);
   g_return_val_if_fail (typelib != NULL, NULL);
 
@@ -616,14 +701,16 @@ gi_repository_get_dependencies (GIRepository *repository,
 
   g_hash_table_unref (transitive_dependencies);
 
+  if (n_dependencies_out != NULL)
+    *n_dependencies_out = out->len;
+
   return (char **) g_ptr_array_free (out, FALSE);
 }
 
 /**
  * gi_repository_load_typelib:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
- * @typelib: the typelib to load
+ * @repository: A #GIRepository
+ * @typelib: (transfer none): the typelib to load
  * @flags: flags affecting the loading operation
  * @error: return location for a [type@GLib.Error], or `NULL`
  *
@@ -645,7 +732,7 @@ gi_repository_load_typelib (GIRepository           *repository,
   gboolean is_lazy;
   char *version_conflict;
 
-  repository = get_repository (repository);
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 
   header = (Header *) typelib->data;
   namespace = gi_typelib_get_string (typelib, header->namespace);
@@ -670,8 +757,7 @@ gi_repository_load_typelib (GIRepository           *repository,
 
 /**
  * gi_repository_is_registered:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace of interest
  * @version: (nullable): Required version, may be `NULL` for latest
  *
@@ -692,41 +778,15 @@ gi_repository_is_registered (GIRepository *repository,
                              const char   *namespace,
                              const char   *version)
 {
-  repository = get_repository (repository);
-  return get_registered (repository, namespace, version) != NULL;
-}
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), FALSE);
 
-/**
- * gi_repository_get_default:
- *
- * Returns the singleton process-global default #GIRepository.
- *
- * It is not currently supported to have multiple repositories in a
- * particular process, but this function is provided in the unlikely
- * eventuality that it would become possible, and as a convenience for
- * higher level language bindings to conform to the GObject method
- * call conventions.
- *
- * All methods on #GIRepository also accept `NULL` as an instance
- * parameter to mean this default repository, which is usually more
- * convenient for C.
- *
- * Returns: (transfer none): The global singleton [class@GIRepository.Repository]
- * Since: 2.80
- */
-GIRepository *
-gi_repository_get_default (void)
-{
-  return get_repository (NULL);
+  return get_registered (repository, namespace, version) != NULL;
 }
 
 /**
  * gi_repository_new:
  *
- * Create a new (non-singleton) [class@GIRepository.Repository].
- *
- * Most callers should use [func@GIRepository.Repository.get_default] instead,
- * as a singleton repository is more useful in most situations.
+ * Create a new [class@GIRepository.Repository].
  *
  * Returns: (transfer full): a new [class@GIRepository.Repository]
  * Since: 2.80
@@ -739,8 +799,7 @@ gi_repository_new (void)
 
 /**
  * gi_repository_get_n_infos:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace to inspect
  *
  * This function returns the number of metadata entries in
@@ -758,10 +817,9 @@ gi_repository_get_n_infos (GIRepository *repository,
   GITypelib *typelib;
   unsigned int n_interfaces = 0;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), -1);
   g_return_val_if_fail (namespace != NULL, -1);
 
-  repository = get_repository (repository);
-
   typelib = get_registered (repository, namespace, NULL);
 
   g_return_val_if_fail (typelib != NULL, -1);
@@ -773,8 +831,7 @@ gi_repository_get_n_infos (GIRepository *repository,
 
 /**
  * gi_repository_get_info:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace to inspect
  * @idx: 0-based offset into namespace metadata for entry
  *
@@ -797,11 +854,10 @@ gi_repository_get_info (GIRepository *repository,
   GITypelib *typelib;
   DirEntry *entry;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (namespace != NULL, NULL);
   g_return_val_if_fail (idx < G_MAXUINT16, NULL);
 
-  repository = get_repository (repository);
-
   typelib = get_registered (repository, namespace, NULL);
 
   g_return_val_if_fail (typelib != NULL, NULL);
@@ -809,7 +865,7 @@ gi_repository_get_info (GIRepository *repository,
   entry = gi_typelib_get_dir_entry (typelib, idx + 1);
   g_return_val_if_fail (entry != NULL, NULL);
 
-  return gi_info_new_full (entry->blob_type,
+  return gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
                            repository,
                            NULL, typelib, entry->offset);
 }
@@ -849,8 +905,7 @@ find_by_gtype (GHashTable *table, FindByGTypeData *data, gboolean check_prefix)
 
 /**
  * gi_repository_find_by_gtype:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @gtype: [type@GObject.Type] to search for
  *
  * Searches all loaded namespaces for a particular [type@GObject.Type].
@@ -873,17 +928,16 @@ gi_repository_find_by_gtype (GIRepository *repository,
   GIBaseInfo *cached;
   DirEntry *entry;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL);
 
-  repository = get_repository (repository);
-
-  cached = g_hash_table_lookup (repository->priv->info_by_gtype,
+  cached = g_hash_table_lookup (repository->info_by_gtype,
                                 (gpointer)gtype);
 
   if (cached != NULL)
     return gi_base_info_ref (cached);
 
-  if (g_hash_table_contains (repository->priv->unknown_gtypes, (gpointer)gtype))
+  if (g_hash_table_contains (repository->unknown_gtypes, (gpointer)gtype))
     return NULL;
 
   data.gtype_name = g_type_name (gtype);
@@ -896,9 +950,9 @@ gi_repository_find_by_gtype (GIRepository *repository,
    * target type does not have this typelib's C prefix. Use this
    * assumption as our first attempt at locating the DirEntry.
    */
-  entry = find_by_gtype (repository->priv->typelibs, &data, TRUE);
+  entry = find_by_gtype (repository->typelibs, &data, TRUE);
   if (entry == NULL)
-    entry = find_by_gtype (repository->priv->lazy_typelibs, &data, TRUE);
+    entry = find_by_gtype (repository->lazy_typelibs, &data, TRUE);
 
   /* Not ever class library necessarily specifies a correct c_prefix,
    * so take a second pass. This time we will try a global lookup,
@@ -906,32 +960,31 @@ gi_repository_find_by_gtype (GIRepository *repository,
    * See http://bugzilla.gnome.org/show_bug.cgi?id=564016
    */
   if (entry == NULL)
-    entry = find_by_gtype (repository->priv->typelibs, &data, FALSE);
+    entry = find_by_gtype (repository->typelibs, &data, FALSE);
   if (entry == NULL)
-    entry = find_by_gtype (repository->priv->lazy_typelibs, &data, FALSE);
+    entry = find_by_gtype (repository->lazy_typelibs, &data, FALSE);
 
   if (entry != NULL)
     {
-      cached = gi_info_new_full (entry->blob_type,
+      cached = gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
                                  repository,
                                  NULL, data.result_typelib, entry->offset);
 
-      g_hash_table_insert (repository->priv->info_by_gtype,
+      g_hash_table_insert (repository->info_by_gtype,
                            (gpointer) gtype,
                            gi_base_info_ref (cached));
       return cached;
     }
   else
     {
-      g_hash_table_add (repository->priv->unknown_gtypes, (gpointer) gtype);
+      g_hash_table_add (repository->unknown_gtypes, (gpointer) gtype);
       return NULL;
     }
 }
 
 /**
  * gi_repository_find_by_name:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace which will be searched
  * @name: Entry name to find
  *
@@ -953,16 +1006,16 @@ gi_repository_find_by_name (GIRepository *repository,
   GITypelib *typelib;
   DirEntry *entry;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (namespace != NULL, NULL);
 
-  repository = get_repository (repository);
   typelib = get_registered (repository, namespace, NULL);
   g_return_val_if_fail (typelib != NULL, NULL);
 
   entry = gi_typelib_get_dir_entry_by_name (typelib, name);
   if (entry == NULL)
     return NULL;
-  return gi_info_new_full (entry->blob_type,
+  return gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
                            repository,
                            NULL, typelib, entry->offset);
 }
@@ -993,8 +1046,7 @@ find_by_error_domain_foreach (gpointer key,
 
 /**
  * gi_repository_find_by_error_domain:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @domain: a [type@GLib.Error] domain
  *
  * Searches for the enum type corresponding to the given [type@GLib.Error]
@@ -1015,9 +1067,9 @@ gi_repository_find_by_error_domain (GIRepository *repository,
   FindByErrorDomainData data;
   GIEnumInfo *cached;
 
-  repository = get_repository (repository);
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 
-  cached = g_hash_table_lookup (repository->priv->info_by_error_domain,
+  cached = g_hash_table_lookup (repository->info_by_error_domain,
                                 GUINT_TO_POINTER (domain));
 
   if (cached != NULL)
@@ -1028,17 +1080,17 @@ gi_repository_find_by_error_domain (GIRepository *repository,
   data.result_typelib = NULL;
   data.result = NULL;
 
-  g_hash_table_foreach (repository->priv->typelibs, find_by_error_domain_foreach, &data);
+  g_hash_table_foreach (repository->typelibs, find_by_error_domain_foreach, &data);
   if (data.result == NULL)
-    g_hash_table_foreach (repository->priv->lazy_typelibs, find_by_error_domain_foreach, &data);
+    g_hash_table_foreach (repository->lazy_typelibs, find_by_error_domain_foreach, &data);
 
   if (data.result != NULL)
     {
-      cached = (GIEnumInfo *) gi_info_new_full (data.result->blob_type,
+      cached = (GIEnumInfo *) gi_info_new_full (gi_typelib_blob_type_to_info_type (data.result->blob_type),
                                                 repository,
                                                 NULL, data.result_typelib, data.result->offset);
 
-      g_hash_table_insert (repository->priv->info_by_error_domain,
+      g_hash_table_insert (repository->info_by_error_domain,
                            GUINT_TO_POINTER (domain),
                            gi_base_info_ref ((GIBaseInfo *) cached));
       return cached;
@@ -1048,7 +1100,7 @@ gi_repository_find_by_error_domain (GIRepository *repository,
 
 /**
  * gi_repository_get_object_gtype_interfaces:
- * @repository: (nullable): a #GIRepository, or `NULL` for the default repository
+ * @repository: a #GIRepository
  * @gtype: a [type@GObject.Type] whose fundamental type is `G_TYPE_OBJECT`
  * @n_interfaces_out: (out): Number of interfaces
  * @interfaces_out: (out) (transfer none) (array length=n_interfaces_out): Interfaces for @gtype
@@ -1077,11 +1129,10 @@ gi_repository_get_object_gtype_interfaces (GIRepository      *repository,
 {
   GTypeInterfaceCache *cache;
 
+  g_return_if_fail (GI_IS_REPOSITORY (repository));
   g_return_if_fail (g_type_fundamental (gtype) == G_TYPE_OBJECT);
 
-  repository = get_repository (repository);
-
-  cache = g_hash_table_lookup (repository->priv->interfaces_for_gtype,
+  cache = g_hash_table_lookup (repository->interfaces_for_gtype,
                                (void *) gtype);
   if (cache == NULL)
     {
@@ -1117,7 +1168,7 @@ gi_repository_get_object_gtype_interfaces (GIRepository      *repository,
         cache->interfaces[i] = iter->data;
       g_list_free (interface_infos);
 
-      g_hash_table_insert (repository->priv->interfaces_for_gtype, (gpointer) gtype,
+      g_hash_table_insert (repository->interfaces_for_gtype, (gpointer) gtype,
                            cache);
 
       g_free (interfaces);
@@ -1139,26 +1190,31 @@ collect_namespaces (gpointer key,
 
 /**
  * gi_repository_get_loaded_namespaces:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
+ * @n_namespaces_out: (optional) (out): Return location for the number of
+ *   namespaces
  *
  * Return the list of currently loaded namespaces.
  *
- * Returns: (element-type utf8) (transfer full) (array zero-terminated=1): `NULL`-terminated
+ * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
+ * counted in @n_namespaces_out.
+ *
+ * Returns: (element-type utf8) (transfer full) (array length=n_namespaces_out):
  *   list of namespaces
  * Since: 2.80
  */
 char **
-gi_repository_get_loaded_namespaces (GIRepository *repository)
+gi_repository_get_loaded_namespaces (GIRepository *repository,
+                                     size_t       *n_namespaces_out)
 {
   GList *l, *list = NULL;
   char **names;
   size_t i;
 
-  repository = get_repository (repository);
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 
-  g_hash_table_foreach (repository->priv->typelibs, collect_namespaces, &list);
-  g_hash_table_foreach (repository->priv->lazy_typelibs, collect_namespaces, &list);
+  g_hash_table_foreach (repository->typelibs, collect_namespaces, &list);
+  g_hash_table_foreach (repository->lazy_typelibs, collect_namespaces, &list);
 
   names = g_malloc0 (sizeof (char *) * (g_list_length (list) + 1));
   i = 0;
@@ -1166,13 +1222,15 @@ gi_repository_get_loaded_namespaces (GIRepository *repository)
     names[i++] = g_strdup (l->data);
   g_list_free (list);
 
+  if (n_namespaces_out != NULL)
+    *n_namespaces_out = i;
+
   return names;
 }
 
 /**
  * gi_repository_get_version:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace to inspect
  *
  * This function returns the loaded version associated with the given
@@ -1192,10 +1250,9 @@ gi_repository_get_version (GIRepository *repository,
   GITypelib *typelib;
   Header *header;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (namespace != NULL, NULL);
 
-  repository = get_repository (repository);
-
   typelib = get_registered (repository, namespace, NULL);
 
   g_return_val_if_fail (typelib != NULL, NULL);
@@ -1206,8 +1263,7 @@ gi_repository_get_version (GIRepository *repository,
 
 /**
  * gi_repository_get_shared_libraries:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace to inspect
  * @out_n_elements: (out) (optional): Return location for the number of elements
  *   in the returned array
@@ -1225,6 +1281,9 @@ gi_repository_get_version (GIRepository *repository,
  * The list is internal to [class@GIRepository.Repository] and should not be
  * freed, nor should its string elements.
  *
+ * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
+ * counted in @out_n_elements.
+ *
  * Returns: (nullable) (array length=out_n_elements) (transfer none): Array of
  *   paths to shared libraries, or `NULL` if none are associated
  * Since: 2.80
@@ -1237,10 +1296,9 @@ gi_repository_get_shared_libraries (GIRepository *repository,
   GITypelib *typelib;
   Header *header;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (namespace != NULL, NULL);
 
-  repository = get_repository (repository);
-
   typelib = get_registered (repository, namespace, NULL);
 
   g_return_val_if_fail (typelib != NULL, NULL);
@@ -1254,27 +1312,26 @@ gi_repository_get_shared_libraries (GIRepository *repository,
     }
 
   /* Populate the cache. */
-  if (repository->priv->cached_shared_libraries == NULL)
+  if (repository->cached_shared_libraries == NULL)
     {
       const char *comma_separated = gi_typelib_get_string (typelib, header->shared_library);
 
       if (comma_separated != NULL && *comma_separated != '\0')
         {
-          repository->priv->cached_shared_libraries = g_strsplit (comma_separated, ",", -1);
-          repository->priv->cached_n_shared_libraries = g_strv_length (repository->priv->cached_shared_libraries);
+          repository->cached_shared_libraries = g_strsplit (comma_separated, ",", -1);
+          repository->cached_n_shared_libraries = g_strv_length (repository->cached_shared_libraries);
         }
     }
 
   if (out_n_elements != NULL)
-    *out_n_elements = repository->priv->cached_n_shared_libraries;
+    *out_n_elements = repository->cached_n_shared_libraries;
 
-  return (const char * const *) repository->priv->cached_shared_libraries;
+  return (const char * const *) repository->cached_shared_libraries;
 }
 
 /**
  * gi_repository_get_c_prefix:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: Namespace to inspect
  *
  * This function returns the ‘C prefix’, or the C level namespace
@@ -1297,10 +1354,9 @@ gi_repository_get_c_prefix (GIRepository *repository,
   GITypelib *typelib;
   Header *header;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (namespace_ != NULL, NULL);
 
-  repository = get_repository (repository);
-
   typelib = get_registered (repository, namespace_, NULL);
 
   g_return_val_if_fail (typelib != NULL, NULL);
@@ -1314,8 +1370,7 @@ gi_repository_get_c_prefix (GIRepository *repository,
 
 /**
  * gi_repository_get_typelib_path:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: GI namespace to use, e.g. `Gtk`
  *
  * If namespace @namespace_ is loaded, return the full path to the
@@ -1334,12 +1389,12 @@ gi_repository_get_typelib_path (GIRepository *repository,
 {
   gpointer orig_key, value;
 
-  repository = get_repository (repository);
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 
-  if (!g_hash_table_lookup_extended (repository->priv->typelibs, namespace,
+  if (!g_hash_table_lookup_extended (repository->typelibs, namespace,
                                      &orig_key, &value))
     {
-      if (!g_hash_table_lookup_extended (repository->priv->lazy_typelibs, namespace,
+      if (!g_hash_table_lookup_extended (repository->lazy_typelibs, namespace,
                                          &orig_key, &value))
 
         return NULL;
@@ -1615,14 +1670,16 @@ find_namespace_latest (const char          *namespace,
 
 /**
  * gi_repository_enumerate_versions:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: GI namespace, e.g. `Gtk`
  * @n_versions_out: (optional) (out): The number of versions returned.
  *
  * Obtain an unordered list of versions (either currently loaded or
  * available) for @namespace_ in this @repository.
  *
+ * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
+ * counted in @n_versions_out.
+ *
  * Returns: (element-type utf8) (transfer full) (array length=n_versions_out): the array of versions.
  * Since: 2.80
  */
@@ -1636,10 +1693,11 @@ gi_repository_enumerate_versions (GIRepository *repository,
   const char *loaded_version;
   char **ret;
 
-  init_globals ();
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
+
   candidates = enumerate_namespace_versions (namespace_,
-                                             (const char * const *) typelib_search_path->pdata,
-                                             typelib_search_path->len);
+                                             (const char * const *) repository->typelib_search_path->pdata,
+                                             repository->typelib_search_path->len);
 
   if (!candidates)
     {
@@ -1688,6 +1746,7 @@ require_internal (GIRepository           *repository,
   GITypelib *ret = NULL;
   Header *header;
   GITypelib *typelib = NULL;
+  GITypelib *typelib_owned = NULL;
   const char *typelib_namespace, *typelib_version;
   gboolean allow_lazy = (flags & GI_REPOSITORY_LOAD_FLAG_LAZY) > 0;
   gboolean is_lazy;
@@ -1695,10 +1754,9 @@ require_internal (GIRepository           *repository,
   char *path = NULL;
   char *tmp_version = NULL;
 
+  g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
   g_return_val_if_fail (namespace != NULL, FALSE);
 
-  repository = get_repository (repository);
-
   typelib = get_registered_status (repository, namespace, version, allow_lazy,
                                    &is_lazy, &version_conflict);
   if (typelib)
@@ -1742,7 +1800,13 @@ require_internal (GIRepository           *repository,
 
   {
     GError *temp_error = NULL;
-    typelib = gi_typelib_new_from_mapped_file (mfile, &temp_error);
+    GBytes *bytes = NULL;
+
+    bytes = g_mapped_file_get_bytes (mfile);
+    typelib_owned = typelib = gi_typelib_new_from_bytes (bytes, &temp_error);
+    g_bytes_unref (bytes);
+    g_clear_pointer (&mfile, g_mapped_file_unref);
+
     if (!typelib)
       {
         g_set_error (error, GI_REPOSITORY_ERROR,
@@ -1752,6 +1816,8 @@ require_internal (GIRepository           *repository,
         g_clear_error (&temp_error);
         goto out;
       }
+
+    typelib->library_paths = (repository->library_paths != NULL) ? g_ptr_array_ref (repository->library_paths) : NULL;
   }
   header = (Header *) typelib->data;
   typelib_namespace = gi_typelib_get_string (typelib, header->namespace);
@@ -1764,7 +1830,6 @@ require_internal (GIRepository           *repository,
                    "Typelib file %s for namespace '%s' contains "
                    "namespace '%s' which doesn't match the file name",
                    path, namespace, typelib_namespace);
-      gi_typelib_free (typelib);
       goto out;
     }
   if (version != NULL && strcmp (typelib_version, version) != 0)
@@ -1774,18 +1839,15 @@ require_internal (GIRepository           *repository,
                    "Typelib file %s for namespace '%s' contains "
                    "version '%s' which doesn't match the expected version '%s'",
                    path, namespace, typelib_version, version);
-      gi_typelib_free (typelib);
       goto out;
     }
 
   if (!register_internal (repository, path, allow_lazy,
                           typelib, error))
-    {
-      gi_typelib_free (typelib);
-      goto out;
-    }
+    goto out;
   ret = typelib;
  out:
+  g_clear_pointer (&typelib_owned, gi_typelib_unref);
   g_free (tmp_version);
   g_free (path);
   return ret;
@@ -1793,8 +1855,7 @@ require_internal (GIRepository           *repository,
 
 /**
  * gi_repository_require:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @namespace_: GI namespace to use, e.g. `Gtk`
  * @version: (nullable): Version of namespace, may be `NULL` for latest
  * @flags: Set of [flags@GIRepository.RepositoryLoadFlags], may be 0
@@ -1820,18 +1881,16 @@ gi_repository_require (GIRepository  *repository,
 {
   GITypelib *typelib;
 
-  init_globals ();
   typelib = require_internal (repository, namespace, version, flags,
-                              (const char * const *) typelib_search_path->pdata,
-                              typelib_search_path->len, error);
+                              (const char * const *) repository->typelib_search_path->pdata,
+                              repository->typelib_search_path->len, error);
 
   return typelib;
 }
 
 /**
  * gi_repository_require_private:
- * @repository: (nullable): A #GIRepository, or `NULL` for the singleton
- *   process-global default #GIRepository
+ * @repository: A #GIRepository
  * @typelib_dir: (type filename): Private directory where to find the requested
  *   typelib
  * @namespace_: GI namespace to use, e.g. `Gtk`
@@ -2006,8 +2065,6 @@ gi_info_type_to_string (GIInfoType type)
       return "callback";
     case GI_INFO_TYPE_STRUCT:
       return "struct";
-    case GI_INFO_TYPE_BOXED:
-      return "boxed";
     case GI_INFO_TYPE_ENUM:
       return "enum";
     case GI_INFO_TYPE_FLAGS:
index f430e95..fa0b93e 100644 (file)
@@ -33,7 +33,6 @@
 
 #include <girepository/giarginfo.h>
 #include <girepository/gibaseinfo.h>
-#include <girepository/giboxedinfo.h>
 #include <girepository/gicallableinfo.h>
 #include <girepository/gicallbackinfo.h>
 #include <girepository/giconstantinfo.h>
 
 G_BEGIN_DECLS
 
-#define GI_TYPE_REPOSITORY              (gi_repository_get_type ())
-#define GI_REPOSITORY(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GI_TYPE_REPOSITORY, GIRepository))
-#define GI_REPOSITORY_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GI_TYPE_REPOSITORY, GIRepositoryClass))
-#define GI_IS_REPOSITORY(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GI_TYPE_REPOSITORY))
-#define GI_IS_REPOSITORY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GI_TYPE_REPOSITORY))
-#define GI_REPOSITORY_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GI_TYPE_REPOSITORY, GIRepositoryClass))
-
-typedef struct _GIRepository         GIRepository;
-typedef struct _GIRepositoryClass    GIRepositoryClass;
-typedef struct _GIRepositoryPrivate  GIRepositoryPrivate;
-
-struct _GIRepository
-{
-  /*< private >*/
-  GObject parent;
-  GIRepositoryPrivate *priv;
-};
-
-struct _GIRepositoryClass
-{
-  /*< private >*/
-  GObjectClass parent;
-};
+#define GI_TYPE_REPOSITORY (gi_repository_get_type ())
+GI_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GIRepository, gi_repository, GI, REPOSITORY, GObject)
 
 /**
  * GIRepositoryLoadFlags:
@@ -97,22 +76,23 @@ typedef enum
 /* Repository */
 
 GI_AVAILABLE_IN_ALL
-GType         gi_repository_get_type      (void) G_GNUC_CONST;
+GIRepository *gi_repository_new (void);
 
 GI_AVAILABLE_IN_ALL
-GIRepository *gi_repository_get_default   (void);
+void          gi_repository_prepend_search_path (GIRepository *repository,
+                                                 const char   *directory);
 
 GI_AVAILABLE_IN_ALL
-GIRepository *gi_repository_new (void);
+void          gi_repository_prepend_library_path (GIRepository *repository,
+                                                  const char   *directory);
 
 GI_AVAILABLE_IN_ALL
-void          gi_repository_prepend_search_path (const char *directory);
+const char * const * gi_repository_get_search_path (GIRepository *repository,
+                                                    size_t       *n_paths_out);
 
 GI_AVAILABLE_IN_ALL
-void          gi_repository_prepend_library_path (const char *directory);
-
-GI_AVAILABLE_IN_ALL
-const char * const * gi_repository_get_search_path (size_t *n_paths_out);
+const char * const *gi_repository_get_library_path (GIRepository *repository,
+                                                    size_t       *n_paths_out);
 
 GI_AVAILABLE_IN_ALL
 const char *  gi_repository_load_typelib  (GIRepository           *repository,
@@ -152,14 +132,17 @@ GITypelib *    gi_repository_require_private (GIRepository           *repository
 
 GI_AVAILABLE_IN_ALL
 char       ** gi_repository_get_immediate_dependencies (GIRepository *repository,
-                                                        const char   *namespace_);
+                                                        const char   *namespace_,
+                                                        size_t       *n_dependencies_out);
 
 GI_AVAILABLE_IN_ALL
 char       ** gi_repository_get_dependencies (GIRepository *repository,
-                                              const char   *namespace_);
+                                              const char   *namespace_,
+                                              size_t       *n_dependencies_out);
 
 GI_AVAILABLE_IN_ALL
-char       ** gi_repository_get_loaded_namespaces (GIRepository *repository);
+char       ** gi_repository_get_loaded_namespaces (GIRepository *repository,
+                                                   size_t       *n_namespaces_out);
 
 GI_AVAILABLE_IN_ALL
 GIBaseInfo *  gi_repository_find_by_gtype (GIRepository *repository,
index b9e6ee8..053fd34 100644 (file)
@@ -32,6 +32,7 @@
 #include <unistd.h>
 #endif
 #include "girffi.h"
+#include "gibaseinfo-private.h"
 #include "girepository.h"
 #include "girepository-private.h"
 
@@ -207,7 +208,7 @@ gi_callable_info_get_ffi_arg_types (GICallableInfo *callable_info,
         GITypeInfo arg_type;
 
         gi_callable_info_load_arg (callable_info, i, &arg_info);
-        gi_arg_info_load_type (&arg_info, &arg_type);
+        gi_arg_info_load_type_info (&arg_info, &arg_type);
         switch (gi_arg_info_get_direction (&arg_info))
           {
             case GI_DIRECTION_IN:
@@ -220,6 +221,9 @@ gi_callable_info_get_ffi_arg_types (GICallableInfo *callable_info,
             default:
               g_assert_not_reached ();
           }
+
+        gi_base_info_clear (&arg_type);
+        gi_base_info_clear (&arg_info);
       }
 
     arg_types[n_invoke_args] = NULL;
@@ -266,6 +270,9 @@ gi_callable_info_get_ffi_return_type (GICallableInfo *callable_info)
  * by a language binding could contain a [type@GIRepository.FunctionInvoker]
  * structure inside the binding’s function mapping.
  *
+ * @invoker must be freed using [method@GIRepository.FunctionInvoker.clear]
+ * when it’s finished with.
+ *
  * Returns: `TRUE` on success, `FALSE` otherwise with @error set.
  * Since: 2.80
  */
@@ -336,7 +343,7 @@ gi_function_invoker_new_for_address (void               *addr,
 }
 
 /**
- * gi_function_invoker_destroy:
+ * gi_function_invoker_clear:
  * @invoker: (transfer none): A #GIFunctionInvoker
  *
  * Release all resources allocated for the internals of @invoker.
@@ -347,7 +354,7 @@ gi_function_invoker_new_for_address (void               *addr,
  * Since: 2.80
  */
 void
-gi_function_invoker_destroy (GIFunctionInvoker *invoker)
+gi_function_invoker_clear (GIFunctionInvoker *invoker)
 {
   g_free (invoker->cif.arg_types);
 }
index ed8c701..85197d8 100644 (file)
@@ -92,7 +92,7 @@ void          gi_type_info_extract_ffi_return_value (GITypeInfo
 
 GI_AVAILABLE_IN_ALL
 void          gi_type_tag_extract_ffi_return_value (GITypeTag         return_tag,
-                                                    GIInfoType        interface_type,
+                                                    GType             interface_type,
                                                     GIFFIReturnValue *ffi_value,
                                                     GIArgument       *arg);
 
@@ -108,7 +108,7 @@ gboolean      gi_function_invoker_new_for_address  (void                 *addr,
                                                     GError              **error);
 
 GI_AVAILABLE_IN_ALL
-void          gi_function_invoker_destroy          (GIFunctionInvoker    *invoker);
+void          gi_function_invoker_clear            (GIFunctionInvoker    *invoker);
 
 
 GI_AVAILABLE_IN_ALL
index 500d749..e595f7a 100644 (file)
 
 #define NUM_SECTIONS 2
 
+/*< private >
+ * gi_ir_module_new:
+ * @name:
+ * @version:
+ * @shared_library: (nullable):
+ * @c_prefix:
+ *
+ * Since: 2.80
+ */
 GIIrModule *
 gi_ir_module_new (const char *name,
                   const char *version,
@@ -49,10 +58,7 @@ gi_ir_module_new (const char *name,
 
   module->name = g_strdup (name);
   module->version = g_strdup (version);
-  if (shared_library)
-      module->shared_library = g_strdup (shared_library);
-  else
-      module->shared_library = NULL;
+  module->shared_library = g_strdup (shared_library);
   module->c_prefix = g_strdup (c_prefix);
   module->dependencies = NULL;
   module->entries = NULL;
@@ -318,6 +324,7 @@ GITypelib *
 gi_ir_module_build_typelib (GIIrModule *module)
 {
   GError *error = NULL;
+  GBytes *bytes = NULL;
   GITypelib *typelib;
   size_t length;
   size_t i;
@@ -568,7 +575,11 @@ gi_ir_module_build_typelib (GIIrModule *module)
   header = (Header *)data;
 
   length = header->size = offset2;
-  typelib = gi_typelib_new_from_memory (data, length, &error);
+
+  bytes = g_bytes_new_take (g_steal_pointer (&data), length);
+  typelib = gi_typelib_new_from_bytes (bytes, &error);
+  g_bytes_unref (bytes);
+
   if (!typelib)
     {
       g_error ("error building typelib: %s",
index 4b8a141..353d6d6 100644 (file)
@@ -94,19 +94,19 @@ typedef enum
 struct _GIIrNode
 {
   GIIrNodeTypeId type;
-  char *name;
-  GIIrModule *module;
+  char *name;  /* (owned) */
+  GIIrModule *module;  /* (unowned) */
 
   uint32_t offset; /* Assigned as we build the typelib */
 
-  GHashTable *attributes;
+  GHashTable *attributes;  /* (element-type utf8 utf8) (owned) */
 };
 
 struct _GIIrNodeXRef
 {
   GIIrNode node;
 
-  char *namespace;
+  char *namespace;  /* (owned) */
 };
 
 struct _GIIrNodeFunction
@@ -124,11 +124,11 @@ struct _GIIrNodeFunction
   uint8_t throws : 1;
   uint8_t instance_transfer_full : 1;
 
-  char *symbol;
-  char *property;
+  char *symbol;  /* (owned) */
+  char *property;  /* (owned) */
 
-  GIIrNodeParam *result;
-  GList *parameters;
+  GIIrNodeParam *result;  /* (owned) */
+  GList *parameters;  /* (element-type GIIrNode) (owned) */
 };
 
 struct _GIIrNodeType
@@ -145,20 +145,20 @@ struct _GIIrNodeType
   uint8_t is_error : 1;
   int tag;
 
-  char *unparsed;
+  char *unparsed; /* (owned) */
 
   uint8_t zero_terminated : 1;
   uint8_t has_length : 1;
-  int length;
+  unsigned int length;
   uint8_t has_size : 1;
-  int size;
-  int array_type;
+  size_t size;
+  GIArrayType array_type;
 
-  GIIrNodeType *parameter_type1;
-  GIIrNodeType *parameter_type2;
+  GIIrNodeType *parameter_type1;  /* (owned) */
+  GIIrNodeType *parameter_type2;  /* (owned) */
 
-  char *giinterface;
-  char **errors;
+  char *giinterface;  /* (owned) */
+  char **errors;  /* (array zero-terminated=1) (owned) */
 };
 
 struct _GIIrNodeParam
@@ -179,7 +179,7 @@ struct _GIIrNodeParam
   int8_t closure;
   int8_t destroy;
 
-  GIIrNodeType *type;
+  GIIrNodeType *type;  /* (owned) */
 };
 
 struct _GIIrNodeProperty
@@ -188,7 +188,7 @@ struct _GIIrNodeProperty
 
   uint8_t deprecated : 1;
 
-  char *name;
+  char *name;  /* (owned) */
   uint8_t readable : 1;
   uint8_t writable : 1;
   uint8_t construct : 1;
@@ -196,10 +196,10 @@ struct _GIIrNodeProperty
   uint8_t transfer : 1;
   uint8_t shallow_transfer : 1;
 
-  char *setter;
-  char *getter;
+  char *setter;  /* (owned) */
+  char *getter;  /* (owned) */
 
-  GIIrNodeType *type;
+  GIIrNodeType *type;  /* (owned) */
 };
 
 struct _GIIrNodeSignal
@@ -220,10 +220,10 @@ struct _GIIrNodeSignal
   uint8_t has_class_closure : 1;
   uint8_t true_stops_emit : 1;
 
-  int class_closure;
+  unsigned int class_closure;
 
-  GList *parameters;
-  GIIrNodeParam *result;
+  GList *parameters;  /* (element-type GIIrNode) (owned) */
+  GIIrNodeParam *result;  /* (owned) */
 };
 
 struct _GIIrNodeVFunc
@@ -238,12 +238,12 @@ struct _GIIrNodeVFunc
   uint8_t throws : 1;
   uint8_t instance_transfer_full : 1;
 
-  char *invoker;
+  char *invoker;  /* (owned) */
 
-  GList *parameters;
-  GIIrNodeParam *result;
+  GList *parameters;  /* (element-type GIIrNode) (owned) */
+  GIIrNodeParam *result;  /* (owned) */
 
-  int offset;
+  size_t offset;
 };
 
 struct _GIIrNodeField
@@ -252,11 +252,12 @@ struct _GIIrNodeField
 
   uint8_t readable : 1;
   uint8_t writable : 1;
-  int bits;
-  int offset;
-  GIIrNodeFunction *callback;
+  unsigned int bits;
+  size_t offset;
+  GIIrOffsetsState offset_state;
+  GIIrNodeFunction *callback;  /* (owned) */
 
-  GIIrNodeType *type;
+  GIIrNodeType *type;  /* (owned) */
 };
 
 struct _GIIrNodeInterface
@@ -268,25 +269,25 @@ struct _GIIrNodeInterface
   uint8_t fundamental : 1;
   uint8_t final_ : 1;
 
-  char *gtype_name;
-  char *gtype_init;
+  char *gtype_name;  /* (owned) */
+  char *gtype_init;  /* (owned) */
 
-  char *ref_func;
-  char *unref_func;
-  char *set_value_func;
-  char *get_value_func;
+  char *ref_func;  /* (owned) */
+  char *unref_func;  /* (owned) */
+  char *set_value_func;  /* (owned) */
+  char *get_value_func;  /* (owned) */
 
-  char *parent;
-  char *glib_type_struct;
+  char *parent;  /* (owned) */
+  char *glib_type_struct;  /* (owned) */
 
-  GList *interfaces;
-  GList *prerequisites;
+  GList *interfaces;  /* (element-type GIIrNode) (owned) */
+  GList *prerequisites; /* (element-type utf8) (owned) */
 
   size_t alignment;
   size_t size;
   GIIrOffsetsState offsets_state;
 
-  GList *members;
+  GList *members;  /* (element-type GIIrNode) (owned) */
 };
 
 struct _GIIrNodeValue
@@ -304,9 +305,9 @@ struct _GIIrNodeConstant
 
   uint8_t deprecated : 1;
 
-  GIIrNodeType *type;
+  GIIrNodeType *type;  /* (owned) */
 
-  char *value;
+  char *value;  /* (owned) */
 };
 
 struct _GIIrNodeEnum
@@ -314,14 +315,14 @@ struct _GIIrNodeEnum
   GIIrNode node;
 
   uint8_t deprecated : 1;
-  int storage_type;
+  GITypeTag storage_type;
 
-  char *gtype_name;
-  char *gtype_init;
-  char *error_domain;
+  char *gtype_name;  /* (owned) */
+  char *gtype_init;  /* (owned) */
+  char *error_domain;  /* (owned) */
 
-  GList *values;
-  GList *methods;
+  GList *values;  /* (element-type GIIrNode) (owned) */
+  GList *methods;  /* (element-type GIIrNode) (owned) */
 };
 
 struct _GIIrNodeBoxed
@@ -330,14 +331,14 @@ struct _GIIrNodeBoxed
 
   uint8_t deprecated : 1;
 
-  char *gtype_name;
-  char *gtype_init;
+  char *gtype_name;  /* (owned) */
+  char *gtype_init;  /* (owned) */
 
   size_t alignment;
   size_t size;
   GIIrOffsetsState offsets_state;
 
-  GList *members;
+  GList *members;  /* (element-type GIIrNode) (owned) */
 };
 
 struct _GIIrNodeStruct
@@ -351,17 +352,17 @@ struct _GIIrNodeStruct
   uint8_t is_gtype_struct : 1;
   uint8_t foreign : 1;
 
-  char *gtype_name;
-  char *gtype_init;
+  char *gtype_name;  /* (owned) */
+  char *gtype_init;  /* (owned) */
 
-  char *copy_func;
-  char *free_func;
+  char *copy_func;  /* (owned) */
+  char *free_func;  /* (owned) */
 
   size_t alignment;
   size_t size;
   GIIrOffsetsState offsets_state;
 
-  GList *members;
+  GList *members;  /* (element-type GIIrNode) (owned) */
 };
 
 struct _GIIrNodeUnion
@@ -370,21 +371,21 @@ struct _GIIrNodeUnion
 
   uint8_t deprecated : 1;
 
-  GList *members;
-  GList *discriminators;
+  GList *members;  /* (element-type GIIrNode) (owned) */
+  GList *discriminators;  /* (element-type GIIrNode) (owned) */
 
-  char *gtype_name;
-  char *gtype_init;
+  char *gtype_name;  /* (owned) */
+  char *gtype_init;  /* (owned) */
 
-  char *copy_func;
-  char *free_func;
+  char *copy_func;  /* (owned) */
+  char *free_func;  /* (owned) */
 
   size_t alignment;
   size_t size;
   GIIrOffsetsState offsets_state;
 
-  int discriminator_offset;
-  GIIrNodeType *discriminator_type;
+  size_t discriminator_offset;
+  GIIrNodeType *discriminator_type;  /* (owned) */
 };
 
 
index b1f243f..3a09830 100644 (file)
@@ -25,7 +25,7 @@
 #include "config.h"
 
 #include "girnode-private.h"
-
+#include "girepository-private.h"
 #include "gitypelib-internal.h"
 
 #include <stdio.h>
@@ -240,7 +240,7 @@ gi_ir_node_free (GIIrNode *node)
 
         g_free (type->giinterface);
         g_strfreev (type->errors);
-
+        g_free (type->unparsed);
       }
       break;
 
@@ -320,6 +320,8 @@ gi_ir_node_free (GIIrNode *node)
           g_free ((GIIrNode *)l->data);
         g_list_free (iface->interfaces);
 
+        g_list_free_full (iface->prerequisites, g_free);
+
         for (l = iface->members; l; l = l->next)
           gi_ir_node_free ((GIIrNode *)l->data);
         g_list_free (iface->members);
@@ -1241,7 +1243,7 @@ serialize_type (GIIrTypelibBuild *build,
           if (node->has_length)
             g_string_append_printf (str, "length=%d", node->length);
           else if (node->has_size)
-            g_string_append_printf (str, "fixed-size=%d", node->size);
+            g_string_append_printf (str, "fixed-size=%" G_GSIZE_FORMAT, node->size);
 
           if (node->zero_terminated)
             g_string_append_printf (str, "%szero-terminated=1",
@@ -1599,7 +1601,7 @@ gi_ir_node_build_typelib (GIIrNode         *node,
         blob->writable = field->writable;
         blob->reserved = 0;
         blob->bits = 0;
-        if (field->offset >= 0)
+        if (field->offset_state == GI_IR_OFFSETS_COMPUTED)
           blob->struct_offset = field->offset;
         else
           blob->struct_offset = 0xFFFF; /* mark as unknown */
index 19cbcbf..d3fe36a 100644 (file)
@@ -392,6 +392,7 @@ compute_struct_field_offsets (GIIrTypelibBuild *build,
                   size = GI_ALIGN (size, member_alignment);
                   alignment = MAX (alignment, member_alignment);
                   field->offset = size;
+                  field->offset_state = GI_IR_OFFSETS_COMPUTED;
                   size += member_size;
                 }
               else
@@ -399,7 +400,10 @@ compute_struct_field_offsets (GIIrTypelibBuild *build,
             }
 
           if (have_error)
-            field->offset = -1;
+            {
+              field->offset = 0;
+              field->offset_state = GI_IR_OFFSETS_FAILED;
+            }
         }
       else if (member->type == GI_IR_NODE_CALLBACK)
         {
index 3924259..0d65590 100644 (file)
@@ -1366,6 +1366,7 @@ start_field (GMarkupParseContext  *context,
   GIIrNodeField *field;
   ParseState target_state;
   gboolean introspectable;
+  guint64 parsed_bits;
 
   switch (ctx->state)
     {
@@ -1428,10 +1429,15 @@ start_field (GMarkupParseContext  *context,
   field->readable = readable == NULL || strcmp (readable, "0") == 0;
   field->writable = writable != NULL && strcmp (writable, "1") == 0;
 
-  if (bits)
-    field->bits = atoi (bits);
-  else
+  if (bits == NULL)
     field->bits = 0;
+  else if (g_ascii_string_to_unsigned (bits, 10, 0, G_MAXUINT, &parsed_bits, error))
+    field->bits = parsed_bits;
+  else
+    {
+      gi_ir_node_free ((GIIrNode *) field);
+      return FALSE;
+    }
 
   switch (CURRENT_NODE (ctx)->type)
     {
@@ -2100,15 +2106,33 @@ start_type (GMarkupParseContext  *context,
       }
 
       if (typenode->array_type == GI_ARRAY_TYPE_C) {
+          guint64 parsed_uint;
+
           zero = find_attribute ("zero-terminated", attribute_names, attribute_values);
           len = find_attribute ("length", attribute_names, attribute_values);
           size = find_attribute ("fixed-size", attribute_names, attribute_values);
 
           typenode->has_length = len != NULL;
-          typenode->length = typenode->has_length ? atoi (len) : -1;
+          if (!typenode->has_length)
+            typenode->length = -1;
+          else if (g_ascii_string_to_unsigned (len, 10, 0, G_MAXUINT, &parsed_uint, error))
+            typenode->length = parsed_uint;
+          else
+            {
+              gi_ir_node_free ((GIIrNode *) typenode);
+              return FALSE;
+            }
 
           typenode->has_size = size != NULL;
-          typenode->size = typenode->has_size ? atoi (size) : -1;
+          if (!typenode->has_size)
+            typenode->size = -1;
+          else if (g_ascii_string_to_unsigned (size, 10, 0, G_MAXSIZE, &parsed_uint, error))
+            typenode->size = parsed_uint;
+          else
+            {
+              gi_ir_node_free ((GIIrNode *) typenode);
+              return FALSE;
+            }
 
           if (zero)
             typenode->zero_terminated = strcmp(zero, "1") == 0;
@@ -2543,6 +2567,7 @@ start_vfunc (GMarkupParseContext  *context,
   const char *throws;
   GIIrNodeInterface *iface;
   GIIrNodeVFunc *vfunc;
+  guint64 parsed_offset;
 
   if (!(strcmp (element_name, "virtual-method") == 0 &&
         (ctx->state == STATE_CLASS ||
@@ -2602,10 +2627,15 @@ start_vfunc (GMarkupParseContext  *context,
   else
     vfunc->throws = FALSE;
 
-  if (offset)
-    vfunc->offset = atoi (offset);
-  else
+  if (offset == NULL)
     vfunc->offset = 0xFFFF;
+  else if (g_ascii_string_to_unsigned (offset, 10, 0, G_MAXSIZE, &parsed_offset, error))
+    vfunc->offset = parsed_offset;
+  else
+    {
+      gi_ir_node_free ((GIIrNode *) vfunc);
+      return FALSE;
+    }
 
   vfunc->invoker = g_strdup (invoker);
 
@@ -2780,6 +2810,8 @@ start_discriminator (GMarkupParseContext  *context,
 {
   const char *type;
   const char *offset;
+  guint64 parsed_offset;
+
   if (!(strcmp (element_name, "discriminator") == 0 &&
         ctx->state == STATE_UNION))
     return FALSE;
@@ -2799,8 +2831,11 @@ start_discriminator (GMarkupParseContext  *context,
 
   ((GIIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_type
     = parse_type (ctx, type);
-  ((GIIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_offset
-    = atoi (offset);
+
+  if (g_ascii_string_to_unsigned (offset, 10, 0, G_MAXSIZE, &parsed_offset, error))
+    ((GIIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_offset = parsed_offset;
+  else
+    return FALSE;
 
   return TRUE;
 }
index 84169f9..4b771bd 100644 (file)
@@ -26,7 +26,9 @@
 
 #include "girwriter-private.h"
 
+#include "gibaseinfo-private.h"
 #include "girepository.h"
+#include "girepository-private.h"
 #include "gitypelib-internal.h"
 
 #include <errno.h>
@@ -165,7 +167,7 @@ xml_free (Xml *xml)
 static void
 check_unresolved (GIBaseInfo *info)
 {
-  if (gi_base_info_get_info_type (info) != GI_INFO_TYPE_UNRESOLVED)
+  if (!GI_IS_UNRESOLVED_INFO (info))
     return;
 
   g_critical ("Found unresolved type '%s' '%s'",
@@ -440,7 +442,7 @@ write_field_info (const char *ns,
     }
 
   interface = gi_type_info_get_interface (type);
-  if (interface && gi_base_info_get_info_type (interface) == GI_INFO_TYPE_CALLBACK)
+  if (interface != NULL && GI_IS_CALLBACK_INFO (interface))
     write_callback_info (ns, (GICallbackInfo *)interface, file);
   else
     write_type_info (ns, type, file);
@@ -658,7 +660,7 @@ write_struct_info (const char   *ns,
   type_name = gi_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info);
   type_init = gi_registered_type_info_get_type_init_function_name ((GIRegisteredTypeInfo*)info);
 
-  if (gi_base_info_get_info_type ((GIBaseInfo *) info) == GI_INFO_TYPE_BOXED)
+  if (gi_registered_type_info_is_boxed (GI_REGISTERED_TYPE_INFO (info)))
     {
       xml_start_element (file, "glib:boxed");
       xml_printf (file, " glib:name=\"%s\"", name);
@@ -845,7 +847,7 @@ write_enum_info (const char *ns,
   type_init = gi_registered_type_info_get_type_init_function_name ((GIRegisteredTypeInfo*)info);
   error_domain = gi_enum_info_get_error_domain (info);
 
-  if (gi_base_info_get_info_type ((GIBaseInfo *) info) == GI_INFO_TYPE_ENUM)
+  if (GI_IS_ENUM_INFO (info))
     xml_start_element (file, "enumeration");
   else
     xml_start_element (file, "bitfield");
@@ -1255,6 +1257,7 @@ write_union_info (const char *ns,
   type_name = gi_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info);
   type_init = gi_registered_type_info_get_type_init_function_name ((GIRegisteredTypeInfo*)info);
 
+  /* FIXME: Add support for boxed unions */
   xml_start_element (file, "union");
   xml_printf (file, " name=\"%s\"", name);
 
@@ -1283,7 +1286,7 @@ write_union_info (const char *ns,
       size_t offset;
       GITypeInfo *type;
 
-      offset = gi_union_info_get_discriminator_offset (info);
+      gi_union_info_get_discriminator_offset (info, &offset);
       type = gi_union_info_get_discriminator_type (info);
 
       xml_start_element (file, "discriminator");
@@ -1335,10 +1338,10 @@ gi_ir_writer_write (const char *filename,
   FILE *ofile;
   size_t i, j;
   char **dependencies;
-  GIRepository *repository;
+  GIRepository *repository = NULL;
   Xml *xml;
 
-  repository = gi_repository_get_default ();
+  repository = gi_repository_new ();
 
   if (filename == NULL)
     ofile = stdout;
@@ -1373,7 +1376,7 @@ gi_ir_writer_write (const char *filename,
               "            xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"\n"
               "            xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\"");
 
-  dependencies = gi_repository_get_immediate_dependencies (repository, ns);
+  dependencies = gi_repository_get_immediate_dependencies (repository, ns, NULL);
   if (dependencies != NULL)
     {
       for (i = 0; dependencies[i]; i++)
@@ -1413,45 +1416,26 @@ gi_ir_writer_write (const char *filename,
       for (j = 0; j < n_infos; j++)
         {
           GIBaseInfo *info = gi_repository_get_info (repository, cur_ns, j);
-          switch (gi_base_info_get_info_type (info))
-            {
-            case GI_INFO_TYPE_FUNCTION:
-              write_function_info (ns, (GIFunctionInfo *)info, xml);
-              break;
-
-            case GI_INFO_TYPE_CALLBACK:
-              write_callback_info (ns, (GICallbackInfo *)info, xml);
-              break;
-
-            case GI_INFO_TYPE_STRUCT:
-            case GI_INFO_TYPE_BOXED:
-              write_struct_info (ns, (GIStructInfo *)info, xml);
-              break;
-
-            case GI_INFO_TYPE_UNION:
-              write_union_info (ns, (GIUnionInfo *)info, xml);
-              break;
-
-            case GI_INFO_TYPE_ENUM:
-            case GI_INFO_TYPE_FLAGS:
-              write_enum_info (ns, (GIEnumInfo *)info, xml);
-              break;
-
-            case GI_INFO_TYPE_CONSTANT:
-              write_constant_info (ns, (GIConstantInfo *)info, xml);
-              break;
-
-            case GI_INFO_TYPE_OBJECT:
-              write_object_info (ns, (GIObjectInfo *)info, xml);
-              break;
-
-            case GI_INFO_TYPE_INTERFACE:
-              write_interface_info (ns, (GIInterfaceInfo *)info, xml);
-              break;
-
-            default:
-              g_error ("unknown info type %d", gi_base_info_get_info_type (info));
-            }
+
+          if (GI_IS_FUNCTION_INFO (info))
+            write_function_info (ns, (GIFunctionInfo *)info, xml);
+          else if (GI_IS_CALLBACK_INFO (info))
+            write_callback_info (ns, (GICallbackInfo *)info, xml);
+          else if (GI_IS_STRUCT_INFO (info))
+            write_struct_info (ns, (GIStructInfo *)info, xml);
+          else if (GI_IS_UNION_INFO (info))
+            write_union_info (ns, (GIUnionInfo *)info, xml);
+          else if (GI_IS_ENUM_INFO (info) ||
+                   GI_IS_FLAGS_INFO (info))
+            write_enum_info (ns, (GIEnumInfo *)info, xml);
+          else if (GI_IS_CONSTANT_INFO (info))
+            write_constant_info (ns, (GIConstantInfo *)info, xml);
+          else if (GI_IS_OBJECT_INFO (info))
+            write_object_info (ns, (GIObjectInfo *)info, xml);
+          else if (GI_IS_INTERFACE_INFO (info))
+            write_interface_info (ns, (GIInterfaceInfo *)info, xml);
+          else
+            g_error ("unknown info type %s", g_type_name (G_TYPE_FROM_INSTANCE (info)));
 
           gi_base_info_unref (info);
         }
@@ -1462,4 +1446,6 @@ gi_ir_writer_write (const char *filename,
   xml_end_element (xml, "repository");
 
   xml_free (xml);
+
+  g_clear_object (&repository);
 }
index d2c31c1..a122a69 100644 (file)
@@ -111,8 +111,8 @@ gi_struct_info_get_field (GIStructInfo *info,
 
   g_return_val_if_fail (n <= G_MAXUINT16, NULL);
 
-  return (GIFieldInfo *) gi_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib,
-                                      gi_struct_get_field_offset (info, n));
+  return (GIFieldInfo *) gi_base_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib,
+                                           gi_struct_get_field_offset (info, n));
 }
 
 /**
@@ -143,10 +143,10 @@ gi_struct_info_find_field (GIStructInfo *info,
 
       if (strcmp (name, fname) == 0)
         {
-          return (GIFieldInfo *) gi_info_new (GI_INFO_TYPE_FIELD,
-                                              (GIBaseInfo* )info,
-                                              rinfo->typelib,
-                                              offset);
+          return (GIFieldInfo *) gi_base_info_new (GI_INFO_TYPE_FIELD,
+                                                   (GIBaseInfo* )info,
+                                                   rinfo->typelib,
+                                                   offset);
         }
 
       offset += header->field_blob_size;
@@ -198,8 +198,8 @@ gi_struct_info_get_method (GIStructInfo *info,
   g_return_val_if_fail (n <= G_MAXUINT16, NULL);
 
   offset = gi_struct_get_field_offset (info, blob->n_fields) + n * header->function_blob_size;
-  return (GIFunctionInfo *) gi_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
-                                         rinfo->typelib, offset);
+  return (GIFunctionInfo *) gi_base_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
+                                              rinfo->typelib, offset);
 }
 
 /**
index 3aa6c93..e07ab8e 100644 (file)
@@ -178,8 +178,9 @@ gi_type_info_get_param_type (GITypeInfo  *info,
  *
  * You can then inspect the type of the returned [class@GIRepository.BaseInfo]
  * to further query whether it is a concrete [class@GObject.Object], an
- * interface, a structure, etc., using
- * [method@GIRepository.BaseInfo.get_info_type].
+ * interface, a structure, etc., using the type checking macros like
+ * [func@GIRepository.IS_OBJECT_INFO], or raw [type@GObject.Type]s with
+ * [func@GObject.TYPE_FROM_INSTANCE].
  *
  * Returns: (transfer full) (nullable): The [class@GIRepository.BaseInfo], or
  *   `NULL`. Free it with gi_base_info_unref() when done.
@@ -211,8 +212,8 @@ gi_type_info_get_interface (GITypeInfo *info)
             g_assert_not_reached ();
             return NULL;
         }
-      return (GIBaseInfo *) gi_info_new (info_type, (GIBaseInfo*)info, rinfo->typelib,
-                                         rinfo->offset);
+      return (GIBaseInfo *) gi_base_info_new (info_type, (GIBaseInfo*)info, rinfo->typelib,
+                                              rinfo->offset);
     }
   else
     {
index 65cb782..73f0ce4 100644 (file)
@@ -94,10 +94,6 @@ GI_AVAILABLE_IN_ALL
 const char *           gi_type_tag_to_string            (GITypeTag   type);
 
 GI_AVAILABLE_IN_ALL
-const char *           gi_info_type_to_string           (GIInfoType  type);
-
-
-GI_AVAILABLE_IN_ALL
 gboolean               gi_type_info_is_pointer          (GITypeInfo *info);
 
 GI_AVAILABLE_IN_ALL
index 9462605..8df65fb 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <gmodule.h>
 #include "girepository.h"
+#include "girepository-private.h"
 
 G_BEGIN_DECLS
 
@@ -179,6 +180,7 @@ Changes since 0.1:
  * Since: 2.80
  */
 typedef enum {
+  /* The values here must be kept in sync with GIInfoType */
   BLOB_TYPE_INVALID,
   BLOB_TYPE_FUNCTION,
   BLOB_TYPE_CALLBACK,
@@ -193,6 +195,8 @@ typedef enum {
   BLOB_TYPE_UNION
 } GITypelibBlobType;
 
+GIInfoType gi_typelib_blob_type_to_info_type (GITypelibBlobType blob_type);
+
 
 #if defined (G_CAN_INLINE) && defined (G_ALWAYS_INLINE)
 
@@ -1313,12 +1317,13 @@ typedef struct {
 
 struct _GITypelib {
   /*< private >*/
-  uint8_t *data;
+  gatomicrefcount ref_count;
+  const uint8_t *data;  /* just a cached pointer to inside @bytes */
   size_t len;
-  gboolean owns_memory;
-  GMappedFile *mfile;
+  GBytes *bytes;  /* (owned) */
   GList *modules;
   gboolean open_attempted;
+  GPtrArray *library_paths;  /* (element-type filename) (owned) (nullable) */
 };
 
 DirEntry *gi_typelib_get_dir_entry (GITypelib *typelib,
index e2d1abe..fecfbb1 100644 (file)
  * Since: 2.80
  */
 
+G_DEFINE_BOXED_TYPE (GITypelib, gi_typelib, gi_typelib_ref, gi_typelib_unref)
+
+GIInfoType
+gi_typelib_blob_type_to_info_type (GITypelibBlobType blob_type)
+{
+  switch (blob_type)
+    {
+    case BLOB_TYPE_BOXED:
+      /* `BLOB_TYPE_BOXED` now always refers to a `StructBlob`, and
+       * `GIRegisteredTypeInfo` (the parent type of `GIStructInfo`) has a method
+       * for distinguishing whether the struct is a boxed type. So presenting
+       * `BLOB_TYPE_BOXED` as its own `GIBaseInfo` subclass is not helpful.
+       * See commit e28078c70cbf4a57c7dbd39626f43f9bd2674145 and
+       * https://gitlab.gnome.org/GNOME/glib/-/issues/3245. */
+      return GI_INFO_TYPE_STRUCT;
+    default:
+      return (GIInfoType) blob_type;
+    }
+}
+
 typedef struct {
   GITypelib *typelib;
   GSList *context_stack;
@@ -2213,36 +2233,6 @@ gi_typelib_error_quark (void)
   return quark;
 }
 
-static GSList *library_paths;
-
-/**
- * gi_repository_prepend_library_path:
- * @directory: (type filename): a single directory to scan for shared libraries
- *
- * Prepends @directory to the search path that is used to
- * search shared libraries referenced by imported namespaces.
- *
- * Multiple calls to this function all contribute to the final
- * list of paths.
- *
- * The list of paths is unique and shared for all
- * [class@GIRepository.Repository] instances across the process, but it doesn’t
- * affect namespaces imported before the call.
- *
- * If the library is not found in the directories configured
- * in this way, loading will fall back to the system library
- * path (i.e. `LD_LIBRARY_PATH` and `DT_RPATH` in ELF systems).
- * See the documentation of your dynamic linker for full details.
- *
- * Since: 2.80
- */
-void
-gi_repository_prepend_library_path (const char *directory)
-{
-  library_paths = g_slist_prepend (library_paths,
-                                   g_strdup (directory));
-}
-
 /* Note on the GModule flags used by this function:
 
  * Glade's autoconnect feature and OpenGL's extension mechanism
@@ -2254,9 +2244,9 @@ gi_repository_prepend_library_path (const char *directory)
  * load modules globally for now.
  */
 static GModule *
-load_one_shared_library (const char *shlib)
+load_one_shared_library (GITypelib  *typelib,
+                         const char *shlib)
 {
-  GSList *p;
   GModule *m;
 
 #ifdef __APPLE__
@@ -2272,9 +2262,9 @@ load_one_shared_library (const char *shlib)
 #endif
     {
       /* First try in configured library paths */
-      for (p = library_paths; p; p = p->next)
+      for (unsigned int i = 0; typelib->library_paths != NULL && i < typelib->library_paths->len; i++)
         {
-          char *path = g_build_filename (p->data, shlib, NULL);
+          char *path = g_build_filename (typelib->library_paths->pdata[i], shlib, NULL);
 
           m = g_module_open (path, G_MODULE_BIND_LAZY);
 
@@ -2319,7 +2309,7 @@ gi_typelib_do_dlopen (GITypelib *typelib)
         {
           GModule *module;
 
-          module = load_one_shared_library (shlibs[i]);
+          module = load_one_shared_library (typelib, shlibs[i]);
 
           if (module == NULL)
             {
@@ -2359,121 +2349,86 @@ gi_typelib_ensure_open (GITypelib *typelib)
 }
 
 /**
- * gi_typelib_new_from_memory: (skip)
- * @memory: (array length=len): address of memory chunk containing the typelib
- * @len: length of memory chunk containing the typelib, in bytes
+ * gi_typelib_new_from_bytes:
+ * @bytes: memory chunk containing the typelib
  * @error: a [type@GLib.Error]
  *
- * Creates a new [type@GIRepository.Typelib] from a memory location.
- *
- * The memory block pointed to by @typelib will be automatically freed when the
- * repository is destroyed.
- *
- * Returns: (transfer full): the new [type@GIRepository.Typelib]
- * Since: 2.80
- */
-GITypelib *
-gi_typelib_new_from_memory (uint8_t *memory,
-                            size_t   len,
-                            GError **error)
-{
-  GITypelib *meta;
-
-  if (!validate_header_basic (memory, len, error))
-    return NULL;
-
-  meta = g_slice_new0 (GITypelib);
-  meta->data = memory;
-  meta->len = len;
-  meta->owns_memory = TRUE;
-  meta->modules = NULL;
-
-  return meta;
-}
-
-/**
- * gi_typelib_new_from_const_memory: (skip)
- * @memory: (array length=len): address of memory chunk containing the typelib
- * @len: length of memory chunk containing the typelib
- * @error: a [type@GLib.Error]
+ * Creates a new [type@GIRepository.Typelib] from a [type@GLib.Bytes].
  *
- * Creates a new [type@GIRepository.Typelib] from a memory location.
+ * The [type@GLib.Bytes] can point to a memory location or a mapped file, and
+ * the typelib will hold a reference to it until the repository is destroyed.
  *
  * Returns: (transfer full): the new [type@GIRepository.Typelib]
  * Since: 2.80
  */
 GITypelib *
-gi_typelib_new_from_const_memory (const uint8_t  *memory,
-                                  size_t          len,
-                                  GError        **error)
+gi_typelib_new_from_bytes (GBytes *bytes,
+                           GError **error)
 {
   GITypelib *meta;
+  size_t len;
+  const uint8_t *data = g_bytes_get_data (bytes, &len);
 
-  if (!validate_header_basic (memory, len, error))
+  if (!validate_header_basic (data, len, error))
     return NULL;
 
   meta = g_slice_new0 (GITypelib);
-  meta->data = (uint8_t *) memory;
+  g_atomic_ref_count_init (&meta->ref_count);
+  meta->bytes = g_bytes_ref (bytes);
+  meta->data = data;
   meta->len = len;
-  meta->owns_memory = FALSE;
   meta->modules = NULL;
 
   return meta;
 }
 
 /**
- * gi_typelib_new_from_mapped_file: (skip)
- * @mfile: (transfer full): a [type@GLib.MappedFile], that will be freed when
- *   the repository is destroyed
- * @error: a #GError
+ * gi_typelib_ref:
+ * @typelib: (transfer none): a #GITypelib
  *
- * Creates a new [type@GIRepository.Typelib] from a [type@GLib.MappedFile].
+ * Increment the reference count of a [type@GIRepository.Typelib].
  *
- * Returns: (transfer full): the new [type@GIRepository.Typelib]
+ * Returns: (transfer full): the same @typelib pointer
  * Since: 2.80
  */
 GITypelib *
-gi_typelib_new_from_mapped_file (GMappedFile  *mfile,
-                                 GError      **error)
+gi_typelib_ref (GITypelib *typelib)
 {
-  GITypelib *meta;
-  uint8_t *data = (uint8_t *) g_mapped_file_get_contents (mfile);
-  size_t len = g_mapped_file_get_length (mfile);
+  g_return_val_if_fail (typelib != NULL, NULL);
 
-  if (!validate_header_basic (data, len, error))
-    return NULL;
+  g_atomic_ref_count_inc (&typelib->ref_count);
 
-  meta = g_slice_new0 (GITypelib);
-  meta->mfile = mfile;
-  meta->owns_memory = FALSE;
-  meta->data = data; 
-  meta->len = len;
-
-  return meta;
+  return typelib;
 }
 
 /**
- * gi_typelib_free:
+ * gi_typelib_unref:
  * @typelib: (transfer full): a #GITypelib
  *
- * Free a [type@GIRepository.Typelib].
+ * Decrement the reference count of a [type@GIRepository.Typelib].
+ *
+ * Once the reference count reaches zero, the typelib is freed.
  *
  * Since: 2.80
  */
 void
-gi_typelib_free (GITypelib *typelib)
+gi_typelib_unref (GITypelib *typelib)
 {
-  if (typelib->mfile)
-    g_mapped_file_unref (typelib->mfile);
-  else
-    if (typelib->owns_memory)
-      g_free (typelib->data);
-  if (typelib->modules)
+  g_return_if_fail (typelib != NULL);
+
+  if (g_atomic_ref_count_dec (&typelib->ref_count))
     {
-      g_list_foreach (typelib->modules, (GFunc) (void *) g_module_close, NULL);
-      g_list_free (typelib->modules);
+      g_clear_pointer (&typelib->bytes, g_bytes_unref);
+
+      g_clear_pointer (&typelib->library_paths, g_ptr_array_unref);
+
+      if (typelib->modules)
+        {
+          g_list_foreach (typelib->modules, (GFunc) (void *) g_module_close, NULL);
+          g_list_free (typelib->modules);
+        }
+      g_slice_free (GITypelib, typelib);
     }
-  g_slice_free (GITypelib, typelib);
 }
 
 /**
index ec03759..add5804 100644 (file)
@@ -36,22 +36,18 @@ G_BEGIN_DECLS
 
 typedef struct _GITypelib GITypelib;
 
+#define GI_TYPE_TYPELIB (gi_typelib_get_type ())
 GI_AVAILABLE_IN_ALL
-GITypelib *    gi_typelib_new_from_memory       (uint8_t *memory,
-                                                 size_t   len,
-                                                 GError **error);
+GType gi_typelib_get_type (void) G_GNUC_CONST;
 
 GI_AVAILABLE_IN_ALL
-GITypelib *    gi_typelib_new_from_const_memory (const uint8_t *memory,
-                                                 size_t         len,
-                                                 GError       **error);
+GITypelib *    gi_typelib_new_from_bytes       (GBytes  *bytes,
+                                                GError **error);
 
 GI_AVAILABLE_IN_ALL
-GITypelib *    gi_typelib_new_from_mapped_file  (GMappedFile  *mfile,
-                                                 GError      **error);
-
+GITypelib *   gi_typelib_ref                   (GITypelib     *typelib);
 GI_AVAILABLE_IN_ALL
-void          gi_typelib_free                  (GITypelib     *typelib);
+void          gi_typelib_unref                 (GITypelib     *typelib);
 
 GI_AVAILABLE_IN_ALL
 gboolean      gi_typelib_symbol                (GITypelib     *typelib,
index 587b8ec..21c873b 100644 (file)
@@ -41,6 +41,17 @@ G_BEGIN_DECLS
 typedef struct _GIBaseInfo GIBaseInfo;
 typedef struct _GIBaseInfoClass GIBaseInfoClass;
 
+typedef struct
+{
+  /*< private >*/
+  GTypeInstance parent_instance;
+
+  int dummy0;
+  void *dummy1[3];
+  uint32_t dummy2[2];
+  void *dummy3[6];
+} GIBaseInfoStack;
+
 /* Documented in gicallableinfo.c */
 typedef struct _GICallableInfo GICallableInfo;
 GI_AVAILABLE_IN_ALL GType gi_callable_info_get_type (void);
@@ -81,10 +92,6 @@ GI_AVAILABLE_IN_ALL GType gi_object_info_get_type (void);
 typedef struct _GIInterfaceInfo GIInterfaceInfo;
 GI_AVAILABLE_IN_ALL GType gi_interface_info_get_type (void);
 
-/* Documented in giboxedinfo.c */
-typedef struct _GIBoxedInfo GIBoxedInfo;
-GI_AVAILABLE_IN_ALL GType gi_boxed_info_get_type (void);
-
 /* Documented in giconstantinfo.c */
 typedef struct _GIConstantInfo GIConstantInfo;
 GI_AVAILABLE_IN_ALL GType gi_constant_info_get_type (void);
@@ -110,11 +117,23 @@ typedef struct _GIFieldInfo GIFieldInfo;
 GI_AVAILABLE_IN_ALL GType gi_field_info_get_type (void);
 
 /* Documented in giarginfo.c */
-typedef struct _GIArgInfo GIArgInfo;
+typedef struct
+{
+  /*< private >*/
+  GIBaseInfoStack parent;
+
+  void *padding[6];
+} GIArgInfo;
 GI_AVAILABLE_IN_ALL GType gi_arg_info_get_type (void);
 
 /* Documented in gitypeinfo.c */
-typedef struct _GITypeInfo GITypeInfo;
+typedef struct
+{
+  /*< private >*/
+  GIBaseInfoStack parent;
+
+  void *padding[6];
+} GITypeInfo;
 GI_AVAILABLE_IN_ALL GType gi_type_info_get_type (void);
 
 /* Documented in giunresolvedinfo.c */
@@ -177,81 +196,6 @@ union _GIArgument
 typedef union _GIArgument GIArgument;
 
 /**
- * GIInfoType:
- * @GI_INFO_TYPE_INVALID: invalid type
- * @GI_INFO_TYPE_FUNCTION: function, see [class@GIRepository.FunctionInfo]
- * @GI_INFO_TYPE_CALLBACK: callback, see [class@GIRepository.FunctionInfo]
- * @GI_INFO_TYPE_STRUCT: struct, see [class@GIRepository.StructInfo]
- * @GI_INFO_TYPE_BOXED: boxed, see [class@GIRepository.StructInfo] or
- *   [class@GIRepository.UnionInfo]
- * @GI_INFO_TYPE_ENUM: enum, see [class@GIRepository.EnumInfo]
- * @GI_INFO_TYPE_FLAGS: flags, see [class@GIRepository.EnumInfo]
- * @GI_INFO_TYPE_OBJECT: object, see [class@GIRepository.ObjectInfo]
- * @GI_INFO_TYPE_INTERFACE: interface, see [class@GIRepository.InterfaceInfo]
- * @GI_INFO_TYPE_CONSTANT: constant, see [class@GIRepository.ConstantInfo]
- * @GI_INFO_TYPE_UNION: union, see [class@GIRepository.UnionInfo]
- * @GI_INFO_TYPE_VALUE: enum value, see [class@GIRepository.ValueInfo]
- * @GI_INFO_TYPE_SIGNAL: signal, see [class@GIRepository.SignalInfo]
- * @GI_INFO_TYPE_VFUNC: virtual function, see [class@GIRepository.VFuncInfo]
- * @GI_INFO_TYPE_PROPERTY: [class@GObject.Object] property, see
- *   [class@GIRepository.PropertyInfo]
- * @GI_INFO_TYPE_FIELD: struct or union field, see
- *   [class@GIRepository.FieldInfo]
- * @GI_INFO_TYPE_ARG: argument of a function or callback, see
- *   [class@GIRepository.ArgInfo]
- * @GI_INFO_TYPE_TYPE: type information, see [class@GIRepository.TypeInfo]
- * @GI_INFO_TYPE_UNRESOLVED: unresolved type, a type which is not present in
- *   the typelib, or any of its dependencies, see
- *   [class@GIRepository.UnresolvedInfo]
- * @GI_INFO_TYPE_CALLABLE: an abstract type representing any callable (function,
- *   callback, vfunc), see [class@GIRepository.CallableInfo]
- * @GI_INFO_TYPE_REGISTERED_TYPE: an abstract type representing any registered
- *   type (enum, interface, object, struct, union), see
- *   [class@GIRepository.RegisteredTypeInfo]
- *
- * The type of a [class@GIRepository.BaseInfo] struct.
- *
- * See [const@GIRepository.INFO_TYPE_N_TYPES] for the total number of elements
- * in this enum.
- *
- * Since: 2.80
- */
-typedef enum
-{
-  GI_INFO_TYPE_INVALID,
-  GI_INFO_TYPE_FUNCTION,
-  GI_INFO_TYPE_CALLBACK,
-  GI_INFO_TYPE_STRUCT,
-  GI_INFO_TYPE_BOXED,
-  GI_INFO_TYPE_ENUM,             /*  5 */
-  GI_INFO_TYPE_FLAGS,
-  GI_INFO_TYPE_OBJECT,
-  GI_INFO_TYPE_INTERFACE,
-  GI_INFO_TYPE_CONSTANT,
-  GI_INFO_TYPE_UNION,            /* 10 */
-  GI_INFO_TYPE_VALUE,
-  GI_INFO_TYPE_SIGNAL,
-  GI_INFO_TYPE_VFUNC,
-  GI_INFO_TYPE_PROPERTY,
-  GI_INFO_TYPE_FIELD,            /* 15 */
-  GI_INFO_TYPE_ARG,
-  GI_INFO_TYPE_TYPE,
-  GI_INFO_TYPE_UNRESOLVED,
-  GI_INFO_TYPE_CALLABLE,
-  GI_INFO_TYPE_REGISTERED_TYPE,  /* 20 */
-  /* keep GI_INFO_TYPE_N_TYPES in sync with this */
-} GIInfoType;
-
-/**
- * GI_INFO_TYPE_N_TYPES:
- *
- * Number of entries in [enum@GIRepository.InfoType].
- *
- * Since: 2.80
- */
-#define GI_INFO_TYPE_N_TYPES (GI_INFO_TYPE_REGISTERED_TYPE + 1)
-
-/**
  * GITransfer:
  * @GI_TRANSFER_NOTHING: Transfer nothing from the callee (function or the type
  *   instance the property belongs to) to the caller. The callee retains the
@@ -441,7 +385,6 @@ typedef enum
  * @GI_VFUNC_MUST_CHAIN_UP: chains up to the parent type
  * @GI_VFUNC_MUST_OVERRIDE: overrides
  * @GI_VFUNC_MUST_NOT_OVERRIDE: does not override
- * @GI_VFUNC_THROWS: includes a [type@GLib.Error]
  *
  * Flags of a [class@GIRepository.VFuncInfo] struct.
  *
@@ -452,7 +395,6 @@ typedef enum
   GI_VFUNC_MUST_CHAIN_UP     = 1 << 0,
   GI_VFUNC_MUST_OVERRIDE     = 1 << 1,
   GI_VFUNC_MUST_NOT_OVERRIDE = 1 << 2,
-  GI_VFUNC_THROWS =            1 << 3
 } GIVFuncInfoFlags;
 
 /**
@@ -462,7 +404,6 @@ typedef enum
  * @GI_FUNCTION_IS_GETTER: is a getter of a [class@GIRepository.PropertyInfo].
  * @GI_FUNCTION_IS_SETTER: is a setter of a [class@GIRepository.PropertyInfo].
  * @GI_FUNCTION_WRAPS_VFUNC: represents a virtual function.
- * @GI_FUNCTION_THROWS: the function may throw an error.
  *
  * Flags for a [class@GIRepository.FunctionInfo] struct.
  *
@@ -475,7 +416,6 @@ typedef enum
   GI_FUNCTION_IS_GETTER      = 1 << 2,
   GI_FUNCTION_IS_SETTER      = 1 << 3,
   GI_FUNCTION_WRAPS_VFUNC    = 1 << 4,
-  GI_FUNCTION_THROWS         = 1 << 5
 } GIFunctionInfoFlags;
 
 G_END_DECLS
index d64ec2c..de9a34b 100644 (file)
@@ -80,9 +80,9 @@ gi_union_info_get_field (GIUnionInfo *info,
   GIRealInfo *rinfo = (GIRealInfo *)info;
   Header *header = (Header *)rinfo->typelib->data;
 
-  return (GIFieldInfo *) gi_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib,
-                                      rinfo->offset + header->union_blob_size +
-                                      n * header->field_blob_size);
+  return (GIFieldInfo *) gi_base_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib,
+                                           rinfo->offset + header->union_blob_size +
+                                           n * header->field_blob_size);
 }
 
 /**
@@ -126,8 +126,8 @@ gi_union_info_get_method (GIUnionInfo *info,
   offset = rinfo->offset + header->union_blob_size
     + blob->n_fields * header->field_blob_size
     + n * header->function_blob_size;
-  return (GIFunctionInfo *) gi_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
-                                         rinfo->typelib, offset);
+  return (GIFunctionInfo *) gi_base_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
+                                              rinfo->typelib, offset);
 }
 
 /**
@@ -151,19 +151,30 @@ gi_union_info_is_discriminated (GIUnionInfo *info)
 /**
  * gi_union_info_get_discriminator_offset:
  * @info: a #GIUnionInfo
+ * @out_offset: (out) (optional): return location for the offset, in bytes, of
+ *   the discriminator
  *
- * Returns the offset of the discriminator field in the structure.
+ * Obtain the offset of the discriminator field within the structure.
  *
- * Returns: offset, in bytes, of the discriminator
+ * The union must be discriminated, or `FALSE` will be returned.
+ *
+ * Returns: `TRUE` if the union is discriminated
  * Since: 2.80
  */
-size_t
-gi_union_info_get_discriminator_offset (GIUnionInfo *info)
+gboolean
+gi_union_info_get_discriminator_offset (GIUnionInfo *info,
+                                        size_t      *out_offset)
 {
   GIRealInfo *rinfo = (GIRealInfo *)info;
   UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset];
+  size_t discriminator_offset;
+
+  discriminator_offset = (blob->discriminated) ? blob->discriminator_offset : 0;
+
+  if (out_offset != NULL)
+    *out_offset = discriminator_offset;
 
-  return blob->discriminator_offset;
+  return blob->discriminated;
 }
 
 /**
@@ -172,7 +183,8 @@ gi_union_info_get_discriminator_offset (GIUnionInfo *info)
  *
  * Obtain the type information of the union discriminator.
  *
- * Returns: (transfer full): the [type@GIRepository.TypeInfo], free it with
+ * Returns: (transfer full) (nullable): the [type@GIRepository.TypeInfo], or
+ *   `NULL` if the union is not discriminated. Free it with
  *   [method@GIRepository.BaseInfo.unref] when done.
  * Since: 2.80
  */
@@ -180,6 +192,10 @@ GITypeInfo *
 gi_union_info_get_discriminator_type (GIUnionInfo *info)
 {
   GIRealInfo *rinfo = (GIRealInfo *)info;
+  UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset];
+
+  if (!blob->discriminated)
+    return NULL;
 
   return gi_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + 24);
 }
@@ -217,8 +233,8 @@ gi_union_info_get_discriminator (GIUnionInfo *info,
         + blob->n_functions * header->function_blob_size
         + n * header->constant_blob_size;
 
-      return (GIConstantInfo *) gi_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info,
-                                             rinfo->typelib, offset);
+      return (GIConstantInfo *) gi_base_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info,
+                                                  rinfo->typelib, offset);
     }
 
   return NULL;
index 6826c7b..d227362 100644 (file)
@@ -76,7 +76,8 @@ GI_AVAILABLE_IN_ALL
 gboolean         gi_union_info_is_discriminated         (GIUnionInfo *info);
 
 GI_AVAILABLE_IN_ALL
-size_t           gi_union_info_get_discriminator_offset (GIUnionInfo *info);
+gboolean         gi_union_info_get_discriminator_offset (GIUnionInfo *info,
+                                                         size_t      *out_offset);
 
 GI_AVAILABLE_IN_ALL
 GITypeInfo *     gi_union_info_get_discriminator_type   (GIUnionInfo *info);
index 0e61d59..728cca4 100644 (file)
@@ -60,8 +60,8 @@ gi_base_info_find_vfunc (GIRealInfo  *rinfo,
       const char *fname = (const char *)&rinfo->typelib->data[fblob->name];
 
       if (strcmp (name, fname) == 0)
-        return (GIVFuncInfo *) gi_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*) rinfo,
-                                            rinfo->typelib, offset);
+        return (GIVFuncInfo *) gi_base_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*) rinfo,
+                                                 rinfo->typelib, offset);
 
       offset += header->vfunc_blob_size;
     }
@@ -104,9 +104,6 @@ gi_vfunc_info_get_flags (GIVFuncInfo *info)
   if (blob->must_not_be_implemented)
     flags = flags | GI_VFUNC_MUST_NOT_OVERRIDE;
 
-  if (blob->throws)
-    flags = flags | GI_VFUNC_THROWS;
-
   return flags;
 }
 
diff --git a/girepository/inspector/inspector.c b/girepository/inspector/inspector.c
new file mode 100644 (file)
index 0000000..088bee4
--- /dev/null
@@ -0,0 +1,135 @@
+/* GObject introspection: typelib inspector
+ *
+ * Copyright (C) 2011-2016 Dominique Leuenberger <dimstar@opensuse.org>
+ * Copyright © 2016 Igor Gnatenko <ignatenko@redhat.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <girepository.h>
+#include <stdlib.h>
+#include <locale.h>
+
+static void
+print_shlibs (GIRepository *repository,
+              const gchar  *namespace)
+{
+  /* Finding the shared library we depend on (if any) */
+  const char * const *shlibs = gi_repository_get_shared_libraries (repository, namespace, NULL);
+  for (size_t i = 0; shlibs != NULL && shlibs[i] != NULL; i++)
+    g_print ("shlib: %s\n", shlibs[i]);
+}
+
+static void
+print_typelibs (GIRepository *repository,
+                const gchar  *namespace)
+{
+  guint i = 0;
+
+  /* Finding all the typelib-based Requires */
+  GStrv deps = gi_repository_get_dependencies (repository, namespace, NULL);
+  if (deps)
+    {
+      for (i = 0; deps[i]; i++)
+        g_print ("typelib: %s\n", deps[i]);
+      g_strfreev (deps);
+    }
+}
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  gint status = EXIT_SUCCESS;
+
+  GError *error = NULL;
+  GIRepository *repository = NULL;
+  GITypelib *typelib = NULL;
+
+  gchar *version = NULL;
+  gboolean opt_shlibs = FALSE;
+  gboolean opt_typelibs = FALSE;
+  GStrv namespaces = NULL;
+  const gchar *namespace = NULL;
+  const GOptionEntry options[] = {
+    { "typelib-version", 0, 0, G_OPTION_ARG_STRING, &version, "Typelib version to inspect", "VERSION" },
+    { "print-shlibs", 0, 0, G_OPTION_ARG_NONE, &opt_shlibs, "List the shared libraries the typelib requires", NULL },
+    { "print-typelibs", 0, 0, G_OPTION_ARG_NONE, &opt_typelibs, "List other typelibs the inspected typelib requires", NULL },
+    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &namespaces, "The typelib to inspect", "NAMESPACE" },
+    G_OPTION_ENTRY_NULL
+  };
+  GOptionContext *context = NULL;
+
+  setlocale (LC_ALL, "");
+
+  context = g_option_context_new ("- Inspect GI typelib");
+  g_option_context_add_main_entries (context, options, NULL);
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      status = EXIT_FAILURE;
+      g_printerr ("Failed to parse command line options: %s\n", error->message);
+      goto out;
+    }
+
+  if (!namespaces)
+    {
+      status = EXIT_FAILURE;
+      g_printerr ("Please specify at least one namespace\n");
+      goto out;
+    }
+
+  if (g_strv_length (namespaces) > 1)
+    {
+      status = EXIT_FAILURE;
+      g_printerr ("Please specify only one namespace\n");
+      goto out;
+    }
+  namespace = namespaces[0];
+
+  if (!opt_shlibs && !opt_typelibs)
+    {
+      status = EXIT_FAILURE;
+      g_printerr ("Please specify --print-shlibs, --print-typelibs or both.\n");
+      goto out;
+    }
+
+  repository = gi_repository_new ();
+  typelib = gi_repository_require (repository, namespace, version, 0, &error);
+  if (!typelib)
+    {
+      status = EXIT_FAILURE;
+      g_printerr ("Failed to load typelib: %s\n", error->message);
+      goto out;
+    }
+
+  if (opt_shlibs)
+    print_shlibs (repository, namespace);
+  if (opt_typelibs)
+    print_typelibs (repository, namespace);
+
+out:
+  g_option_context_free (context);
+  if (error)
+    g_error_free (error);
+  g_clear_object (&repository);
+  g_strfreev (namespaces);
+  g_free (version);
+
+  return status;
+}
diff --git a/girepository/inspector/meson.build b/girepository/inspector/meson.build
new file mode 100644 (file)
index 0000000..8948ced
--- /dev/null
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# SPDX-FileCopyrightText: 2024 GNOME Foundation
+
+custom_c_args = [
+  '-DG_LOG_DOMAIN="GLib-GirInspector"',
+]
+
+if cc.get_id() != 'msvc'
+  custom_c_args = cc.get_supported_arguments([
+    '-Wno-old-style-definition',
+    '-Wno-cast-align',
+    '-Wno-unused-parameter',
+    '-Wno-duplicated-branches',
+  ])
+endif
+
+giinspecttypelib = executable('gi-inspect-typelib', 'inspector.c',
+  dependencies: [
+    libgirepository_dep,
+    libgio_dep,
+  ],
+  install: true,
+  c_args: custom_c_args,
+)
diff --git a/girepository/introspection/meson.build b/girepository/introspection/meson.build
new file mode 100644 (file)
index 0000000..9405686
--- /dev/null
@@ -0,0 +1,320 @@
+
+gi_identifier_prefix = 'G'
+gi_symbol_prefix = 'g'
+
+gi_gen_shared_sources = [
+  # Required to compile gdump
+  gmodule_visibility_h,
+]
+
+gi_gen_env_variables = environment()
+
+if get_option('b_sanitize') != ''
+  gi_gen_env_variables.append(
+    'ASAN_OPTIONS', 'verify_asan_link_order=0', separator: ',')
+endif
+
+# GLib
+glib_gir_sources = [
+  gi_gen_shared_sources,
+  glibconfig_h,
+  gversionmacros_h,
+  glib_visibility_h,
+  glib_headers,
+  glib_deprecated_headers,
+  glib_sub_headers,
+  glib_enumtypes_h,
+  glib_types_h,
+  glib_deprecated_sources,
+  glib_sources,
+]
+
+# For API compatibility reasons, GLib-2.0.gir needs to contain the platform
+# specific APIs which are also present in the (newer) GLibUnix-2.0.gir and
+# GLibWin32-2.0.gir repositories.
+# See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3892#note_2001361
+# These can be dropped when GLib next breaks API (i.e. with GLib-3.0.gir).
+if host_system == 'windows'
+  glib_gir_sources += files('../../glib/gwin32.h')
+else
+  glib_gir_sources += files('../../glib/glib-unix.h')
+endif
+
+glib_gir = gnome.generate_gir(libglib,
+  sources: glib_gir_sources,
+  namespace: 'GLib',
+  nsversion: '2.0',
+  identifier_prefix: gi_identifier_prefix,
+  symbol_prefix: gi_symbol_prefix,
+  export_packages: 'glib-2.0',
+  header: 'glib.h',
+  install: true,
+  dependencies: [
+    libgobject_dep,
+  ],
+  env: gi_gen_env_variables,
+  extra_args: gir_args + [
+    '-DGLIB_COMPILATION',
+    '-DGETTEXT_PACKAGE="dummy"',
+    '--symbol-prefix=glib',
+    '--library-path=' + meson.current_build_dir(),
+    '--library=gobject-2.0',
+  ],
+)
+
+if host_system == 'windows'
+  glib_win32_gir = gnome.generate_gir(libglib,
+    sources: [
+      gi_gen_shared_sources,
+      glib_win32_headers,
+    ],
+    namespace: 'GLibWin32',
+    nsversion: '2.0',
+    identifier_prefix: gi_identifier_prefix,
+    symbol_prefix: gi_symbol_prefix,
+    export_packages: 'glib-2.0',
+    header: 'glib.h',
+    includes: [ glib_gir[0] ],
+    install: true,
+    dependencies: [
+      libgobject_dep,
+    ],
+    env: gi_gen_env_variables,
+    extra_args: gir_args + [
+      '-DGLIB_COMPILATION',
+      '-DGETTEXT_PACKAGE="dummy"',
+      '--symbol-prefix=glib',
+      '--symbol-prefix=g_win32',
+      '--identifier-prefix=GWin32',
+      '--library-path=' + meson.current_build_dir(),
+      '--library=gobject-2.0',
+    ],
+  )
+else
+  glib_unix_gir = gnome.generate_gir(libglib,
+    sources: [
+      gi_gen_shared_sources,
+      glib_unix_headers,
+    ],
+    namespace: 'GLibUnix',
+    nsversion: '2.0',
+    identifier_prefix: gi_identifier_prefix,
+    symbol_prefix: gi_symbol_prefix,
+    export_packages: 'glib-2.0',
+    header: 'glib.h',
+    includes: [ glib_gir[0] ],
+    install: true,
+    dependencies: [
+      libgobject_dep,
+    ],
+    env: gi_gen_env_variables,
+    extra_args: gir_args + [
+      '-DGLIB_COMPILATION',
+      '-DGETTEXT_PACKAGE="dummy"',
+      '--symbol-prefix=glib',
+      '--symbol-prefix=g_unix',
+      '--identifier-prefix=GUnix',
+      '--library-path=' + meson.current_build_dir(),
+      '--library=gobject-2.0',
+      '--c-include=glib-unix.h',
+    ],
+  )
+endif
+
+# GObject
+gobject_gir = gnome.generate_gir(libgobject,
+  sources: [
+    gi_gen_shared_sources,
+    gobject_visibility_h,
+    gobject_install_headers,
+    gobject_sources,
+  ],
+  namespace: 'GObject',
+  nsversion: '2.0',
+  identifier_prefix: gi_identifier_prefix,
+  symbol_prefix: gi_symbol_prefix,
+  export_packages: 'gobject-2.0',
+  header: 'glib-object.h',
+  includes: [ glib_gir[0] ],
+  install: true,
+  env: gi_gen_env_variables,
+  extra_args: gir_args + [
+      '-DGOBJECT_COMPILATION',
+      '--symbol-prefix=gobject',
+  ],
+)
+
+# GModule
+gmodule_gir = gnome.generate_gir(libgmodule,
+  sources: [
+    gi_gen_shared_sources,
+    gmoduleconf_h,
+    gmodule_h,
+    gmodule_c,
+    gmodule_deprecated_c,
+    gmodule_visibility_h,
+  ],
+  namespace: 'GModule',
+  nsversion: '2.0',
+  identifier_prefix: gi_identifier_prefix,
+  symbol_prefix: gi_symbol_prefix,
+  export_packages: 'gmodule-2.0',
+  header: 'gmodule.h',
+  includes: [ glib_gir[0] ],
+  install: true,
+  dependencies: [
+    libglib_dep,
+  ],
+  env: gi_gen_env_variables,
+  extra_args: gir_args + [
+      '-DGMODULE_COMPILATION',
+      '-DGETTEXT_PACKAGE="dummy"',
+      '--symbol-prefix=gmodule',
+  ],
+)
+
+# Gio
+gio_gir_sources = [
+  gi_gen_shared_sources,
+  gio_visibility_h,
+  gioenumtypes_h,
+  gnetworking_h,
+  gio_headers,
+  gio_base_sources,
+  application_sources,
+  gdbus_sources,
+  contenttype_sources,
+  settings_sources,
+]
+gio_gir_packages = [ 'gio-2.0' ]
+gio_gir_args = [
+  '-DGIO_COMPILATION',
+  '-DG_SETTINGS_ENABLE_BACKEND',
+  '--symbol-prefix=gio',
+]
+
+# For API compatibility reasons, Gio-2.0.gir needs to contain the platform
+# specific APIs which are also present in the (newer) GioUnix-2.0.gir and
+# GioWin32-2.0.gir repositories.
+# See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3892#note_2001361
+# These can be dropped when GIO next breaks API (i.e. with Gio-3.0.gir).
+if host_system == 'windows'
+  gio_gir_sources += [ gio_win32_include_headers, win32_sources ]
+  foreach h: gio_win32_include_headers
+    gio_gir_args += '--c-include=@0@'.format(h)
+  endforeach
+  gio_gir_packages += 'gio-win32-2.0'
+  gio_gir_args += '--pkg=gio-win32-2.0'
+else
+  gio_gir_sources += [ gio_unix_include_headers, unix_sources ]
+  foreach h: gio_unix_include_headers
+    gio_gir_args += '--c-include=@0@'.format(h)
+  endforeach
+  gio_gir_packages += 'gio-unix-2.0'
+  gio_gir_args += '--pkg=gio-unix-2.0'
+endif
+
+gio_gir = gnome.generate_gir(libgio,
+  sources: gio_gir_sources,
+  namespace: 'Gio',
+  nsversion: '2.0',
+  identifier_prefix: gi_identifier_prefix,
+  symbol_prefix: gi_symbol_prefix,
+  export_packages: gio_gir_packages,
+  header: 'gio/gio.h',
+  includes: [ glib_gir[0], gmodule_gir[0], gobject_gir[0] ],
+  install: true,
+  dependencies: [
+    libglib_dep,
+    libgobject_dep,
+    libgmodule_dep,
+  ],
+  env: gi_gen_env_variables,
+  extra_args: gir_args + gio_gir_args,
+)
+
+if host_system == 'windows'
+  gio_win32_gir_c_includes = []
+  foreach h: gio_win32_include_headers
+    gio_win32_gir_c_includes += '--c-include=@0@'.format(h)
+  endforeach
+
+  gio_win32_gir = gnome.generate_gir(libgio,
+    sources: gio_win32_include_headers + win32_sources,
+    namespace: 'GioWin32',
+    nsversion: '2.0',
+    identifier_prefix: gi_identifier_prefix,
+    symbol_prefix: gi_symbol_prefix,
+    export_packages: [ 'gio-win32-2.0' ],
+    header: 'gio/gio.h',
+    includes: [ glib_gir[0], gmodule_gir[0], gobject_gir[0], gio_gir[0] ],
+    install: true,
+    dependencies: [
+      libglib_dep,
+      libgobject_dep,
+      libgmodule_dep,
+    ],
+    env: gi_gen_env_variables,
+    extra_args: gir_args + gio_gir_args + gio_win32_gir_c_includes + [
+      '--pkg=gio-win32-2.0',
+      '--symbol-prefix=g_win32',
+      '--identifier-prefix=GWin32'
+    ],
+  )
+else
+  gio_unix_gir_c_includes = []
+  foreach h: gio_unix_include_headers
+    gio_unix_gir_c_includes += '--c-include=@0@'.format(h)
+  endforeach
+
+  gio_unix_gir = gnome.generate_gir(libgio,
+    sources: gio_unix_include_headers + unix_sources,
+    namespace: 'GioUnix',
+    nsversion: '2.0',
+    identifier_prefix: gi_identifier_prefix,
+    symbol_prefix: gi_symbol_prefix,
+    export_packages: [ 'gio-unix-2.0' ],
+    header: 'gio/gio.h',
+    includes: [ glib_gir[0], gmodule_gir[0], gobject_gir[0], gio_gir[0] ],
+    install: true,
+    dependencies: [
+      libglib_dep,
+      libgobject_dep,
+      libgmodule_dep,
+    ],
+    env: gi_gen_env_variables,
+    extra_args: gir_args + gio_gir_args + gio_unix_gir_c_includes + [
+      '--pkg=gio-unix-2.0',
+      '--symbol-prefix=g_unix',
+      '--identifier-prefix=GUnix'
+    ],
+  )
+endif
+
+# GIRepository
+libgirepository_gir_sources = [
+  gi_visibility_h,
+  girepo_headers,
+  girepo_sources,
+]
+libgirepository_gir_packages = [ 'girepository-2.0' ]
+libgirepository_gir_args = [
+  '-DGI_COMPILATION',
+  '--symbol-prefix=gi',
+  '--identifier-prefix=GI',
+]
+
+girepository_gir = gnome.generate_gir(libgirepository,
+  sources: libgirepository_gir_sources,
+  namespace: 'GIRepository',
+  nsversion: '3.0',
+  identifier_prefix: 'GI',
+  symbol_prefix: 'gi',
+  export_packages: libgirepository_gir_packages,
+  header: 'girepository/girepository.h',
+  includes: [ glib_gir[0], gmodule_gir[0], gobject_gir[0], gio_gir[0] ],
+  install: true,
+  dependencies: [ libglib_dep, libgobject_dep, libgmodule_dep, libgio_dep ],
+  extra_args: gir_args + libgirepository_gir_args,
+)
+
index 6c0a1f8..d64a64a 100644 (file)
@@ -44,7 +44,6 @@ gi_visibility_h = custom_target(
 girepo_headers = files(
   'giarginfo.h',
   'gibaseinfo.h',
-  'giboxedinfo.h',
   'gicallableinfo.h',
   'gicallbackinfo.h',
   'giconstantinfo.h',
@@ -148,7 +147,6 @@ girepo_sources = files(
   'gdump.c',
   'giarginfo.c',
   'gibaseinfo.c',
-  'giboxedinfo.c',
   'gicallableinfo.c',
   'gicallbackinfo.c',
   'giconstantinfo.c',
@@ -207,6 +205,13 @@ libgirepository_dep = declare_dependency(
   dependencies: [libglib_dep, libgobject_dep, libgio_dep, libgmodule_dep],
   sources: [gi_visibility_h],
   include_directories: [girepoinc],
+  variables: {
+    # Export the path for the built GLib-2.0.typelib (etc.) for when GLib is
+    # used as a subproject. The variable names match those in
+    # pkgconfig_variables below.
+    'girdir': meson.current_build_dir() / 'introspection',
+    'typelibdir': meson.current_build_dir() / 'introspection',
+  },
 )
 
 executable('gi-dump-types',
@@ -235,33 +240,13 @@ pkg.generate(libgirepository,
 )
 
 if enable_gir
-  libgirepository_gir_sources = [
-    gi_visibility_h,
-    girepo_headers,
-    girepo_sources,
-  ]
-  libgirepository_gir_packages = [ 'girepository-2.0' ]
-  libgirepository_gir_args = [
-    '-DGI_COMPILATION',
-    '--symbol-prefix=gi',
-    '--identifier-prefix=GI',
-  ]
-
-  girepository_gir = gnome.generate_gir(libgirepository,
-    sources: libgirepository_gir_sources,
-    namespace: 'GIRepository',
-    nsversion: '3.0',
-    identifier_prefix: 'GI',
-    symbol_prefix: 'gi',
-    export_packages: libgirepository_gir_packages,
-    header: 'girepository/girepository.h',
-    includes: [ glib_gir[0], gmodule_gir[0], gobject_gir[0], gio_gir[0] ],
-    install: true,
-    dependencies: [ libglib_dep, libgobject_dep, libgmodule_dep, libgio_dep ],
-    extra_args: gir_args + libgirepository_gir_args,
-  )
+  subdir('introspection')
 endif
 
 if build_tests
   subdir('tests')
 endif
+
+subdir('compiler')
+subdir('decompiler')
+subdir('inspector')
\ No newline at end of file
diff --git a/girepository/tests/function-info.c b/girepository/tests/function-info.c
new file mode 100644 (file)
index 0000000..9645cf4
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 Philip Chimento
+ * Copyright 2024 GNOME Foundation, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Philip Withnall <pwithnall@gnome.org>
+ */
+
+#include "girepository.h"
+#include "girffi.h"
+#include "glib.h"
+#include "test-common.h"
+
+static void
+test_function_info_invoker (RepositoryFixture *fx,
+                            const void *unused)
+{
+  GIFunctionInfo *function_info = NULL;
+  GIFunctionInvoker invoker;
+  GError *local_error = NULL;
+
+  g_test_summary ("Test preparing a function invoker");
+
+  function_info = GI_FUNCTION_INFO (gi_repository_find_by_name (fx->repository, "GLib", "get_locale_variants"));
+  g_assert_nonnull (function_info);
+
+  gi_function_info_prep_invoker (function_info, &invoker, &local_error);
+  g_assert_no_error (local_error);
+
+  gi_function_invoker_clear (&invoker);
+  g_clear_pointer (&function_info, gi_base_info_unref);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  repository_init (&argc, &argv);
+
+  ADD_REPOSITORY_TEST ("/function-info/invoker", test_function_info_invoker, &typelib_load_spec_glib);
+
+  return g_test_run ();
+}
index df22465..7814ca0 100644 (file)
@@ -1,21 +1,44 @@
-girepository_tests = {}
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# SPDX-FileCopyrightText: 2024 GNOME Foundation
 
-# Some GIR files are needed to test against
+girepository_tests = {
+  'cmph-bdz': {
+    'dependencies': [cmph_dep],
+  },
+  'gthash' : {
+    'dependencies': [girepo_gthash_dep],
+  },
+}
+
+# Some tests require GIR files to have been generated
 if enable_gir
   girepository_tests += {
-    'cmph-bdz': {
-      'dependencies': [cmph_dep],
+    'function-info' : {
+      'dependencies': [libffi_dep],
+      'depends': [glib_gir],
     },
-    'gthash' : {
-      'dependencies': [girepo_gthash_dep],
+    'object-info' : {
+      'depends': [gio_gir],
+    },
+    'registered-type-info' : {
+      'depends': [gobject_gir],
     },
     'repository' : {
-      'depends': [glib_gir, gobject_gir],
+      'depends': [glib_gir, gio_gir, gobject_gir],
     },
     'repository-search-paths' : {
       'c_args': '-DGOBJECT_INTROSPECTION_LIBDIR="@0@"'.format(glib_libdir),
       'depends': [glib_gir],
     },
+    'struct-info' : {
+      'depends': [gobject_gir],
+    },
+    'throws' : {
+      'depends': [glib_gir, gio_gir],
+    },
+    'union-info' : {
+      'depends': [glib_gir],
+    },
   }
 endif
 
@@ -54,7 +77,7 @@ foreach test_name, extra_args : girepository_tests
     )
   endif
 
-  exe = executable(test_name, source,
+  exe = executable(test_name, source, 'test-common.c',
     c_args: test_cargs + extra_args.get('c_args', []),
     cpp_args: test_cpp_args + extra_args.get('cpp_args', []),
     link_args: extra_args.get('link_args', []),
diff --git a/girepository/tests/object-info.c b/girepository/tests/object-info.c
new file mode 100644 (file)
index 0000000..7245dae
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2024 Philip Chimento <philip.chimento@gmail.com>
+ * Copyright 2024 GNOME Foundation
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "girepository.h"
+#include "test-common.h"
+
+static void
+test_object_info_find_method_using_interfaces (RepositoryFixture *fx,
+                                               const void        *unused)
+{
+  GIObjectInfo *class_info = NULL;
+  GIFunctionInfo *method_info = NULL;
+  GIBaseInfo *declarer_info = NULL;
+
+  class_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "Gio", "DBusProxy"));
+  g_assert_nonnull (class_info);
+
+  method_info = gi_object_info_find_method_using_interfaces (class_info, "init", &declarer_info);
+
+  g_assert_nonnull (declarer_info);
+  g_assert_cmpstr (gi_base_info_get_namespace (declarer_info), ==, "Gio");
+  g_assert_cmpstr (gi_base_info_get_name (declarer_info), ==, "Initable");
+  g_assert_true (GI_IS_INTERFACE_INFO (declarer_info));
+
+  g_clear_pointer (&class_info, gi_base_info_unref);
+  g_clear_pointer (&method_info, gi_base_info_unref);
+  g_clear_pointer (&declarer_info, gi_base_info_unref);
+}
+
+static void
+test_object_info_find_vfunc_using_interfaces (RepositoryFixture *fx,
+                                              const void        *unused)
+{
+  GIObjectInfo *class_info = NULL;
+  GIVFuncInfo *vfunc_info = NULL;
+  GIBaseInfo *declarer_info = NULL;
+
+  class_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "Gio", "Application"));
+  g_assert_nonnull (class_info);
+
+  vfunc_info = gi_object_info_find_vfunc_using_interfaces (class_info, "after_emit", &declarer_info);
+
+  g_assert_nonnull (declarer_info);
+  g_assert_cmpstr (gi_base_info_get_namespace (declarer_info), ==, "Gio");
+  g_assert_cmpstr (gi_base_info_get_name (declarer_info), ==, "Application");
+  g_assert_true (GI_IS_OBJECT_INFO (declarer_info));
+
+  g_clear_pointer (&class_info, gi_base_info_unref);
+  g_clear_pointer (&vfunc_info, gi_base_info_unref);
+  g_clear_pointer (&declarer_info, gi_base_info_unref);
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+  repository_init (&argc, &argv);
+
+  ADD_REPOSITORY_TEST ("/object-info/find-method-using-interfaces", test_object_info_find_method_using_interfaces, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/object-info/find-vfunc-using-interfaces", test_object_info_find_vfunc_using_interfaces, &typelib_load_spec_gio);
+
+  return g_test_run ();
+}
diff --git a/girepository/tests/registered-type-info.c b/girepository/tests/registered-type-info.c
new file mode 100644 (file)
index 0000000..16dbf82
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2024 GNOME Foundation
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "girepository.h"
+#include "test-common.h"
+
+static void
+test_boxed (RepositoryFixture *fx,
+            const void        *unused)
+{
+  const struct
+    {
+      const char *name;
+      GType expect_info_type;
+      gboolean expect_nonnull_gtype_info;
+      gboolean expect_is_gtype_struct;
+      gboolean expect_boxed;
+    }
+  types[] =
+    {
+      {
+        /* POD struct */
+        .name = "CClosure",
+        .expect_info_type = GI_TYPE_STRUCT_INFO,
+        .expect_nonnull_gtype_info = FALSE,
+        .expect_is_gtype_struct = FALSE,
+        .expect_boxed = FALSE,
+      },
+      {
+        /* POD union */
+        .name = "TypeCValue",
+        .expect_info_type = GI_TYPE_UNION_INFO,
+        .expect_nonnull_gtype_info = FALSE,
+        .expect_is_gtype_struct = FALSE,
+        .expect_boxed = FALSE,
+      },
+      {
+        /* struct for a different non-boxed GType */
+        .name = "InitiallyUnownedClass",
+        .expect_info_type = GI_TYPE_STRUCT_INFO,
+        .expect_nonnull_gtype_info = FALSE,
+        .expect_is_gtype_struct = TRUE,
+        .expect_boxed = FALSE,
+      },
+      {
+        /* boxed struct */
+        .name = "BookmarkFile",
+        .expect_info_type = GI_TYPE_STRUCT_INFO,
+        .expect_nonnull_gtype_info = TRUE,
+        .expect_is_gtype_struct = FALSE,
+        .expect_boxed = TRUE,
+      },
+      {
+        /* boxed struct */
+        .name = "Closure",
+        .expect_info_type = GI_TYPE_STRUCT_INFO,
+        .expect_nonnull_gtype_info = TRUE,
+        .expect_is_gtype_struct = FALSE,
+        .expect_boxed = TRUE,
+      },
+      {
+        /* non-boxed GType */
+        .name = "Object",
+        .expect_info_type = GI_TYPE_OBJECT_INFO,
+        .expect_nonnull_gtype_info = TRUE,
+        .expect_is_gtype_struct = FALSE,
+        .expect_boxed = FALSE,
+      },
+    };
+
+  g_test_summary ("Test various boxed and non-boxed types for GIRegisteredTypeInfo");
+
+  for (size_t i = 0; i < G_N_ELEMENTS (types); i++)
+    {
+      GIRegisteredTypeInfo *type_info = GI_REGISTERED_TYPE_INFO (gi_repository_find_by_name (fx->repository, "GObject", types[i].name));
+      g_assert_nonnull (type_info);
+
+      g_test_message ("Expecting %s to %s", types[i].name, types[i].expect_boxed ? "be boxed" : "not be boxed");
+
+      g_assert_cmpuint (G_TYPE_FROM_INSTANCE (type_info), ==, types[i].expect_info_type);
+
+      if (types[i].expect_nonnull_gtype_info)
+        {
+          g_assert_nonnull (gi_registered_type_info_get_type_name (type_info));
+          g_assert_nonnull (gi_registered_type_info_get_type_init_function_name (type_info));
+        }
+      else
+        {
+          g_assert_null (gi_registered_type_info_get_type_name (type_info));
+          g_assert_null (gi_registered_type_info_get_type_init_function_name (type_info));
+        }
+
+      if (GI_IS_STRUCT_INFO (type_info))
+        {
+          if (types[i].expect_is_gtype_struct)
+            g_assert_true (gi_struct_info_is_gtype_struct (GI_STRUCT_INFO (type_info)));
+          else
+            g_assert_false (gi_struct_info_is_gtype_struct (GI_STRUCT_INFO (type_info)));
+        }
+
+      if (types[i].expect_boxed)
+        g_assert_true (gi_registered_type_info_is_boxed (type_info));
+      else
+        g_assert_false (gi_registered_type_info_is_boxed (type_info));
+
+      g_clear_pointer (&type_info, gi_base_info_unref);
+    }
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+  repository_init (&argc, &argv);
+
+  ADD_REPOSITORY_TEST ("/registered-type-info/boxed", test_boxed, &typelib_load_spec_gobject);
+
+  return g_test_run ();
+}
index 55eebef..7bfe14d 100644 (file)
 #include "glib.h"
 #include "girepository.h"
 
-/* Keep this test first, not to add search paths to other tests */
-static void
-test_repository_search_paths_unset (void)
-{
-  const char * const *search_paths;
-  size_t n_search_paths;
-
-  search_paths = gi_repository_get_search_path (&n_search_paths);
-  g_assert_nonnull (search_paths);
-  g_assert_cmpstrv (search_paths, ((char *[]){NULL}));
-  g_assert_cmpuint (n_search_paths, ==, 0);
-
-  search_paths = gi_repository_get_search_path (NULL);
-  g_assert_cmpuint (g_strv_length ((char **) search_paths), ==, 0);
-}
-
 static void
 test_repository_search_paths_default (void)
 {
   const char * const *search_paths;
   size_t n_search_paths;
+  GIRepository *repository = NULL;
 
-  search_paths = gi_repository_get_search_path (&n_search_paths);
-  g_assert_nonnull (search_paths);
-
-  /* Init default paths */
-  g_assert_nonnull (gi_repository_get_default ());
+  repository = gi_repository_new ();
 
-  search_paths = gi_repository_get_search_path (&n_search_paths);
+  search_paths = gi_repository_get_search_path (repository, &n_search_paths);
   g_assert_nonnull (search_paths);
   g_assert_cmpuint (g_strv_length ((char **) search_paths), ==, 2);
 
@@ -61,6 +42,8 @@ test_repository_search_paths_default (void)
   g_assert_cmpstr (search_paths[1], ==, expected_path);
   g_clear_pointer (&expected_path, g_free);
 #endif
+
+  g_clear_object (&repository);
 }
 
 static void
@@ -68,9 +51,12 @@ test_repository_search_paths_prepend (void)
 {
   const char * const *search_paths;
   size_t n_search_paths;
+  GIRepository *repository = NULL;
 
-  gi_repository_prepend_search_path (g_test_get_dir (G_TEST_BUILT));
-  search_paths = gi_repository_get_search_path (&n_search_paths);
+  repository = gi_repository_new ();
+
+  gi_repository_prepend_search_path (repository, g_test_get_dir (G_TEST_BUILT));
+  search_paths = gi_repository_get_search_path (repository, &n_search_paths);
   g_assert_nonnull (search_paths);
   g_assert_cmpuint (g_strv_length ((char **) search_paths), ==, 3);
 
@@ -83,8 +69,8 @@ test_repository_search_paths_prepend (void)
   g_clear_pointer (&expected_path, g_free);
 #endif
 
-  gi_repository_prepend_search_path (g_test_get_dir (G_TEST_DIST));
-  search_paths = gi_repository_get_search_path (&n_search_paths);
+  gi_repository_prepend_search_path (repository, g_test_get_dir (G_TEST_DIST));
+  search_paths = gi_repository_get_search_path (repository, &n_search_paths);
   g_assert_nonnull (search_paths);
   g_assert_cmpuint (g_strv_length ((char **) search_paths), ==, 4);
 
@@ -97,6 +83,51 @@ test_repository_search_paths_prepend (void)
   g_assert_cmpstr (search_paths[3], ==, expected_path);
   g_clear_pointer (&expected_path, g_free);
 #endif
+
+  g_clear_object (&repository);
+}
+
+static void
+test_repository_library_paths_default (void)
+{
+  const char * const *library_paths;
+  size_t n_library_paths;
+  GIRepository *repository = NULL;
+
+  repository = gi_repository_new ();
+
+  library_paths = gi_repository_get_library_path (repository, &n_library_paths);
+  g_assert_nonnull (library_paths);
+  g_assert_cmpuint (g_strv_length ((char **) library_paths), ==, 0);
+
+  g_clear_object (&repository);
+}
+
+static void
+test_repository_library_paths_prepend (void)
+{
+  const char * const *library_paths;
+  size_t n_library_paths;
+  GIRepository *repository = NULL;
+
+  repository = gi_repository_new ();
+
+  gi_repository_prepend_library_path (repository, g_test_get_dir (G_TEST_BUILT));
+  library_paths = gi_repository_get_library_path (repository, &n_library_paths);
+  g_assert_nonnull (library_paths);
+  g_assert_cmpuint (g_strv_length ((char **) library_paths), ==, 1);
+
+  g_assert_cmpstr (library_paths[0], ==, g_test_get_dir (G_TEST_BUILT));
+
+  gi_repository_prepend_library_path (repository, g_test_get_dir (G_TEST_DIST));
+  library_paths = gi_repository_get_library_path (repository, &n_library_paths);
+  g_assert_nonnull (library_paths);
+  g_assert_cmpuint (g_strv_length ((char **) library_paths), ==, 2);
+
+  g_assert_cmpstr (library_paths[0], ==, g_test_get_dir (G_TEST_DIST));
+  g_assert_cmpstr (library_paths[1], ==, g_test_get_dir (G_TEST_BUILT));
+
+  g_clear_object (&repository);
 }
 
 int
@@ -109,9 +140,10 @@ main (int   argc,
   g_setenv ("GI_TYPELIB_PATH", g_get_tmp_dir (), TRUE);
   g_setenv ("GI_GIR_PATH", g_get_user_cache_dir (), TRUE);
 
-  g_test_add_func ("/repository-search-paths/unset", test_repository_search_paths_unset);
-  g_test_add_func ("/repository-search-paths/default", test_repository_search_paths_default);
-  g_test_add_func ("/repository-search-paths/prepend", test_repository_search_paths_prepend);
+  g_test_add_func ("/repository/search-paths/default", test_repository_search_paths_default);
+  g_test_add_func ("/repository/search-paths/prepend", test_repository_search_paths_prepend);
+  g_test_add_func ("/repository/library-paths/default", test_repository_library_paths_default);
+  g_test_add_func ("/repository/library-paths/prepend", test_repository_library_paths_prepend);
 
   return g_test_run ();
 }
index d1ac865..5067263 100644 (file)
@@ -1,4 +1,13 @@
 /*
+ * Copyright 2008-2011 Colin Walters <walters@verbum.org>
+ * Copyright 2011 Laszlo Pandy <lpandy@src.gnome.org>
+ * Copyright 2011 Torsten Schönfeld <kaffeetisch@gmx.de>
+ * Copyright 2011, 2012 Pavel Holejsovsky <pavel.holejsovsky@gmail.com>
+ * Copyright 2013 Martin Pitt <martinpitt@gnome.org>
+ * Copyright 2014 Giovanni Campagna <gcampagna@src.gnome.org>
+ * Copyright 2018 Christoph Reiter
+ * Copyright 2019, 2024 Philip Chimento <philip.chimento@gmail.com>
+ * Copyright 2022 Emmanuele Bassi <ebassi@gnome.org>
  * Copyright 2023 GNOME Foundation, Inc.
  *
  * SPDX-License-Identifier: LGPL-2.1-or-later
  * Author: Philip Withnall <pwithnall@gnome.org>
  */
 
-#include "glib.h"
-#include "girepository.h"
-
-static GIRepository *
-load_typelib_from_builddir (const char *name,
-                            const char *version)
-{
-  GIRepository *repository;
-  char *gobject_typelib_dir = NULL;
-  GITypelib *typelib = NULL;
-  GError *local_error = NULL;
-
-  gobject_typelib_dir = g_test_build_filename (G_TEST_BUILT, "..", "..", "introspection", NULL);
-  g_test_message ("Using GI_TYPELIB_DIR = %s", gobject_typelib_dir);
-  gi_repository_prepend_search_path (gobject_typelib_dir);
-  g_free (gobject_typelib_dir);
-
-  repository = gi_repository_new ();
-  g_assert_nonnull (repository);
-
-  typelib = gi_repository_require (repository, name, version, 0, &local_error);
-  g_assert_no_error (local_error);
-  g_assert_nonnull (typelib);
+#include "config.h"
 
-  return g_steal_pointer (&repository);
-}
+#include "gio.h"
+#include "girepository.h"
+#include "glib.h"
+#include "test-common.h"
 
 static void
-test_repository_basic (void)
+test_repository_basic (RepositoryFixture *fx,
+                       const void *unused)
 {
-  GIRepository *repository;
-  char *gobject_typelib_dir = NULL;
   const char * const * search_paths;
-  GITypelib *typelib = NULL;
   char **namespaces = NULL;
+  size_t n_namespaces;
   const char *expected_namespaces[] = { "GLib", NULL };
-  GError *local_error = NULL;
   char **versions;
   size_t n_versions;
+  const char *prefix = NULL;
 
   g_test_summary ("Test basic opening of a repository and requiring a typelib");
 
-  gobject_typelib_dir = g_test_build_filename (G_TEST_BUILT, "..", "..", "introspection", NULL);
-  g_test_message ("Using GI_TYPELIB_DIR = %s", gobject_typelib_dir);
-  gi_repository_prepend_search_path (gobject_typelib_dir);
-
-  repository = gi_repository_new ();
-  g_assert_nonnull (repository);
-
-  versions = gi_repository_enumerate_versions (repository, "SomeInvalidNamespace", &n_versions);
+  versions = gi_repository_enumerate_versions (fx->repository, "SomeInvalidNamespace", &n_versions);
   g_assert_nonnull (versions);
   g_assert_cmpstrv (versions, ((char *[]){NULL}));
   g_assert_cmpuint (n_versions, ==, 0);
   g_clear_pointer (&versions, g_strfreev);
 
-  versions = gi_repository_enumerate_versions (repository, "GLib", NULL);
+  versions = gi_repository_enumerate_versions (fx->repository, "GLib", NULL);
   g_assert_nonnull (versions);
   g_assert_cmpstrv (versions, ((char *[]){"2.0", NULL}));
   g_clear_pointer (&versions, g_strfreev);
 
-  search_paths = gi_repository_get_search_path (NULL);
+  search_paths = gi_repository_get_search_path (fx->repository, NULL);
   g_assert_nonnull (search_paths);
   g_assert_cmpuint (g_strv_length ((char **) search_paths), >, 0);
-  g_assert_cmpstr (search_paths[0], ==, gobject_typelib_dir);
-
-  typelib = gi_repository_require (repository, "GLib", "2.0", 0, &local_error);
-  g_assert_no_error (local_error);
-  g_assert_nonnull (typelib);
+  g_assert_cmpstr (search_paths[0], ==, fx->gobject_typelib_dir);
 
-  namespaces = gi_repository_get_loaded_namespaces (repository);
+  namespaces = gi_repository_get_loaded_namespaces (fx->repository, &n_namespaces);
   g_assert_cmpstrv (namespaces, expected_namespaces);
+  g_assert_cmpuint (n_namespaces, ==, g_strv_length ((char **) expected_namespaces));
   g_strfreev (namespaces);
 
-  g_free (gobject_typelib_dir);
-  g_clear_object (&repository);
+  prefix = gi_repository_get_c_prefix (fx->repository, "GLib");
+  g_assert_nonnull (prefix);
+  g_assert_cmpstr (prefix, ==, "G");
 }
 
 static void
-test_repository_info (void)
+test_repository_info (RepositoryFixture *fx,
+                      const void *unused)
 {
-  GIRepository *repository;
-  char *gobject_typelib_dir = NULL;
-  GITypelib *typelib = NULL;
-  GIObjectInfo *object_info = NULL;
+  GIBaseInfo *not_found_info = NULL;
+  GIObjectInfo *object_info = NULL, *object_info_by_gtype = NULL;
   GISignalInfo *signal_info = NULL;
   GIFunctionInfo *method_info = NULL;
-  GError *local_error = NULL;
+  GType gtype;
 
   g_test_summary ("Test retrieving some basic info blobs from a typelib");
 
-  gobject_typelib_dir = g_test_build_filename (G_TEST_BUILT, "..", "..", "introspection", NULL);
-  g_test_message ("Using GI_TYPELIB_DIR = %s", gobject_typelib_dir);
-  gi_repository_prepend_search_path (gobject_typelib_dir);
-  g_free (gobject_typelib_dir);
+  not_found_info = gi_repository_find_by_name (fx->repository, "GObject", "ThisDoesNotExist");
+  g_assert_null (not_found_info);
 
-  repository = gi_repository_new ();
-  g_assert_nonnull (repository);
+  object_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "Object"));
+  g_assert_nonnull (object_info);
+  g_assert_true (GI_IS_OBJECT_INFO (object_info));
+  g_assert_true (GI_IS_REGISTERED_TYPE_INFO (object_info));
+  g_assert_true (GI_IS_BASE_INFO (object_info));
 
-  typelib = gi_repository_require (repository, "GObject", "2.0", 0, &local_error);
-  g_assert_no_error (local_error);
-  g_assert_nonnull (typelib);
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (object_info)), ==, "Object");
+  g_assert_cmpstr (gi_base_info_get_namespace (GI_BASE_INFO (object_info)), ==, "GObject");
 
-  object_info = (GIObjectInfo *) gi_repository_find_by_name (repository, "GObject", "Object");
-  g_assert_nonnull (object_info);
+  gtype = gi_registered_type_info_get_g_type (GI_REGISTERED_TYPE_INFO (object_info));
+  g_assert_true (g_type_is_a (gtype, G_TYPE_OBJECT));
 
-  g_assert_cmpint (gi_base_info_get_info_type ((GIBaseInfo *) object_info), ==, GI_INFO_TYPE_OBJECT);
-  g_assert_cmpstr (gi_base_info_get_name ((GIBaseInfo *) object_info), ==, "Object");
-  g_assert_cmpstr (gi_base_info_get_namespace ((GIBaseInfo *) object_info), ==, "GObject");
+  object_info_by_gtype = GI_OBJECT_INFO (gi_repository_find_by_gtype (fx->repository, G_TYPE_OBJECT));
+  g_assert_nonnull (object_info);
 
   signal_info = gi_object_info_find_signal (object_info, "notify");
   g_assert_nonnull (signal_info);
+  g_assert_true (GI_IS_SIGNAL_INFO (signal_info));
+  g_assert_true (GI_IS_CALLABLE_INFO (signal_info));
+  g_assert_true (GI_IS_BASE_INFO (signal_info));
 
   g_assert_cmpint (gi_signal_info_get_flags (signal_info), ==,
                    G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION);
@@ -138,72 +118,62 @@ test_repository_info (void)
 
   method_info = gi_object_info_find_method (object_info, "get_property");
   g_assert_nonnull (method_info);
-  g_assert_true (gi_callable_info_is_method ((GICallableInfo *) method_info));
-  g_assert_cmpuint (gi_callable_info_get_n_args ((GICallableInfo *) method_info), ==, 2);
+  g_assert_true (GI_IS_FUNCTION_INFO (method_info));
+  g_assert_true (GI_IS_CALLABLE_INFO (method_info));
+  g_assert_true (GI_IS_BASE_INFO (method_info));
+
+  g_assert_true (gi_callable_info_is_method (GI_CALLABLE_INFO (method_info)));
+  g_assert_cmpuint (gi_callable_info_get_n_args (GI_CALLABLE_INFO (method_info)), ==, 2);
   g_clear_pointer (&method_info, gi_base_info_unref);
 
   method_info = gi_object_info_get_method (object_info,
                                            gi_object_info_get_n_methods (object_info) - 1);
-  g_assert_true (gi_callable_info_is_method ((GICallableInfo *) method_info));
-  g_assert_cmpuint (gi_callable_info_get_n_args ((GICallableInfo *) method_info), >, 0);
+  g_assert_true (gi_callable_info_is_method (GI_CALLABLE_INFO (method_info)));
+  g_assert_cmpuint (gi_callable_info_get_n_args (GI_CALLABLE_INFO (method_info)), >, 0);
   g_clear_pointer (&method_info, gi_base_info_unref);
 
   gi_base_info_unref (signal_info);
   gi_base_info_unref (object_info);
-  g_clear_object (&repository);
+  gi_base_info_unref (object_info_by_gtype);
 }
 
 static void
-test_repository_dependencies (void)
+test_repository_dependencies (RepositoryFixture *fx,
+                              const void *unused)
 {
-  GIRepository *repository;
-  GITypelib *typelib;
   GError *error = NULL;
-  char *gobject_typelib_dir = NULL;
   char **dependencies;
+  size_t n_dependencies;
 
   g_test_summary ("Test ensures namespace dependencies are correctly exposed");
 
-  gobject_typelib_dir = g_test_build_filename (G_TEST_BUILT, "..", "..", "gobject", NULL);
-  g_test_message ("Using GI_TYPELIB_DIR = %s", gobject_typelib_dir);
-  gi_repository_prepend_search_path (gobject_typelib_dir);
-  g_free (gobject_typelib_dir);
-
-  repository = gi_repository_new ();
-  g_assert_nonnull (repository);
-
-  typelib = gi_repository_require (repository, "GObject", "2.0", 0, &error);
-  g_assert_no_error (error);
-  g_assert_nonnull (typelib);
-
-  dependencies = gi_repository_get_dependencies (repository, "GObject");
+  dependencies = gi_repository_get_dependencies (fx->repository, "GObject", &n_dependencies);
   g_assert_cmpuint (g_strv_length (dependencies), ==, 1);
+  g_assert_cmpuint (n_dependencies, ==, 1);
   g_assert_true (g_strv_contains ((const char **) dependencies, "GLib-2.0"));
 
   g_clear_error (&error);
-  g_clear_object (&repository);
   g_clear_pointer (&dependencies, g_strfreev);
 }
 
 static void
-test_repository_arg_info (void)
+test_repository_arg_info (RepositoryFixture *fx,
+                          const void *unused)
 {
-  GIRepository *repository;
   GIObjectInfo *object_info = NULL;
   GIStructInfo *struct_info = NULL;
   GIFunctionInfo *method_info = NULL;
   GIArgInfo *arg_info = NULL;
   GITypeInfo *type_info = NULL;
+  GITypeInfo type_info_stack;
   unsigned int idx;
 
   g_test_summary ("Test retrieving GIArgInfos from a typelib");
 
-  repository = load_typelib_from_builddir ("GObject", "2.0");
-
   /* Test all the methods of GIArgInfo. Here we’re looking at the
    * `const char *property_name` argument of g_object_get_property(). (The
    * ‘self’ argument is not exposed through gi_callable_info_get_arg().) */
-  object_info = (GIObjectInfo *) gi_repository_find_by_name (repository, "GObject", "Object");
+  object_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "Object"));
   g_assert_nonnull (object_info);
 
   method_info = gi_object_info_find_method (object_info, "get_property");
@@ -232,14 +202,20 @@ test_repository_arg_info (void)
   g_assert_true (gi_type_info_is_pointer (type_info));
   g_assert_cmpint (gi_type_info_get_tag (type_info), ==, GI_TYPE_TAG_UTF8);
 
+  gi_arg_info_load_type_info (arg_info, &type_info_stack);
+  g_assert_true (gi_type_info_is_pointer (&type_info_stack) == gi_type_info_is_pointer (type_info));
+  g_assert_cmpint (gi_type_info_get_tag (&type_info_stack), ==, gi_type_info_get_tag (type_info));
+
+  gi_base_info_clear (&type_info_stack);
   g_clear_pointer (&type_info, gi_base_info_unref);
+
   g_clear_pointer (&arg_info, gi_base_info_unref);
   g_clear_pointer (&method_info, gi_base_info_unref);
   g_clear_pointer (&object_info, gi_base_info_unref);
 
   /* Test an (out) argument. Here it’s the `guint *n_properties` from
    * g_object_class_list_properties(). */
-  struct_info = (GIStructInfo *) gi_repository_find_by_name (repository, "GObject", "ObjectClass");
+  struct_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "ObjectClass"));
   g_assert_nonnull (struct_info);
 
   method_info = gi_struct_info_find_method (struct_info, "list_properties");
@@ -256,48 +232,27 @@ test_repository_arg_info (void)
   g_clear_pointer (&arg_info, gi_base_info_unref);
   g_clear_pointer (&method_info, gi_base_info_unref);
   g_clear_pointer (&struct_info, gi_base_info_unref);
-
-  g_clear_object (&repository);
 }
 
 static void
-test_repository_boxed_info (void)
+test_repository_callable_info (RepositoryFixture *fx,
+                               const void *unused)
 {
-  GIRepository *repository;
-  GIBoxedInfo *boxed_info = NULL;
-
-  g_test_summary ("Test retrieving GIBoxedInfos from a typelib");
-
-  repository = load_typelib_from_builddir ("GObject", "2.0");
-
-  /* Test all the methods of GIBoxedInfo. This is simple, because there are none. */
-  boxed_info = (GIBoxedInfo *) gi_repository_find_by_name (repository, "GObject", "BookmarkFile");
-  g_assert_nonnull (boxed_info);
-
-  g_clear_pointer (&boxed_info, gi_base_info_unref);
-
-  g_clear_object (&repository);
-}
-
-static void
-test_repository_callable_info (void)
-{
-  GIRepository *repository;
   GIObjectInfo *object_info = NULL;
   GIFunctionInfo *method_info = NULL;
   GICallableInfo *callable_info;
   GITypeInfo *type_info = NULL;
+  GITypeInfo type_info_stack;
   GIAttributeIter iter = GI_ATTRIBUTE_ITER_INIT;
   const char *name, *value;
   GIArgInfo *arg_info = NULL;
+  GIArgInfo arg_info_stack;
 
   g_test_summary ("Test retrieving GICallableInfos from a typelib");
 
-  repository = load_typelib_from_builddir ("GObject", "2.0");
-
   /* Test all the methods of GICallableInfo. Here we’re looking at
    * g_object_get_qdata(). */
-  object_info = (GIObjectInfo *) gi_repository_find_by_name (repository, "GObject", "Object");
+  object_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "Object"));
   g_assert_nonnull (object_info);
 
   method_info = gi_object_info_find_method (object_info, "get_qdata");
@@ -312,6 +267,12 @@ test_repository_callable_info (void)
   g_assert_nonnull (type_info);
   g_assert_true (gi_type_info_is_pointer (type_info));
   g_assert_cmpint (gi_type_info_get_tag (type_info), ==, GI_TYPE_TAG_VOID);
+
+  gi_callable_info_load_return_type (callable_info, &type_info_stack);
+  g_assert_true (gi_type_info_is_pointer (&type_info_stack) == gi_type_info_is_pointer (type_info));
+  g_assert_cmpint (gi_type_info_get_tag (&type_info_stack), ==, gi_type_info_get_tag (type_info));
+
+  gi_base_info_clear (&type_info_stack);
   g_clear_pointer (&type_info, gi_base_info_unref);
 
   /* This method has no attributes */
@@ -327,52 +288,485 @@ test_repository_callable_info (void)
 
   arg_info = gi_callable_info_get_arg (callable_info, 0);
   g_assert_nonnull (arg_info);
+
+  gi_callable_info_load_arg (callable_info, 0, &arg_info_stack);
+  g_assert_cmpint (gi_arg_info_get_direction (&arg_info_stack), ==, gi_arg_info_get_direction (arg_info));
+  g_assert_true (gi_arg_info_may_be_null (&arg_info_stack) == gi_arg_info_may_be_null (arg_info));
+
+  gi_base_info_clear (&arg_info_stack);
   g_clear_pointer (&arg_info, gi_base_info_unref);
 
   g_assert_cmpint (gi_callable_info_get_instance_ownership_transfer (callable_info), ==, GI_TRANSFER_NOTHING);
 
   g_clear_pointer (&method_info, gi_base_info_unref);
   g_clear_pointer (&object_info, gi_base_info_unref);
-
-  g_clear_object (&repository);
 }
 
 static void
-test_repository_callback_info (void)
+test_repository_callback_info (RepositoryFixture *fx,
+                               const void *unused)
 {
-  GIRepository *repository;
   GICallbackInfo *callback_info = NULL;
 
   g_test_summary ("Test retrieving GICallbackInfos from a typelib");
 
-  repository = load_typelib_from_builddir ("GObject", "2.0");
-
   /* Test all the methods of GICallbackInfo. This is simple, because there are none. */
-  callback_info = (GICallbackInfo *) gi_repository_find_by_name (repository, "GObject", "ObjectFinalizeFunc");
+  callback_info = GI_CALLBACK_INFO (gi_repository_find_by_name (fx->repository, "GObject", "ObjectFinalizeFunc"));
   g_assert_nonnull (callback_info);
 
   g_clear_pointer (&callback_info, gi_base_info_unref);
+}
+
+static void
+test_repository_char_types (RepositoryFixture *fx,
+                            const void *unused)
+{
+  GIStructInfo *gvalue_info;
+  GIFunctionInfo *method_info;
+  GITypeInfo *type_info;
+
+  g_test_summary ("Test that signed and unsigned char GITypeInfo have GITypeTag of INT8 and UINT8 respectively");
+
+  gvalue_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "Value"));
+  g_assert_nonnull (gvalue_info);
 
-  g_clear_object (&repository);
+  /* unsigned char */
+  method_info = gi_struct_info_find_method (gvalue_info, "get_uchar");
+  g_assert_nonnull (method_info);
+
+  type_info = gi_callable_info_get_return_type (GI_CALLABLE_INFO (method_info));
+  g_assert_nonnull (type_info);
+  g_assert_cmpuint (gi_type_info_get_tag (type_info), ==, GI_TYPE_TAG_UINT8);
+
+  g_clear_pointer (&type_info, gi_base_info_unref);
+  g_clear_pointer (&method_info, gi_base_info_unref);
+
+  /* signed char */
+  method_info = gi_struct_info_find_method (gvalue_info, "get_schar");
+  g_assert_nonnull (method_info);
+
+  type_info = gi_callable_info_get_return_type (GI_CALLABLE_INFO (method_info));
+  g_assert_nonnull (type_info);
+  g_assert_cmpuint (gi_type_info_get_tag (type_info), ==, GI_TYPE_TAG_INT8);
+
+  g_clear_pointer (&type_info, gi_base_info_unref);
+  g_clear_pointer (&method_info, gi_base_info_unref);
+  g_clear_pointer (&gvalue_info, gi_base_info_unref);
+}
+
+static void
+test_repository_constructor_return_type (RepositoryFixture *fx,
+                                         const void *unused)
+{
+  GIObjectInfo *object_info = NULL;
+  GIFunctionInfo *constructor = NULL;
+  GITypeInfo *return_type = NULL;
+  GIBaseInfo *return_info = NULL;
+  const char *class_name = NULL;
+  const char *return_name = NULL;
+
+  g_test_summary ("Test the return type of a constructor, g_object_newv()");
+
+  object_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "Object"));
+  g_assert_nonnull (object_info);
+
+  class_name = gi_registered_type_info_get_type_name (GI_REGISTERED_TYPE_INFO (object_info));
+  g_assert_nonnull (class_name);
+
+  constructor = gi_object_info_find_method (object_info, "newv");
+  g_assert_nonnull (constructor);
+
+  return_type = gi_callable_info_get_return_type (GI_CALLABLE_INFO (constructor));
+  g_assert_nonnull (return_type);
+  g_assert_cmpuint (gi_type_info_get_tag (return_type), ==, GI_TYPE_TAG_INTERFACE);
+
+  return_info = gi_type_info_get_interface (return_type);
+  g_assert_nonnull (return_info);
+
+  return_name = gi_registered_type_info_get_type_name (GI_REGISTERED_TYPE_INFO (return_info));
+  g_assert_nonnull (return_name);
+  g_assert_cmpstr (class_name, ==, return_name);
+
+  g_clear_pointer (&return_info, gi_base_info_unref);
+  g_clear_pointer (&return_type, gi_base_info_unref);
+  g_clear_pointer (&constructor, gi_base_info_unref);
+  g_clear_pointer (&object_info, gi_base_info_unref);
+}
+
+static void
+test_repository_enum_info_c_identifier (RepositoryFixture *fx,
+                                        const void *unused)
+{
+  GIBaseInfo *info = NULL;
+  GIValueInfo *value_info = NULL;
+  unsigned n_infos, n_values, ix, jx;
+  const char *c_identifier = NULL;
+
+  g_test_summary ("Test that every enum member has a C identifier");
+
+  n_infos = gi_repository_get_n_infos (fx->repository, "GLib");
+
+  for (ix = 0; ix < n_infos; ix++)
+    {
+      info = gi_repository_get_info (fx->repository, "GLib", ix);
+
+      if (GI_IS_ENUM_INFO (info))
+        {
+          n_values = gi_enum_info_get_n_values (GI_ENUM_INFO (info));
+          for (jx = 0; jx < n_values; jx++)
+            {
+              value_info = gi_enum_info_get_value (GI_ENUM_INFO (info), jx);
+              c_identifier = gi_base_info_get_attribute (GI_BASE_INFO (value_info), "c:identifier");
+              g_assert_nonnull (c_identifier);
+
+              g_clear_pointer (&value_info, gi_base_info_unref);
+            }
+        }
+
+      g_clear_pointer (&info, gi_base_info_unref);
+    }
+}
+
+static void
+test_repository_enum_info_static_methods (RepositoryFixture *fx,
+                                          const void *unused)
+{
+  GIEnumInfo *enum_info = NULL;
+  unsigned n_methods, ix;
+  GIFunctionInfo *function_info = NULL;
+  GIFunctionInfoFlags flags;
+  const char *symbol = NULL;
+
+  g_test_summary ("Test an enum with methods");
+
+  enum_info = GI_ENUM_INFO (gi_repository_find_by_name (fx->repository, "GLib", "UnicodeScript"));
+  g_assert_nonnull (enum_info);
+
+  n_methods = gi_enum_info_get_n_methods (enum_info);
+  g_assert_cmpuint (n_methods, >, 0);
+
+  for (ix = 0; ix < n_methods; ix++)
+    {
+      function_info = gi_enum_info_get_method (enum_info, ix);
+      g_assert_nonnull (function_info);
+
+      flags = gi_function_info_get_flags (function_info);
+      g_assert_false (flags & GI_FUNCTION_IS_METHOD); /* must be static */
+
+      symbol = gi_function_info_get_symbol (function_info);
+      g_assert_nonnull (symbol);
+      g_assert_true (g_str_has_prefix (symbol, "g_unicode_script_"));
+
+      g_clear_pointer (&function_info, gi_base_info_unref);
+    }
+
+  g_clear_pointer (&enum_info, gi_base_info_unref);
+}
+
+static void
+test_repository_error_quark (RepositoryFixture *fx,
+                             const void *unused)
+{
+  GIEnumInfo *error_info = NULL;
+
+  g_test_summary ("Test finding an error quark by error domain");
+
+  error_info = gi_repository_find_by_error_domain (fx->repository, G_RESOLVER_ERROR);
+  g_assert_nonnull (error_info);
+  g_assert_true (GI_IS_ENUM_INFO (error_info));
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (error_info)), ==, "ResolverError");
+
+  g_clear_pointer (&error_info, gi_base_info_unref);
+}
+
+static void
+test_repository_flags_info_c_identifier (RepositoryFixture *fx,
+                                         const void *unused)
+{
+  GIBaseInfo *info = NULL;
+  GIValueInfo *value_info = NULL;
+  unsigned n_infos, n_values, ix, jx;
+  const char *c_identifier = NULL;
+
+  g_test_summary ("Test that every flags member has a C identifier");
+
+  n_infos = gi_repository_get_n_infos (fx->repository, "GLib");
+
+  for (ix = 0; ix < n_infos; ix++)
+    {
+      info = gi_repository_get_info (fx->repository, "GLib", ix);
+
+      if (GI_IS_FLAGS_INFO (info))
+        {
+          n_values = gi_enum_info_get_n_values (GI_ENUM_INFO (info));
+          for (jx = 0; jx < n_values; jx++)
+            {
+              value_info = gi_enum_info_get_value (GI_ENUM_INFO (info), jx);
+              c_identifier = gi_base_info_get_attribute (GI_BASE_INFO (value_info), "c:identifier");
+              g_assert_nonnull (c_identifier);
+
+              g_clear_pointer (&value_info, gi_base_info_unref);
+            }
+        }
+
+      g_clear_pointer (&info, gi_base_info_unref);
+    }
+}
+
+static void
+test_repository_fundamental_ref_func (RepositoryFixture *fx,
+                                      const void *unused)
+{
+  GIObjectInfo *info;
+
+  g_test_summary ("Test getting the ref func of a fundamental type");
+
+  info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "ParamSpec"));
+  g_assert_nonnull (info);
+
+  g_assert_nonnull (gi_object_info_get_ref_function_pointer (info));
+
+  g_clear_pointer (&info, gi_base_info_unref);
+}
+
+static void
+test_repository_instance_method_ownership_transfer (RepositoryFixture *fx,
+                                                    const void *unused)
+{
+  GIObjectInfo *class_info = NULL;
+  GIFunctionInfo *func_info = NULL;
+  GITransfer transfer;
+
+  g_test_summary ("Test two methods of the same object having opposite ownership transfer of the instance parameter");
+
+  class_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "Gio", "DBusMethodInvocation"));
+  g_assert_nonnull (class_info);
+
+  func_info = gi_object_info_find_method (class_info, "get_connection");
+  g_assert_nonnull (func_info);
+  transfer = gi_callable_info_get_instance_ownership_transfer (GI_CALLABLE_INFO (func_info));
+  g_assert_cmpint (GI_TRANSFER_NOTHING, ==, transfer);
+
+  g_clear_pointer (&func_info, gi_base_info_unref);
+
+  func_info = gi_object_info_find_method (class_info, "return_error_literal");
+  g_assert_nonnull (func_info);
+  transfer = gi_callable_info_get_instance_ownership_transfer (GI_CALLABLE_INFO (func_info));
+  g_assert_cmpint (GI_TRANSFER_EVERYTHING, ==, transfer);
+
+  g_clear_pointer (&func_info, gi_base_info_unref);
+  g_clear_pointer (&class_info, gi_base_info_unref);
+}
+
+static void
+test_repository_object_gtype_interfaces (RepositoryFixture *fx,
+                                         const void *unused)
+{
+  GIInterfaceInfo **interfaces;
+  size_t n_interfaces, ix;
+  const char *name;
+  gboolean found_initable = FALSE, found_async_initable = FALSE;
+
+  g_test_summary ("Test gi_repository_get_object_gtype_interfaces()");
+
+  gi_repository_get_object_gtype_interfaces (fx->repository, G_TYPE_DBUS_CONNECTION, &n_interfaces, &interfaces);
+
+  g_assert_cmpuint (n_interfaces, ==, 2);
+
+  for (ix = 0; ix < n_interfaces; ix++)
+    {
+      name = gi_base_info_get_name (GI_BASE_INFO (*(interfaces + ix)));
+      if (strcmp (name, "Initable") == 0)
+        found_initable = TRUE;
+      else if (strcmp (name, "AsyncInitable") == 0)
+        found_async_initable = TRUE;
+    }
+
+  g_assert_true (found_initable);
+  g_assert_true (found_async_initable);
+}
+
+static void
+test_repository_signal_info_with_array_length_arg (RepositoryFixture *fx,
+                                                   const void *unused)
+{
+  GIObjectInfo *gsettings_info = NULL;
+  GISignalInfo *sig_info = NULL;
+  GIArgInfo *arg_info = NULL;
+  GITypeInfo *type_info = NULL;
+  unsigned length_ix;
+
+  g_test_summary ("Test finding the associated array length argument of an array parameter of a signal");
+
+  gsettings_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "Gio", "Settings"));
+  g_assert_nonnull (gsettings_info);
+
+  sig_info = gi_object_info_find_signal (gsettings_info, "change-event");
+  g_assert_nonnull (sig_info);
+
+  g_assert_cmpuint (gi_callable_info_get_n_args (GI_CALLABLE_INFO (sig_info)), ==, 2);
+
+  /* verify array argument */
+  arg_info = gi_callable_info_get_arg (GI_CALLABLE_INFO (sig_info), 0);
+  g_assert_nonnull (arg_info);
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (arg_info)), ==, "keys");
+
+  type_info = gi_arg_info_get_type_info (arg_info);
+  g_assert_nonnull (type_info);
+  g_assert_cmpint (gi_type_info_get_tag (type_info), ==, GI_TYPE_TAG_ARRAY);
+  g_assert_cmpint (gi_type_info_get_array_type (type_info), ==, GI_ARRAY_TYPE_C);
+  g_assert_false (gi_type_info_is_zero_terminated (type_info));
+  gboolean ok = gi_type_info_get_array_length_index (type_info, &length_ix);
+  g_assert_true (ok);
+  g_assert_cmpuint (length_ix, ==, 1);
+
+  g_clear_pointer (&arg_info, gi_base_info_unref);
+  g_clear_pointer (&type_info, gi_base_info_unref);
+
+  /* verify array length argument */
+  arg_info = gi_callable_info_get_arg (GI_CALLABLE_INFO (sig_info), 1);
+  g_assert_nonnull (arg_info);
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (arg_info)), ==, "n_keys");
+
+  g_clear_pointer (&arg_info, gi_base_info_unref);
+  g_clear_pointer (&type_info, gi_base_info_unref);
+  g_clear_pointer (&sig_info, gi_base_info_unref);
+  g_clear_pointer (&gsettings_info, gi_base_info_unref);
+}
+
+static void
+test_repository_type_info_name (RepositoryFixture *fx,
+                                const void *unused)
+{
+  GIInterfaceInfo *interface_info = NULL;
+  GIVFuncInfo *vfunc;
+  GITypeInfo *typeinfo;
+
+  g_test_summary ("Test that gi_base_info_get_name() returns null for GITypeInfo");
+  g_test_bug ("https://gitlab.gnome.org/GNOME/gobject-introspection/issues/96");
+
+  interface_info = GI_INTERFACE_INFO (gi_repository_find_by_name (fx->repository, "Gio", "File"));
+  g_assert_nonnull (interface_info);
+  vfunc = gi_interface_info_find_vfunc (interface_info, "read_async");
+  g_assert_nonnull (vfunc);
+
+  typeinfo = gi_callable_info_get_return_type (GI_CALLABLE_INFO (vfunc));
+  g_assert_nonnull (typeinfo);
+
+  g_assert_null (gi_base_info_get_name (GI_BASE_INFO (typeinfo)));
+
+  g_clear_pointer (&interface_info, gi_base_info_unref);
+  g_clear_pointer (&vfunc, gi_base_info_unref);
+  g_clear_pointer (&typeinfo, gi_base_info_unref);
+}
+
+static void
+test_repository_vfunc_info_with_no_invoker (RepositoryFixture *fx,
+                                            const void *unused)
+{
+  GIObjectInfo *object_info = NULL;
+  GIVFuncInfo *vfunc_info = NULL;
+  GIFunctionInfo *invoker_info = NULL;
+
+  g_test_summary ("Test vfunc with no known invoker on object, such as GObject.dispose");
+
+  object_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "Object"));
+  g_assert_nonnull (object_info);
+
+  vfunc_info = gi_object_info_find_vfunc (object_info, "dispose");
+  g_assert_nonnull (vfunc_info);
+
+  invoker_info = gi_vfunc_info_get_invoker (vfunc_info);
+  g_assert_null (invoker_info);
+
+  g_clear_pointer (&object_info, gi_base_info_unref);
+  g_clear_pointer (&vfunc_info, gi_base_info_unref);
+}
+
+static void
+test_repository_vfunc_info_with_invoker_on_interface (RepositoryFixture *fx,
+                                                      const void *unused)
+{
+  GIInterfaceInfo *interface_info = NULL;
+  GIVFuncInfo *vfunc_info = NULL;
+  GIFunctionInfo *invoker_info = NULL;
+
+  g_test_summary ("Test vfunc with invoker on interface, such as GFile.read_async");
+
+  interface_info = GI_INTERFACE_INFO (gi_repository_find_by_name (fx->repository, "Gio", "File"));
+  g_assert_nonnull (interface_info);
+
+  vfunc_info = gi_interface_info_find_vfunc (interface_info, "read_async");
+  g_assert_nonnull (vfunc_info);
+
+  invoker_info = gi_vfunc_info_get_invoker (vfunc_info);
+  g_assert_nonnull (invoker_info);
+
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (invoker_info)), ==, "read_async");
+
+  g_clear_pointer (&interface_info, gi_base_info_unref);
+  g_clear_pointer (&vfunc_info, gi_base_info_unref);
+  g_clear_pointer (&invoker_info, gi_base_info_unref);
+}
+
+static void
+test_repository_vfunc_info_with_invoker_on_object (RepositoryFixture *fx,
+                                                   const void *unused)
+{
+  GIObjectInfo *object_info = NULL;
+  GIVFuncInfo *vfunc_info = NULL;
+  GIFunctionInfo *invoker_info = NULL;
+
+  g_test_summary ("Test vfunc with invoker on object, such as GAppLaunchContext.get_display");
+
+  object_info = GI_OBJECT_INFO (gi_repository_find_by_name (fx->repository, "Gio", "AppLaunchContext"));
+  g_assert_nonnull (object_info);
+
+  vfunc_info = gi_object_info_find_vfunc (object_info, "get_display");
+  g_assert_nonnull (vfunc_info);
+
+  invoker_info = gi_vfunc_info_get_invoker (vfunc_info);
+  g_assert_nonnull (invoker_info);
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (invoker_info)), ==, "get_display");
+
+  /* And let's be sure we can find the method directly */
+  g_clear_pointer (&invoker_info, gi_base_info_unref);
+
+  invoker_info = gi_object_info_find_method (object_info, "get_display");
+  g_assert_nonnull (invoker_info);
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (invoker_info)), ==, "get_display");
+
+  g_clear_pointer (&object_info, gi_base_info_unref);
+  g_clear_pointer (&vfunc_info, gi_base_info_unref);
+  g_clear_pointer (&invoker_info, gi_base_info_unref);
 }
 
 int
 main (int   argc,
       char *argv[])
 {
-  /* Isolate from the system typelibs and GIRs. */
-  g_setenv ("GI_TYPELIB_PATH", "/dev/null", TRUE);
-  g_setenv ("GI_GIR_PATH", "/dev/null", TRUE);
-
-  g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
-
-  g_test_add_func ("/repository/basic", test_repository_basic);
-  g_test_add_func ("/repository/info", test_repository_info);
-  g_test_add_func ("/repository/dependencies", test_repository_dependencies);
-  g_test_add_func ("/repository/arg-info", test_repository_arg_info);
-  g_test_add_func ("/repository/boxed-info", test_repository_boxed_info);
-  g_test_add_func ("/repository/callable-info", test_repository_callable_info);
-  g_test_add_func ("/repository/callback-info", test_repository_callback_info);
+  repository_init (&argc, &argv);
+
+  ADD_REPOSITORY_TEST ("/repository/basic", test_repository_basic, &typelib_load_spec_glib);
+  ADD_REPOSITORY_TEST ("/repository/info", test_repository_info, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/dependencies", test_repository_dependencies, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/arg-info", test_repository_arg_info, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/callable-info", test_repository_callable_info, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/callback-info", test_repository_callback_info, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/char-types", test_repository_char_types, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/constructor-return-type", test_repository_constructor_return_type, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/enum-info-c-identifier", test_repository_enum_info_c_identifier, &typelib_load_spec_glib);
+  ADD_REPOSITORY_TEST ("/repository/enum-info-static-methods", test_repository_enum_info_static_methods, &typelib_load_spec_glib);
+  ADD_REPOSITORY_TEST ("/repository/error-quark", test_repository_error_quark, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/repository/flags-info-c-identifier", test_repository_flags_info_c_identifier, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/fundamental-ref-func", test_repository_fundamental_ref_func, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/instance-method-ownership-transfer", test_repository_instance_method_ownership_transfer, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/repository/object-gtype-interfaces", test_repository_object_gtype_interfaces, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/repository/signal-info-with-array-length-arg", test_repository_signal_info_with_array_length_arg, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/repository/type-info-name", test_repository_type_info_name, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/repository/vfunc-info-with-no-invoker", test_repository_vfunc_info_with_no_invoker, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/repository/vfunc-info-with-invoker-on-interface", test_repository_vfunc_info_with_invoker_on_interface, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/repository/vfunc-info-with-invoker-on-object", test_repository_vfunc_info_with_invoker_on_object, &typelib_load_spec_gio);
 
   return g_test_run ();
 }
diff --git a/girepository/tests/struct-info.c b/girepository/tests/struct-info.c
new file mode 100644 (file)
index 0000000..7ed18c8
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 Simon Feltman <sfeltman@gnome.org>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "girepository.h"
+#include "test-common.h"
+
+static void
+test_field_iterators (RepositoryFixture *fx,
+                      const void *unused)
+{
+  GIStructInfo *class_info = NULL;
+  GIFieldInfo *field_info = NULL;
+  unsigned ix;
+
+  g_test_summary ("Test iterating through a struct's fields with gi_struct_info_get_field()");
+
+  class_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "ObjectClass"));
+  g_assert_nonnull (class_info);
+
+  for (ix = 0; ix < gi_struct_info_get_n_fields (class_info); ix++)
+    {
+      const char *field_name = NULL;
+      GIFieldInfo *found = NULL;
+
+      field_info = gi_struct_info_get_field (class_info, ix);
+      g_assert_nonnull (field_info);
+
+      field_name = gi_base_info_get_name (GI_BASE_INFO (field_info));
+      g_assert_nonnull (field_name);
+
+      found = gi_struct_info_find_field (class_info, field_name);
+      g_assert_nonnull (found);
+      g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (found)), ==, field_name);
+
+      g_clear_pointer (&found, gi_base_info_unref);
+      g_clear_pointer (&field_info, gi_base_info_unref);
+    }
+
+  field_info = gi_struct_info_find_field (class_info, "not_a_real_field_name");
+  g_assert_null (field_info);
+
+  g_clear_pointer (&class_info, gi_base_info_unref);
+}
+
+static void
+test_size_of_gvalue (RepositoryFixture *fx,
+                     const void *unused)
+{
+  GIStructInfo *struct_info;
+
+  g_test_summary ("Test that gi_struct_info_get_size() reports the correct sizeof GValue");
+
+  struct_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "Value"));
+  g_assert_nonnull (struct_info);
+
+  g_assert_cmpuint (gi_struct_info_get_size (struct_info), ==, sizeof (GValue));
+
+  g_clear_pointer (&struct_info, gi_base_info_unref);
+}
+
+static void
+test_is_pointer_for_struct_method_arg (RepositoryFixture *fx,
+                                       const void *unused)
+{
+  GIStructInfo *variant_info = NULL;
+  GIFunctionInfo *equal_info = NULL;
+  GIArgInfo *arg_info = NULL;
+  GITypeInfo *type_info = NULL;
+
+  g_test_summary ("Test that a struct method reports the correct type with gi_type_info_is_pointer()");
+
+  variant_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "GLib", "Variant"));
+  g_assert_nonnull (variant_info);
+
+  equal_info = gi_struct_info_find_method (variant_info, "equal");
+  g_assert_nonnull (equal_info);
+
+  arg_info = gi_callable_info_get_arg (GI_CALLABLE_INFO (equal_info), 0);
+  g_assert_nonnull (arg_info);
+
+  type_info = gi_arg_info_get_type_info (arg_info);
+  g_assert_nonnull (type_info);
+  g_assert_true (gi_type_info_is_pointer (type_info));
+
+  g_clear_pointer (&type_info, gi_base_info_unref);
+  g_clear_pointer (&arg_info, gi_base_info_unref);
+  g_clear_pointer (&equal_info, gi_base_info_unref);
+  g_clear_pointer (&variant_info, gi_base_info_unref);
+}
+
+static void
+test_boxed (RepositoryFixture *fx,
+            const void        *unused)
+{
+  GIStructInfo *struct_info = NULL;
+
+  g_test_summary ("Test that a boxed struct is recognised as such");
+
+  struct_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "GObject", "BookmarkFile"));
+  g_assert_nonnull (struct_info);
+  g_assert_true (gi_registered_type_info_is_boxed (GI_REGISTERED_TYPE_INFO (struct_info)));
+
+  g_clear_pointer (&struct_info, gi_base_info_unref);
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+  repository_init (&argc, &argv);
+
+  ADD_REPOSITORY_TEST ("/struct-info/field-iterators", test_field_iterators, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/struct-info/sizeof-gvalue", test_size_of_gvalue, &typelib_load_spec_gobject);
+  ADD_REPOSITORY_TEST ("/struct-info/is-pointer-for-struct-method-arg", test_is_pointer_for_struct_method_arg, &typelib_load_spec_glib);
+  ADD_REPOSITORY_TEST ("/struct-info/boxed", test_boxed, &typelib_load_spec_gobject);
+
+  return g_test_run ();
+}
diff --git a/girepository/tests/test-common.c b/girepository/tests/test-common.c
new file mode 100644 (file)
index 0000000..3b5ce3e
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 GNOME Foundation, Inc.
+ * Copyright 2024 Philip Chimento <philip.chimento@gmail.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "girepository.h"
+#include "glib.h"
+#include "test-common.h"
+
+void
+repository_init (int *argc,
+                 char **argv[])
+{
+  /* Isolate from the system typelibs and GIRs. */
+  g_setenv ("GI_TYPELIB_PATH", "/dev/null", TRUE);
+  g_setenv ("GI_GIR_PATH", "/dev/null", TRUE);
+
+  g_test_init (argc, argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
+}
+
+void
+repository_setup (RepositoryFixture *fx,
+                  const void *data)
+{
+  GITypelib *typelib = NULL;
+  GError *local_error = NULL;
+  TypelibLoadSpec *load_spec = (TypelibLoadSpec *) data;
+
+  fx->repository = gi_repository_new ();
+  g_assert_nonnull (fx->repository);
+
+  fx->gobject_typelib_dir = g_test_build_filename (G_TEST_BUILT, "..", "introspection", NULL);
+  g_test_message ("Using GI_TYPELIB_DIR = %s", fx->gobject_typelib_dir);
+  gi_repository_prepend_search_path (fx->repository, fx->gobject_typelib_dir);
+
+  if (load_spec)
+    {
+      typelib = gi_repository_require (fx->repository, load_spec->name, load_spec->version, 0, &local_error);
+      g_assert_no_error (local_error);
+      g_assert_nonnull (typelib);
+    }
+}
+
+void
+repository_teardown (RepositoryFixture *fx,
+                     const void *unused)
+{
+  g_clear_pointer (&fx->gobject_typelib_dir, g_free);
+  g_clear_object (&fx->repository);
+}
diff --git a/girepository/tests/test-common.h b/girepository/tests/test-common.h
new file mode 100644 (file)
index 0000000..9d31998
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 Philip Chimento <philip.chimento@gmail.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "girepository.h"
+
+typedef struct
+{
+  GIRepository *repository;
+  char *gobject_typelib_dir;
+} RepositoryFixture;
+
+typedef struct
+{
+  const char *name;
+  const char *version;
+} TypelibLoadSpec;
+
+static const TypelibLoadSpec typelib_load_spec_glib = { "GLib", "2.0" };
+static const TypelibLoadSpec typelib_load_spec_gobject = { "GObject", "2.0" };
+static const TypelibLoadSpec typelib_load_spec_gio = { "Gio", "2.0" };
+
+void repository_init (int *argc,
+                      char **argv[]);
+
+void repository_setup (RepositoryFixture *fx,
+                       const void *data);
+
+void repository_teardown (RepositoryFixture *fx,
+                          const void *unused);
+
+#define ADD_REPOSITORY_TEST(testpath, ftest, load_spec) \
+  g_test_add ((testpath), RepositoryFixture, load_spec, repository_setup, (ftest), repository_teardown)
diff --git a/girepository/tests/throws.c b/girepository/tests/throws.c
new file mode 100644 (file)
index 0000000..2849e53
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2008 litl, LLC
+ * Copyright 2014 Simon Feltman <sfeltman@gnome.org>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "girepository.h"
+#include "test-common.h"
+
+static void
+test_invoke_gerror (RepositoryFixture *fx,
+                    const void *unused)
+{
+  GIFunctionInfo *func_info = NULL;
+  GIArgument in_arg[1];
+  GIArgument ret_arg;
+  GError *error = NULL;
+  gboolean invoke_return;
+
+  g_test_summary ("Test invoking a function that throws a GError");
+
+  func_info = GI_FUNCTION_INFO (gi_repository_find_by_name (fx->repository, "GLib", "file_read_link"));
+  g_assert_nonnull (func_info);
+  g_assert_true (gi_callable_info_can_throw_gerror (GI_CALLABLE_INFO (func_info)));
+
+  in_arg[0].v_string = g_strdup ("non-existent-file/hope");
+  invoke_return = gi_function_info_invoke (func_info, in_arg, 1, NULL, 0, &ret_arg, &error);
+  g_clear_pointer (&in_arg[0].v_string, g_free);
+
+  g_assert_false (invoke_return);
+  g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
+
+  g_clear_error (&error);
+  g_clear_pointer (&func_info, gi_base_info_unref);
+}
+
+static void
+test_vfunc_can_throw_gerror (RepositoryFixture *fx,
+                             const void *unused)
+{
+  GIInterfaceInfo *interface_info = NULL;
+  GIFunctionInfo *invoker_info = NULL;
+  GIVFuncInfo *vfunc_info = NULL;
+
+  g_test_summary ("Test gi_callable_info_can_throw_gerror() on a vfunc");
+
+  interface_info = GI_INTERFACE_INFO (gi_repository_find_by_name (fx->repository, "Gio", "AppInfo"));
+  g_assert_nonnull (interface_info);
+
+  invoker_info = gi_interface_info_find_method (interface_info, "launch");
+  g_assert_nonnull (invoker_info);
+  g_assert_true (gi_callable_info_can_throw_gerror (GI_CALLABLE_INFO (invoker_info)));
+
+  vfunc_info = gi_interface_info_find_vfunc (interface_info, "launch");
+  g_assert_nonnull (vfunc_info);
+  g_assert_true (gi_callable_info_can_throw_gerror (GI_CALLABLE_INFO (vfunc_info)));
+
+  g_clear_pointer (&vfunc_info, gi_base_info_unref);
+  g_clear_pointer (&invoker_info, gi_base_info_unref);
+  g_clear_pointer (&interface_info, gi_base_info_unref);
+}
+
+static void
+test_callback_can_throw_gerror (RepositoryFixture *fx,
+                                const void *unused)
+{
+  GIStructInfo *class_info = NULL;
+  GIFieldInfo *field_info = NULL;
+  GITypeInfo *field_type = NULL;
+  GICallbackInfo *callback_info = NULL;
+
+  g_test_summary ("Test gi_callable_info_can_throw_gerror() on a callback");
+
+  class_info = GI_STRUCT_INFO (gi_repository_find_by_name (fx->repository, "Gio", "AppInfoIface"));
+  g_assert_nonnull (class_info);
+
+  field_info = gi_struct_info_find_field (class_info, "launch");
+  g_assert_nonnull (field_info);
+  g_assert_true (GI_IS_FIELD_INFO (field_info));
+
+  field_type = gi_field_info_get_type_info (field_info);
+  g_assert_nonnull (field_type);
+  g_assert_true (GI_IS_TYPE_INFO (field_type));
+  g_assert_cmpint (gi_type_info_get_tag (field_type), ==, GI_TYPE_TAG_INTERFACE);
+
+  callback_info = GI_CALLBACK_INFO (gi_type_info_get_interface (field_type));
+  g_assert_nonnull (callback_info);
+  g_assert (gi_callable_info_can_throw_gerror (GI_CALLABLE_INFO (callback_info)));
+
+  g_clear_pointer (&callback_info, gi_base_info_unref);
+  g_clear_pointer (&field_type, gi_base_info_unref);
+  g_clear_pointer (&field_info, gi_base_info_unref);
+  g_clear_pointer (&class_info, gi_base_info_unref);
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+  repository_init (&argc, &argv);
+
+  ADD_REPOSITORY_TEST ("/throws/invoke-gerror", test_invoke_gerror, &typelib_load_spec_glib);
+  ADD_REPOSITORY_TEST ("/throws/vfunc-can-throw-gerror", test_vfunc_can_throw_gerror, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/throws/callback-can-throw-gerror", test_callback_can_throw_gerror, &typelib_load_spec_gio);
+
+  return g_test_run ();
+}
diff --git a/girepository/tests/union-info.c b/girepository/tests/union-info.c
new file mode 100644 (file)
index 0000000..7eca2b6
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2024 GNOME Foundation
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "girepository.h"
+#include "test-common.h"
+
+static void
+test_basic (RepositoryFixture *fx,
+            const void        *unused)
+{
+  GIUnionInfo *double_info = NULL;
+  GIFieldInfo *field_info = NULL;
+  size_t offset = 123;
+
+  g_test_summary ("Test basic properties of GIUnionInfo");
+
+  double_info = GI_UNION_INFO (gi_repository_find_by_name (fx->repository, "GLib", "DoubleIEEE754"));
+  g_assert_nonnull (double_info);
+
+  g_assert_cmpuint (gi_union_info_get_n_fields (double_info), ==, 1);
+
+  field_info = gi_union_info_get_field (double_info, 0);
+  g_assert_true (GI_IS_FIELD_INFO (field_info));
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (field_info)), ==, "v_double");
+  g_clear_pointer (&field_info, gi_base_info_unref);
+
+  g_assert_cmpuint (gi_union_info_get_n_methods (double_info), ==, 0);
+  g_assert_null (gi_union_info_find_method (double_info, "not_exist"));
+
+  g_assert_false (gi_union_info_is_discriminated (double_info));
+  g_assert_false (gi_union_info_get_discriminator_offset (double_info, &offset));
+  g_assert_cmpuint (offset, ==, 0);
+  g_assert_null (gi_union_info_get_discriminator_type (double_info));
+  g_assert_null (gi_union_info_get_discriminator (double_info, 0));
+
+  g_assert_cmpuint (gi_union_info_get_size (double_info), ==, 8);
+  g_assert_cmpuint (gi_union_info_get_alignment (double_info), ==, 8);
+
+  g_assert_null (gi_union_info_get_copy_function_name (double_info));
+  g_assert_null (gi_union_info_get_free_function_name (double_info));
+
+  g_clear_pointer (&double_info, gi_base_info_unref);
+}
+
+static void
+test_methods (RepositoryFixture *fx,
+              const void        *unused)
+{
+  GIUnionInfo *mutex_info = NULL;
+  GIFunctionInfo *method_info = NULL;
+
+  g_test_summary ("Test retrieving methods from GIUnionInfo");
+
+  mutex_info = GI_UNION_INFO (gi_repository_find_by_name (fx->repository, "GLib", "Mutex"));
+  g_assert_nonnull (mutex_info);
+
+  g_assert_cmpuint (gi_union_info_get_n_methods (mutex_info), ==, 5);
+
+  method_info = gi_union_info_get_method (mutex_info, 0);
+  g_assert_true (GI_IS_FUNCTION_INFO (method_info));
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (method_info)), ==, "clear");
+  g_clear_pointer (&method_info, gi_base_info_unref);
+
+  method_info = gi_union_info_find_method (mutex_info, "trylock");
+  g_assert_true (GI_IS_FUNCTION_INFO (method_info));
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (method_info)), ==, "trylock");
+  g_clear_pointer (&method_info, gi_base_info_unref);
+
+  g_clear_pointer (&mutex_info, gi_base_info_unref);
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+  repository_init (&argc, &argv);
+
+  ADD_REPOSITORY_TEST ("/union-info/basic", test_basic, &typelib_load_spec_glib);
+  ADD_REPOSITORY_TEST ("/union-info/methods", test_methods, &typelib_load_spec_glib);
+
+  return g_test_run ();
+}
index ba109ae..efd031b 100644 (file)
@@ -878,6 +878,7 @@ g_array_remove_range (GArray *farray,
 
   g_return_val_if_fail (array, NULL);
   g_return_val_if_fail (index_ <= array->len, NULL);
+  g_return_val_if_fail (index_ <= G_MAXUINT - length, NULL);
   g_return_val_if_fail (index_ + length <= array->len, NULL);
 
   if (array->clear_func != NULL)
@@ -2093,6 +2094,7 @@ g_ptr_array_remove_range (GPtrArray *array,
   g_return_val_if_fail (rarray != NULL, NULL);
   g_return_val_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL), NULL);
   g_return_val_if_fail (index_ <= rarray->len, NULL);
+  g_return_val_if_fail (index_ <= G_MAXUINT - length, NULL);
   g_return_val_if_fail (length == 0 || index_ + length <= rarray->len, NULL);
 
   if (length == 0)
@@ -2972,6 +2974,7 @@ g_byte_array_remove_range (GByteArray *array,
 {
   g_return_val_if_fail (array, NULL);
   g_return_val_if_fail (index_ <= array->len, NULL);
+  g_return_val_if_fail (index_ <= G_MAXUINT - length, NULL);
   g_return_val_if_fail (index_ + length <= array->len, NULL);
 
   return (GByteArray *)g_array_remove_range ((GArray *)array, index_, length);
index 4eaabc2..e7c222f 100644 (file)
@@ -73,8 +73,17 @@ try_conversion (const char *to_codeset,
 
   if (*cd == (iconv_t)-1 && errno == EINVAL)
     return FALSE;
-  else
-    return TRUE;
+
+#if defined(__FreeBSD__) && defined(ICONV_SET_ILSEQ_INVALID)
+  /* On FreeBSD request GNU iconv compatible handling of characters that cannot
+   * be repesented in the destination character set.
+   * See https://cgit.freebsd.org/src/commit/?id=7c5b23111c5fd1992047922d4247c4a1ce1bb6c3
+   */
+  int value = 1;
+  if (iconvctl (*cd, ICONV_SET_ILSEQ_INVALID, &value) != 0)
+    return FALSE;
+#endif
+  return TRUE;
 }
 
 static gboolean
index 12c77b9..992e173 100644 (file)
@@ -40,6 +40,7 @@
 
 #include "gslice.h"
 #include "gdatasetprivate.h"
+#include "gutilsprivate.h"
 #include "ghash.h"
 #include "gquark.h"
 #include "gstrfuncs.h"
@@ -164,18 +165,26 @@ datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify
   GData *d;
 
   d = *data;
-
   if (!d)
     {
-      d = g_malloc (sizeof (GData));
+      d = g_malloc (G_STRUCT_OFFSET (GData, data) + 2u * sizeof (GDataElt));
       d->len = 0;
-      d->alloc = 1;
+      d->alloc = 2u;
       *data = d;
       reallocated = TRUE;
     }
   else if (d->len == d->alloc)
     {
       d->alloc = d->alloc * 2u;
+#if G_ENABLE_DEBUG
+      /* d->alloc is always a power of two. It thus overflows the first time
+       * when going to (G_MAXUINT32+1), or when requesting 2^31+1 elements.
+       *
+       * This is not handled, and we just crash. That's because we track the GData
+       * in a linear list, which horribly degrades long before we add 2 billion entries.
+       * Don't ever try to do that. */
+      g_assert (d->alloc > d->len);
+#endif
       d = g_realloc (d, G_STRUCT_OFFSET (GData, data) + d->alloc * sizeof (GDataElt));
       *data = d;
       reallocated = TRUE;
@@ -193,6 +202,78 @@ datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify
   return reallocated;
 }
 
+static void
+datalist_remove (GData *data, guint32 idx)
+{
+#if G_ENABLE_DEBUG
+  g_assert (idx < data->len);
+#endif
+
+  /* g_data_remove_internal() relies on the fact, that this function removes
+   * the entry similar to g_array_remove_index_fast(). That is, the entries up
+   * to @idx are left unchanged, and the last entry is moved to position @idx.
+   * */
+
+  data->len--;
+
+  if (idx != data->len)
+    data->data[idx] = data->data[data->len];
+}
+
+static gboolean
+datalist_shrink (GData **data, GData **d_to_free)
+{
+  guint32 alloc_by_4;
+  guint32 v;
+  GData *d;
+
+  d = *data;
+
+  alloc_by_4 = d->alloc / 4u;
+
+  if (G_LIKELY (d->len > alloc_by_4))
+    {
+      /* No shrinking */
+      return FALSE;
+    }
+
+  if (d->len == 0)
+    {
+      /* The list became empty. We drop the allocated memory altogether. */
+
+      /* The caller will free the buffer after releasing the lock, to minimize
+       * the time we hold the lock. Transfer it out. */
+      *d_to_free = d;
+      *data = NULL;
+      return TRUE;
+    }
+
+  /* If the buffer is filled not more than 25%. Shrink to double the current length. */
+
+  v = d->len;
+  if (v != alloc_by_4)
+    {
+      /* d->alloc is a power of two. Usually, we remove one element at a
+       * time, then we will just reach reach a quarter of that.
+       *
+       * However, with g_datalist_id_remove_multiple(), len can be smaller
+       * at once. In that case, find first the next power of two. */
+      v = g_nearest_pow (v);
+    }
+  v *= 2u;
+
+#if G_ENABLE_DEBUG
+  g_assert (v > d->len);
+  g_assert (v <= d->alloc / 2u);
+#endif
+
+  d->alloc = v;
+  d = g_realloc (d, G_STRUCT_OFFSET (GData, data) + (v * sizeof (GDataElt)));
+  *d_to_free = NULL;
+  *data = d;
+  return TRUE;
+}
+
 static GDataElt *
 datalist_find (GData *data, GQuark key_id, guint32 *out_idx)
 {
@@ -234,18 +315,22 @@ g_datalist_clear (GData **datalist)
   g_return_if_fail (datalist != NULL);
 
   data = g_datalist_lock_and_get (datalist);
-  g_datalist_unlock_and_set (datalist, NULL);
 
-  if (data)
+  if (!data)
     {
-      for (i = 0; i < data->len; i++)
-        {
-          if (data->data[i].data && data->data[i].destroy)
-            data->data[i].destroy (data->data[i].data);
-        }
+      g_datalist_unlock (datalist);
+      return;
+    }
 
-      g_free (data);
+  g_datalist_unlock_and_set (datalist, NULL);
+
+  for (i = 0; i < data->len; i++)
+    {
+      if (data->data[i].data && data->data[i].destroy)
+        data->data[i].destroy (data->data[i].data);
     }
+
+  g_free (data);
 }
 
 /* HOLDS: g_dataset_global_lock */
@@ -348,33 +433,26 @@ g_data_set_internal (GData          **datalist,
     {
       if (data)
         {
+          GData *d_to_free;
+
           old = *data;
-          if (idx != d->len - 1u)
-            *data = d->data[d->len - 1u];
-          d->len--;
 
-          /* We don't bother to shrink, but if all data are now gone
-           * we at least free the memory
-           */
-          if (d->len == 0)
+          datalist_remove (d, idx);
+          if (datalist_shrink (&d, &d_to_free))
             {
-              /* datalist may be situated in dataset, so must not be
-               * unlocked when we free it
-               */
-              g_datalist_unlock_and_set (datalist, NULL);
-
-              g_free (d);
+              g_datalist_unlock_and_set (datalist, d);
 
               /* the dataset destruction *must* be done
                * prior to invocation of the data destroy function
                */
-              if (dataset)
+              if (dataset && !d)
                 g_dataset_destroy_internal (dataset);
+
+              if (d_to_free)
+                g_free (d_to_free);
             }
           else
-            {
-              g_datalist_unlock (datalist);
-            }
+            g_datalist_unlock (datalist);
 
           /* We found and removed an old value
            * the GData struct *must* already be unlinked
@@ -449,10 +527,10 @@ g_data_remove_internal (GData  **datalist,
   GData *d;
   GDataElt *old;
   GDataElt *old_to_free = NULL;
-  GDataElt *data;
-  GDataElt *data_end;
+  GData *d_to_free;
   gsize found_keys;
-  gboolean free_d = FALSE;
+  gsize i_keys;
+  guint32 i_data;
 
   d = g_datalist_lock_and_get (datalist);
 
@@ -478,68 +556,50 @@ g_data_remove_internal (GData  **datalist,
       old = old_to_free;
     }
 
-  data = d->data;
-  data_end = data + d->len;
+  i_data = 0;
   found_keys = 0;
-
-  while (data < data_end && found_keys < n_keys)
+  while (i_data < d->len && found_keys < n_keys)
     {
+      GDataElt *data = &d->data[i_data];
       gboolean remove = FALSE;
 
-      for (gsize i = 0; i < n_keys; i++)
+      for (i_keys = 0; i_keys < n_keys; i_keys++)
         {
-          if (data->key == keys[i])
+          if (data->key == keys[i_keys])
             {
-              old[i] = *data;
+              /* We must invoke the destroy notifications in the order of @keys.
+               * Hence, build up the list @old at index @i_keys. */
+              old[i_keys] = *data;
+              found_keys++;
               remove = TRUE;
               break;
             }
         }
 
-      if (remove)
-        {
-          GDataElt *data_last = data_end - 1;
-
-          found_keys++;
-
-          if (data < data_last)
-            *data = *data_last;
-
-          data_end--;
-          d->len--;
-
-          /* We don't bother to shrink, but if all data are now gone
-           * we at least free the memory
-           */
-          if (d->len == 0)
-            {
-              free_d = TRUE;
-              break;
-            }
-        }
-      else
+      if (!remove)
         {
-          data++;
+          i_data++;
+          continue;
         }
+
+      datalist_remove (d, i_data);
     }
 
-  if (free_d)
+  if (found_keys > 0 && datalist_shrink (&d, &d_to_free))
     {
-      g_datalist_unlock_and_set (datalist, NULL);
-      g_free (d);
+      g_datalist_unlock_and_set (datalist, d);
+      if (d_to_free)
+        g_free (d_to_free);
     }
   else
     g_datalist_unlock (datalist);
 
   if (found_keys > 0)
     {
-      for (gsize i = 0; i < n_keys; i++)
+      for (i_keys = 0; i_keys < n_keys; i_keys++)
         {
-          /* If keys[i] was not found, then old[i].destroy is NULL.
-           * Call old[i].destroy() only if keys[i] was found, and
-           * is associated with a destroy notifier: */
-          if (old[i].destroy)
-            old[i].destroy (old[i].data);
+          if (old[i_keys].destroy)
+            old[i_keys].destroy (old[i_keys].data);
         }
     }
 
@@ -736,13 +796,17 @@ g_datalist_id_set_data_full (GData          **datalist,
  * g_datalist_id_remove_multiple:
  * @datalist: a datalist
  * @keys: (array length=n_keys): keys to remove
- * @n_keys: length of @keys, must be <= 16
+ * @n_keys: length of @keys.
  *
  * Removes multiple keys from a datalist.
  *
  * This is more efficient than calling g_datalist_id_remove_data()
  * multiple times in a row.
  *
+ * Before 2.80, @n_keys had to be not larger than 16. Now it can be larger, but
+ * note that GData does a linear search, so an excessive number of keys will
+ * perform badly.
+ *
  * Since: 2.74
  */
 void
@@ -750,8 +814,6 @@ g_datalist_id_remove_multiple (GData  **datalist,
                                GQuark  *keys,
                                gsize    n_keys)
 {
-  g_return_if_fail (n_keys <= 16);
-
   g_data_remove_internal (datalist, keys, n_keys);
 }
 
@@ -827,6 +889,113 @@ g_datalist_id_remove_no_notify (GData     **datalist,
   return ret_data;
 }
 
+/*< private >
+ * g_datalist_id_update_atomic:
+ * @datalist: the data list
+ * @key_id: the key to add.
+ * @callback: (scope call): callback to update (set, remove, steal, update) the
+ *   data.
+ * @user_data: the user data for @callback.
+ *
+ * Will call @callback while holding the lock on @datalist. Be careful to not
+ * end up calling into another data-list function, because the lock is not
+ * reentrant and deadlock will happen.
+ *
+ * The callback receives the current data and destroy function. If @key_id is
+ * currently not in @datalist, they will be %NULL. The callback can update
+ * those pointers, and @datalist will be updated with the result. Note that if
+ * callback modifies a received data, then it MUST steal it and take ownership
+ * on it. Possibly by freeing it with the provided destroy function.
+ *
+ * The point is to atomically access the entry, while holding a lock
+ * of @datalist. Without this, the user would have to hold their own mutex
+ * while handling @key_id entry.
+ *
+ * The return value of @callback is not used, except it becomes the return
+ * value of the function. This is an alternative to returning a result via
+ * @user_data.
+ *
+ * Returns: the value returned by @callback.
+ *
+ * Since: 2.80
+ */
+gpointer
+g_datalist_id_update_atomic (GData **datalist,
+                             GQuark key_id,
+                             GDataListUpdateAtomicFunc callback,
+                             gpointer user_data)
+{
+  GData *d;
+  GDataElt *data;
+  gpointer new_data;
+  gpointer result;
+  GDestroyNotify new_destroy;
+  guint32 idx;
+  gboolean to_unlock = TRUE;
+
+  d = g_datalist_lock_and_get (datalist);
+
+  data = datalist_find (d, key_id, &idx);
+
+  if (data)
+    {
+      new_data = data->data;
+      new_destroy = data->destroy;
+    }
+  else
+    {
+      new_data = NULL;
+      new_destroy = NULL;
+    }
+
+  result = callback (key_id, &new_data, &new_destroy, user_data);
+
+  if (data && !new_data)
+    {
+      GData *d_to_free;
+
+      /* Remove. The callback indicates to drop the entry.
+       *
+       * The old data->data was stolen by callback(). */
+      datalist_remove (d, idx);
+      if (datalist_shrink (&d, &d_to_free))
+        {
+          g_datalist_unlock_and_set (datalist, d);
+          if (d_to_free)
+            g_free (d_to_free);
+          to_unlock = FALSE;
+        }
+    }
+  else if (data)
+    {
+      /* Update. The callback may have provided new pointers to an existing
+       * entry.
+       *
+       * The old data was stolen by callback(). We only update the pointers and
+       * are done. */
+      data->data = new_data;
+      data->destroy = new_destroy;
+    }
+  else if (!data && !new_data)
+    {
+      /* Absent. No change. The entry didn't exist and still does not. */
+    }
+  else
+    {
+      /* Add. Add a new entry that didn't exist previously. */
+      if (datalist_append (&d, key_id, new_data, new_destroy))
+        {
+          g_datalist_unlock_and_set (datalist, d);
+          to_unlock = FALSE;
+        }
+    }
+
+  if (to_unlock)
+    g_datalist_unlock (datalist);
+
+  return result;
+}
+
 /**
  * g_dataset_id_get_data:
  * @dataset_location: (not nullable): the location identifying the dataset.
@@ -993,10 +1162,9 @@ g_datalist_id_replace_data (GData          **datalist,
 {
   gpointer val = NULL;
   GData *d;
-  GData *new_d = NULL;
   GDataElt *data;
-  gboolean free_d = FALSE;
-  gboolean set_new_d = FALSE;
+  GData *d_to_free = NULL;
+  gboolean set_d = FALSE;
   guint32 idx;
 
   g_return_val_if_fail (datalist != NULL, FALSE);
@@ -1022,18 +1190,9 @@ g_datalist_id_replace_data (GData          **datalist,
             }
           else
             {
-              if (idx != d->len - 1u)
-                *data = d->data[d->len - 1u];
-              d->len--;
-
-              /* We don't bother to shrink, but if all data are now gone
-               * we at least free the memory
-               */
-              if (d->len == 0)
-                {
-                  set_new_d = TRUE;
-                  free_d = TRUE;
-                }
+              datalist_remove (d, idx);
+              if (datalist_shrink (&d, &d_to_free))
+                set_d = TRUE;
             }
         }
     }
@@ -1042,18 +1201,17 @@ g_datalist_id_replace_data (GData          **datalist,
     {
       if (datalist_append (&d, key_id, newval, destroy))
         {
-          new_d = d;
-          set_new_d = TRUE;
+          set_d = TRUE;
         }
     }
 
-  if (set_new_d)
-    g_datalist_unlock_and_set (datalist, new_d);
+  if (set_d)
+    g_datalist_unlock_and_set (datalist, d);
   else
     g_datalist_unlock (datalist);
 
-  if (free_d)
-    g_free (d);
+  if (d_to_free)
+    g_free (d_to_free);
 
   return val == oldval;
 }
@@ -1086,6 +1244,12 @@ g_datalist_get_data (GData        **datalist,
       data_end = data + d->len;
       while (data < data_end)
        {
+         /* Here we intentionally compare by strings, instead of calling
+          * g_quark_try_string() first.
+          *
+          * See commit 1cceda49b60b ('Make g_datalist_get_data not look up the
+          * quark').
+          */
          if (g_strcmp0 (g_quark_to_string (data->key), key) == 0)
            {
              res = data->data;
index f22cf38..c53455a 100644 (file)
@@ -38,6 +38,29 @@ G_BEGIN_DECLS
 #define G_DATALIST_GET_FLAGS(datalist)                         \
   ((gsize) g_atomic_pointer_get (datalist) & G_DATALIST_FLAGS_MASK)
 
+/*< private >
+ * GDataListUpdateAtomicFunc:
+ * @key_id: ID of the entry to update
+ * @data: (inout) (nullable) (not optional): the existing data corresponding
+ *   to @key_id, and return location for the new value for it
+ * @destroy_notify: (inout) (nullable) (not optional): the existing destroy
+ *   notify function for @data, and return location for the destroy notify
+ *   function for the new value for it
+ * @user_data: user data passed in to [func@GLib.datalist_id_update_atomic]
+ *
+ * Callback from [func@GLib.datalist_id_update_atomic].
+ *
+ * Since: 2.80
+ */
+typedef gpointer (*GDataListUpdateAtomicFunc) (GQuark key_id,
+                                               gpointer *data,
+                                               GDestroyNotify *destroy_notify,
+                                               gpointer user_data);
+
+gpointer g_datalist_id_update_atomic (GData **datalist,
+                                      GQuark key_id,
+                                      GDataListUpdateAtomicFunc callback,
+                                      gpointer user_data);
 
 G_END_DECLS
 
index c2b68a0..15dbc66 100644 (file)
@@ -24,6 +24,7 @@
 #include "glib-private.h"
 #include "glib-init.h"
 #include "gutilsprivate.h"
+#include "gdatasetprivate.h"
 
 #ifdef USE_INVALID_PARAMETER_HANDLER
 #include <crtdbg.h>
  * Do not call this function; it is used to share private
  * API between glib, gobject, and gio.
  */
-GLibPrivateVTable *
+const GLibPrivateVTable *
 glib__private__ (void)
 {
-  static GLibPrivateVTable table = {
+  static const GLibPrivateVTable table = {
     g_wakeup_new,
     g_wakeup_free,
     g_wakeup_get_pollfd,
@@ -74,6 +75,8 @@ glib__private__ (void)
     g_uri_get_default_scheme_port,
 
     g_set_prgname_once,
+
+    g_datalist_id_update_atomic,
   };
 
   return &table;
index 479ebb9..c24b59d 100644 (file)
@@ -23,6 +23,7 @@
 #include <glib.h>
 #include "gwakeup.h"
 #include "gstdioprivate.h"
+#include "gdatasetprivate.h"
 
 /* gcc defines __SANITIZE_ADDRESS__, clang sets the address_sanitizer
  * feature flag.
@@ -291,11 +292,16 @@ typedef struct {
   /* See gutils.c */
   gboolean (* g_set_prgname_once) (const gchar *prgname);
 
+  gpointer (*g_datalist_id_update_atomic) (GData **datalist,
+                                           GQuark key_id,
+                                           GDataListUpdateAtomicFunc callback,
+                                           gpointer user_data);
+
   /* Add other private functions here, initialize them in glib-private.c */
 } GLibPrivateVTable;
 
 GLIB_AVAILABLE_IN_ALL
-GLibPrivateVTable *glib__private__ (void);
+const GLibPrivateVTable *glib__private__ (void);
 
 /* Please see following for the use of ".ACP" over ""
  * on Windows, although both are accepted at compile-time
@@ -327,4 +333,8 @@ guint g_uint_hash (gconstpointer v);
 #undef G_THREAD_LOCAL
 #endif
 
+/* Convenience wrapper to call private g_datalist_id_update_atomic() function. */
+#define _g_datalist_id_update_atomic(datalist, key_id, callback, user_data) \
+  (GLIB_PRIVATE_CALL (g_datalist_id_update_atomic) ((datalist), (key_id), (callback), (user_data)))
+
 #endif /* __GLIB_PRIVATE_H__ */
index a7716fb..7cb76dd 100644 (file)
@@ -1,6 +1,20 @@
 /* GLIB - Library of useful routines for C programming
- * Copyright (C) 2011 Red Hat, Inc.
- * Copyright 2023 Collabora Ltd.
+ * Copyright 2000-2022 Red Hat, Inc.
+ * Copyright 2006-2007 Matthias Clasen
+ * Copyright 2006 Padraig O'Briain
+ * Copyright 2007 Lennart Poettering
+ * Copyright 2018-2022 Endless OS Foundation, LLC
+ * Copyright 2018 Peter Wu
+ * Copyright 2019 Ting-Wei Lan
+ * Copyright 2019 Sebastian Schwarz
+ * Copyright 2020 Matt Rose
+ * Copyright 2021 Casper Dik
+ * Copyright 2022 Alexander Richardson
+ * Copyright 2022 Ray Strode
+ * Copyright 2022 Thomas Haller
+ * Copyright 2023-2024 Collabora Ltd.
+ * Copyright 2023 Sebastian Wilhelmi
+ * Copyright 2023 CaiJingLong
  *
  * glib-unix.c: UNIX specific API wrappers and convenience functions
  *
 #include "glib-unixprivate.h"
 #include "gmain-internal.h"
 
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>   /* for fdwalk */
 #include <string.h>
 #include <sys/types.h>
 #include <pwd.h>
+#include <unistd.h>
+
+#if defined(__linux__) || defined(__DragonFly__)
+#include <sys/syscall.h>  /* for syscall and SYS_getdents64 */
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif /* HAVE_SYS_RESOURCE_H */
+
+#if defined(__APPLE__) && defined(HAVE_LIBPROC_H)
+#include <libproc.h>
+#include <sys/proc_info.h>
+#endif
 
 G_STATIC_ASSERT (sizeof (ssize_t) == GLIB_SIZEOF_SSIZE_T);
 G_STATIC_ASSERT (G_ALIGNOF (gssize) == G_ALIGNOF (ssize_t));
@@ -507,3 +539,388 @@ g_unix_get_passwd_entry (const gchar  *user_name,
 
   return (struct passwd *) g_steal_pointer (&buffer);
 }
+
+/* This function is called between fork() and exec() and hence must be
+ * async-signal-safe (see signal-safety(7)). */
+static int
+set_cloexec (void *data, gint fd)
+{
+  if (fd >= GPOINTER_TO_INT (data))
+    fcntl (fd, F_SETFD, FD_CLOEXEC);
+
+  return 0;
+}
+
+/* fdwalk()-compatible callback to close a fd for non-compliant
+ * implementations of fdwalk() that potentially pass already
+ * closed fds.
+ *
+ * It is not an error to pass an invalid fd to this function.
+ *
+ * This function is called between fork() and exec() and hence must be
+ * async-signal-safe (see signal-safety(7)).
+ */
+G_GNUC_UNUSED static int
+close_func_with_invalid_fds (void *data, int fd)
+{
+  /* We use close and not g_close here because on some platforms, we
+   * don't know how to close only valid, open file descriptors, so we
+   * have to pass bad fds to close too. g_close warns if given a bad
+   * fd.
+   *
+   * This function returns no error, because there is nothing that the caller
+   * could do with that information. That is even the case for EINTR. See
+   * g_close() about the specialty of EINTR and why that is correct.
+   * If g_close() ever gets extended to handle EINTR specially, then this place
+   * should get updated to do the same handling.
+   */
+  if (fd >= GPOINTER_TO_INT (data))
+    close (fd);
+
+  return 0;
+}
+
+#ifdef __linux__
+struct linux_dirent64
+{
+  guint64        d_ino;    /* 64-bit inode number */
+  guint64        d_off;    /* 64-bit offset to next structure */
+  unsigned short d_reclen; /* Size of this dirent */
+  unsigned char  d_type;   /* File type */
+  char           d_name[]; /* Filename (null-terminated) */
+};
+
+/* This function is called between fork() and exec() and hence must be
+ * async-signal-safe (see signal-safety(7)). */
+static gint
+filename_to_fd (const char *p)
+{
+  char c;
+  int fd = 0;
+  const int cutoff = G_MAXINT / 10;
+  const int cutlim = G_MAXINT % 10;
+
+  if (*p == '\0')
+    return -1;
+
+  while ((c = *p++) != '\0')
+    {
+      if (c < '0' || c > '9')
+        return -1;
+      c -= '0';
+
+      /* Check for overflow. */
+      if (fd > cutoff || (fd == cutoff && c > cutlim))
+        return -1;
+
+      fd = fd * 10 + c;
+    }
+
+  return fd;
+}
+#endif
+
+static int safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data);
+
+/* This function is called between fork() and exec() and hence must be
+ * async-signal-safe (see signal-safety(7)). */
+static int
+safe_fdwalk (int (*cb)(void *data, int fd), void *data)
+{
+#if 0
+  /* Use fdwalk function provided by the system if it is known to be
+   * async-signal safe.
+   *
+   * Currently there are no operating systems known to provide a safe
+   * implementation, so this section is not used for now.
+   */
+  return fdwalk (cb, data);
+#else
+  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
+   * may fail on non-Linux operating systems. See safe_fdwalk_with_invalid_fds
+   * for a slower alternative.
+   */
+
+#ifdef __linux__
+  gint fd;
+  gint res = 0;
+
+  /* Avoid use of opendir/closedir since these are not async-signal-safe. */
+  int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
+  if (dir_fd >= 0)
+    {
+      /* buf needs to be aligned correctly to receive linux_dirent64.
+       * C11 has _Alignof for this purpose, but for now a
+       * union serves the same purpose. */
+      union
+      {
+        char buf[4096];
+        struct linux_dirent64 alignment;
+      } u;
+      int pos, nread;
+      struct linux_dirent64 *de;
+
+      while ((nread = syscall (SYS_getdents64, dir_fd, u.buf, sizeof (u.buf))) > 0)
+        {
+          for (pos = 0; pos < nread; pos += de->d_reclen)
+            {
+              de = (struct linux_dirent64 *) (u.buf + pos);
+
+              fd = filename_to_fd (de->d_name);
+              if (fd < 0 || fd == dir_fd)
+                  continue;
+
+              if ((res = cb (data, fd)) != 0)
+                  break;
+            }
+        }
+
+      g_close (dir_fd, NULL);
+      return res;
+    }
+
+  /* If /proc is not mounted or not accessible we fail here and rely on
+   * safe_fdwalk_with_invalid_fds to fall back to the old
+   * rlimit trick. */
+
+#endif
+
+#if defined(__sun__) && defined(F_PREVFD) && defined(F_NEXTFD)
+/*
+ * Solaris 11.4 has a signal-safe way which allows
+ * us to find all file descriptors in a process.
+ *
+ * fcntl(fd, F_NEXTFD, maxfd)
+ * - returns the first allocated file descriptor <= maxfd  > fd.
+ *
+ * fcntl(fd, F_PREVFD)
+ * - return highest allocated file descriptor < fd.
+ */
+  gint fd;
+  gint res = 0;
+
+  open_max = fcntl (INT_MAX, F_PREVFD); /* find the maximum fd */
+  if (open_max < 0) /* No open files */
+    return 0;
+
+  for (fd = -1; (fd = fcntl (fd, F_NEXTFD, open_max)) != -1; )
+    if ((res = cb (data, fd)) != 0 || fd == open_max)
+      break;
+
+  return res;
+#endif
+
+  return safe_fdwalk_with_invalid_fds (cb, data);
+#endif
+}
+
+/* This function is called between fork() and exec() and hence must be
+ * async-signal-safe (see signal-safety(7)). */
+static int
+safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data)
+{
+  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
+   * may be slow, especially on systems allowing very high number of open file
+   * descriptors.
+   */
+  gint open_max = -1;
+  gint fd;
+  gint res = 0;
+
+#if 0 && defined(HAVE_SYS_RESOURCE_H)
+  struct rlimit rl;
+
+  /* Use getrlimit() function provided by the system if it is known to be
+   * async-signal safe.
+   *
+   * Currently there are no operating systems known to provide a safe
+   * implementation, so this section is not used for now.
+   */
+  if (getrlimit (RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
+    open_max = rl.rlim_max;
+#endif
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+  /* Use sysconf() function provided by the system if it is known to be
+   * async-signal safe.
+   *
+   * FreeBSD: sysconf() is included in the list of async-signal safe functions
+   * found in https://man.freebsd.org/sigaction(2).
+   *
+   * OpenBSD: sysconf() is included in the list of async-signal safe functions
+   * found in https://man.openbsd.org/sigaction.2.
+   *
+   * Apple: sysconf() is included in the list of async-signal safe functions
+   * found in https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man2/sigaction.2
+   */
+  if (open_max < 0)
+    open_max = sysconf (_SC_OPEN_MAX);
+#endif
+  /* Hardcoded fallback: the default process hard limit in Linux as of 2020 */
+  if (open_max < 0)
+    open_max = 4096;
+
+#if defined(__APPLE__) && defined(HAVE_LIBPROC_H)
+  /* proc_pidinfo isn't documented as async-signal-safe but looking at the implementation
+   * in the darwin tree here:
+   *
+   * https://opensource.apple.com/source/Libc/Libc-498/darwin/libproc.c.auto.html
+   *
+   * It's just a thin wrapper around a syscall, so it's probably okay.
+   */
+  {
+    char buffer[4096 * PROC_PIDLISTFD_SIZE];
+    ssize_t buffer_size;
+
+    buffer_size = proc_pidinfo (getpid (), PROC_PIDLISTFDS, 0, buffer, sizeof (buffer));
+
+    if (buffer_size > 0 &&
+        sizeof (buffer) >= (size_t) buffer_size &&
+        (buffer_size % PROC_PIDLISTFD_SIZE) == 0)
+      {
+        const struct proc_fdinfo *fd_info = (const struct proc_fdinfo *) buffer;
+        size_t number_of_fds = (size_t) buffer_size / PROC_PIDLISTFD_SIZE;
+
+        for (size_t i = 0; i < number_of_fds; i++)
+          if ((res = cb (data, fd_info[i].proc_fd)) != 0)
+            break;
+
+        return res;
+      }
+  }
+#endif
+
+  for (fd = 0; fd < open_max; fd++)
+      if ((res = cb (data, fd)) != 0)
+          break;
+
+  return res;
+}
+
+/**
+ * g_fdwalk_set_cloexec:
+ * @lowfd: Minimum fd to act on, which must be non-negative
+ *
+ * Mark every file descriptor equal to or greater than @lowfd to be closed
+ * at the next `execve()` or similar, as if via the `FD_CLOEXEC` flag.
+ *
+ * Typically @lowfd will be 3, to leave standard input, standard output
+ * and standard error open after exec.
+ *
+ * This is the same as Linux `close_range (lowfd, ~0U, CLOSE_RANGE_CLOEXEC)`,
+ * but portable to other OSs and to older versions of Linux.
+ *
+ * This function is async-signal safe, making it safe to call from a
+ * signal handler or a [callback@GLib.SpawnChildSetupFunc], as long as @lowfd is
+ * non-negative.
+ * See [`signal(7)`](man:signal(7)) and
+ * [`signal-safety(7)`](man:signal-safety(7)) for more details.
+ *
+ * Returns: 0 on success, -1 with errno set on error
+ * Since: 2.80
+ */
+int
+g_fdwalk_set_cloexec (int lowfd)
+{
+  int ret;
+
+  g_return_val_if_fail (lowfd >= 0, (errno = EINVAL, -1));
+
+#if defined(HAVE_CLOSE_RANGE) && defined(CLOSE_RANGE_CLOEXEC)
+  /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at
+   * around the same time. It was designed for use in async-signal-safe
+   * situations: https://bugs.python.org/issue38061
+   *
+   * The `CLOSE_RANGE_CLOEXEC` flag was added in Linux 5.11, and is not yet
+   * present in FreeBSD.
+   *
+   * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
+   * fall back to safe_fdwalk(). Handle EINVAL in case `CLOSE_RANGE_CLOEXEC`
+   * is not supported. */
+  ret = close_range (lowfd, G_MAXUINT, CLOSE_RANGE_CLOEXEC);
+  if (ret == 0 || !(errno == ENOSYS || errno == EINVAL))
+    return ret;
+#endif  /* HAVE_CLOSE_RANGE */
+
+  ret = safe_fdwalk (set_cloexec, GINT_TO_POINTER (lowfd));
+
+  return ret;
+}
+
+/**
+ * g_closefrom:
+ * @lowfd: Minimum fd to close, which must be non-negative
+ *
+ * Close every file descriptor equal to or greater than @lowfd.
+ *
+ * Typically @lowfd will be 3, to leave standard input, standard output
+ * and standard error open.
+ *
+ * This is the same as Linux `close_range (lowfd, ~0U, 0)`,
+ * but portable to other OSs and to older versions of Linux.
+ * Equivalently, it is the same as BSD `closefrom (lowfd)`, but portable,
+ * and async-signal-safe on all OSs.
+ *
+ * This function is async-signal safe, making it safe to call from a
+ * signal handler or a [callback@GLib.SpawnChildSetupFunc], as long as @lowfd is
+ * non-negative.
+ * See [`signal(7)`](man:signal(7)) and
+ * [`signal-safety(7)`](man:signal-safety(7)) for more details.
+ *
+ * Returns: 0 on success, -1 with errno set on error
+ * Since: 2.80
+ */
+int
+g_closefrom (int lowfd)
+{
+  int ret;
+
+  g_return_val_if_fail (lowfd >= 0, (errno = EINVAL, -1));
+
+#if defined(HAVE_CLOSE_RANGE)
+  /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at
+   * around the same time. It was designed for use in async-signal-safe
+   * situations: https://bugs.python.org/issue38061
+   *
+   * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
+   * fall back to safe_fdwalk(). */
+  ret = close_range (lowfd, G_MAXUINT, 0);
+  if (ret == 0 || errno != ENOSYS)
+    return ret;
+#endif  /* HAVE_CLOSE_RANGE */
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || \
+  (defined(__sun__) && defined(F_CLOSEFROM))
+  /* Use closefrom function provided by the system if it is known to be
+   * async-signal safe.
+   *
+   * FreeBSD: closefrom is included in the list of async-signal safe functions
+   * found in https://man.freebsd.org/sigaction(2).
+   *
+   * OpenBSD: closefrom is not included in the list, but a direct system call
+   * should be safe to use.
+   *
+   * In Solaris as of 11.3 SRU 31, closefrom() is also a direct system call.
+   * On such systems, F_CLOSEFROM is defined.
+   */
+  (void) closefrom (lowfd);
+  return 0;
+#elif defined(__DragonFly__)
+  /* It is unclear whether closefrom function included in DragonFlyBSD libc_r
+   * is safe to use because it calls a lot of library functions. It is also
+   * unclear whether libc_r itself is still being used. Therefore, we do a
+   * direct system call here ourselves to avoid possible issues.
+   */
+  (void) syscall (SYS_closefrom, lowfd);
+  return 0;
+#elif defined(F_CLOSEM)
+  /* NetBSD and AIX have a special fcntl command which does the same thing as
+   * closefrom. NetBSD also includes closefrom function, which seems to be a
+   * simple wrapper of the fcntl command.
+   */
+  return fcntl (lowfd, F_CLOSEM);
+#else
+  ret = safe_fdwalk (close_func_with_invalid_fds, GINT_TO_POINTER (lowfd));
+
+  return ret;
+#endif
+}
index c58769e..bccbea5 100644 (file)
@@ -173,7 +173,7 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
 /**
  * g_unix_pipe_open:
  * @self: A pair of file descriptors
- * @flags: Flags to pass to g_unix_open_pipe(), typically `FD_CLOEXEC`
+ * @flags: Flags to pass to g_unix_open_pipe(), typically `O_CLOEXEC`
  * @error: Used to report an error on failure
  *
  * Open a pipe. This is the same as g_unix_open_pipe(), but uses the
@@ -326,6 +326,12 @@ g_unix_pipe_clear (GUnixPipe *self)
 
 G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (GUnixPipe, g_unix_pipe_clear)
 
+GLIB_AVAILABLE_IN_2_80
+int g_closefrom (int lowfd);
+
+GLIB_AVAILABLE_IN_2_80
+int g_fdwalk_set_cloexec (int lowfd);
+
 G_GNUC_END_IGNORE_DEPRECATIONS
 
 G_END_DECLS
index 68af202..bff9cad 100644 (file)
@@ -86,6 +86,7 @@ int main()
         long double y = frexpl (x, &exp);
         /* On machines with IEEE854 arithmetic: x = 1.68105e-4932,
            exp = -16382, y = 0.5.  On Mac OS X 10.5: exp = -16384, y = 0.5.  */
+        (void) y;
         if (exp != LDBL_MIN_EXP - 1)
           result |= 8;
       }
index 3283c98..6cb2964 100644 (file)
@@ -16,27 +16,27 @@ static double zero = 0.0;
 int main ()
 {
   int result = 0;
-  if (sprintf (buf, "%a %d", 3.1416015625, 33, 44, 55) < 0
+  if (sprintf (buf, "%a %d", 3.1416015625, 33) < 0
       || (strcmp (buf, "0x1.922p+1 33") != 0
           && strcmp (buf, "0x3.244p+0 33") != 0
           && strcmp (buf, "0x6.488p-1 33") != 0
           && strcmp (buf, "0xc.91p-2 33") != 0))
     result |= 1;
-  if (sprintf (buf, "%A %d", -3.1416015625, 33, 44, 55) < 0
+  if (sprintf (buf, "%A %d", -3.1416015625, 33) < 0
       || (strcmp (buf, "-0X1.922P+1 33") != 0
           && strcmp (buf, "-0X3.244P+0 33") != 0
           && strcmp (buf, "-0X6.488P-1 33") != 0
           && strcmp (buf, "-0XC.91P-2 33") != 0))
     result |= 2;
   /* This catches a FreeBSD 6.1 bug: it doesn't round.  */
-  if (sprintf (buf, "%.2a %d", 1.51, 33, 44, 55) < 0
+  if (sprintf (buf, "%.2a %d", 1.51, 33) < 0
       || (strcmp (buf, "0x1.83p+0 33") != 0
           && strcmp (buf, "0x3.05p-1 33") != 0
           && strcmp (buf, "0x6.0ap-2 33") != 0
           && strcmp (buf, "0xc.14p-3 33") != 0))
     result |= 4;
   /* This catches a Mac OS X 10.12.4 (Darwin 16.5) bug: it doesn't round.  */
-  if (sprintf (buf, "%.0a %d", 1.51, 33, 44, 55) < 0
+  if (sprintf (buf, "%.0a %d", 1.51, 33) < 0
       || (strcmp (buf, "0x2p+0 33") != 0
           && strcmp (buf, "0x3p-1 33") != 0
           && strcmp (buf, "0x6p-2 33") != 0
@@ -44,7 +44,7 @@ int main ()
     result |= 4;
   /* This catches a FreeBSD 6.1 bug.  See
      <https://lists.gnu.org/r/bug-gnulib/2007-04/msg00107.html> */
-  if (sprintf (buf, "%010a %d", 1.0 / zero, 33, 44, 55) < 0
+  if (sprintf (buf, "%010a %d", 1.0 / zero, 33) < 0
       || buf[0] == '0')
     result |= 8;
   /* This catches a Mac OS X 10.3.9 (Darwin 7.9) bug.  */
index afde78b..5137e98 100644 (file)
@@ -15,7 +15,7 @@ static double zero = 0.0;
 int main ()
 {
   int result = 0;
-  if (sprintf (buf, "%F %d", 1234567.0, 33, 44, 55) < 0
+  if (sprintf (buf, "%F %d", 1234567.0, 33) < 0
       || strcmp (buf, "1234567.000000 33") != 0)
     result |= 1;
   if (sprintf (buf, "%F", 1.0 / zero) < 0
index 2cb00a8..eea5dbb 100644 (file)
@@ -15,7 +15,7 @@ static char buf[100];
 static double zero = 0.0;
 int main ()
 {
-  if (sprintf (buf, "%010f", 1.0 / zero, 33, 44, 55) < 0
+  if (sprintf (buf, "%010f", 1.0 / zero) < 0
       || (strcmp (buf, "       inf") != 0
           && strcmp (buf, "  infinity") != 0))
     return 1;
index 3d0e019..2659ca3 100644 (file)
@@ -15,15 +15,15 @@ int main ()
 {
   int result = 0;
   buf[0] = '\0';
-  if (sprintf (buf, "%Lf %d", 1.75L, 33, 44, 55) < 0
+  if (sprintf (buf, "%Lf %d", 1.75L, 33) < 0
       || strcmp (buf, "1.750000 33") != 0)
     result |= 1;
   buf[0] = '\0';
-  if (sprintf (buf, "%Le %d", 1.75L, 33, 44, 55) < 0
+  if (sprintf (buf, "%Le %d", 1.75L, 33) < 0
       || strcmp (buf, "1.750000e+00 33") != 0)
     result |= 2;
   buf[0] = '\0';
-  if (sprintf (buf, "%Lg %d", 1.75L, 33, 44, 55) < 0
+  if (sprintf (buf, "%Lg %d", 1.75L, 33) < 0
       || strcmp (buf, "1.75 33") != 0)
     result |= 4;
   return result;
index 35290c5..bd39094 100644 (file)
@@ -23,14 +23,14 @@ int main ()
   /* On BeOS, this would crash and show a dialog box.  Avoid the crash.  */
   return 1;
 #endif
-  if (sprintf (buf, "%.4000d %d", 1, 33, 44) < 4000 + 3)
+  if (sprintf (buf, "%.4000d %d", 1, 33) < 4000 + 3)
     result |= 1;
-  if (sprintf (buf, "%.4000f %d", 1.0, 33, 44) < 4000 + 5)
+  if (sprintf (buf, "%.4000f %d", 1.0, 33) < 4000 + 5)
     result |= 2;
-  if (sprintf (buf, "%.511f %d", 1.0, 33, 44) < 511 + 5
+  if (sprintf (buf, "%.511f %d", 1.0, 33) < 511 + 5
       || buf[0] != '1')
     result |= 4;
-  if (sprintf (buf, "%.999f %d", 1.0, 33, 44) < 999 + 5
+  if (sprintf (buf, "%.999f %d", 1.0, 33) < 999 + 5
       || buf[0] != '1')
     result |= 4;
   return result;
index 7cf30d2..c9a9914 100644 (file)
 
 /**
  * g_printf:
- * @format: a standard printf() format string, but notice 
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @...: the arguments to insert in the output.
+ * @format: a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @...: the arguments to insert in the output
  *
- * An implementation of the standard printf() function which supports 
+ * An implementation of the standard `printf()` function which supports
  * positional parameters, as specified in the Single Unix Specification.
  *
- * As with the standard printf(), this does not automatically append a trailing
+ * As with the standard `printf()`, this does not automatically append a trailing
  * new-line character to the message, so typically @format should end with its
  * own new-line character.
  *
  * `glib/gprintf.h` must be explicitly included in order to use this function.
  *
- * Returns: the number of bytes printed.
+ * Returns: the number of bytes printed
  *
  * Since: 2.2
  **/
@@ -63,17 +63,17 @@ g_printf (gchar const *format,
 
 /**
  * g_fprintf:
- * @file: (not nullable): the stream to write to.
- * @format: a standard printf() format string, but notice 
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @...: the arguments to insert in the output.
+ * @file: (not nullable): the stream to write to
+ * @format: a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @...: the arguments to insert in the output
  *
- * An implementation of the standard fprintf() function which supports 
+ * An implementation of the standard `fprintf()` function which supports
  * positional parameters, as specified in the Single Unix Specification.
  *
  * `glib/gprintf.h` must be explicitly included in order to use this function.
  *
- * Returns: the number of bytes printed.
+ * Returns: the number of bytes printed
  *
  * Since: 2.2
  **/
@@ -95,23 +95,23 @@ g_fprintf (FILE        *file,
 /**
  * g_sprintf:
  * @string: A pointer to a memory buffer to contain the resulting string. It
- *          is up to the caller to ensure that the allocated buffer is large
- *          enough to hold the formatted result
- * @format: a standard printf() format string, but notice
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @...: the arguments to insert in the output.
+ *   is up to the caller to ensure that the allocated buffer is large
+ *   enough to hold the formatted result.
+ * @format: a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @...: the arguments to insert in the output
  *
- * An implementation of the standard sprintf() function which supports
+ * An implementation of the standard `sprintf()` function which supports
  * positional parameters, as specified in the Single Unix Specification.
  *
- * Note that it is usually better to use g_snprintf(), to avoid the
+ * Note that it is usually better to use [func@GLib.snprintf], to avoid the
  * risk of buffer overflow.
  *
  * `glib/gprintf.h` must be explicitly included in order to use this function.
  *
- * See also g_strdup_printf().
+ * See also [func@GLib.strdup_printf].
  *
- * Returns: the number of bytes printed.
+ * Returns: the number of bytes printed
  *
  * Since: 2.2
  **/
@@ -132,18 +132,18 @@ g_sprintf (gchar       *string,
 
 /**
  * g_snprintf:
- * @string: the buffer to hold the output.
+ * @string: the buffer to hold the output
  * @n: the maximum number of bytes to produce (including the
- *     terminating nul character).
- * @format: a standard printf() format string, but notice
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @...: the arguments to insert in the output.
+ *   terminating nul character)
+ * @format: a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @...: the arguments to insert in the output
  *
  * A safer form of the standard sprintf() function. The output is guaranteed
  * to not exceed @n characters (including the terminating nul character), so
  * it is easy to ensure that a buffer overflow cannot occur.
  *
- * See also g_strdup_printf().
+ * See also [func@GLib.strdup_printf].
  *
  * In versions of GLib prior to 1.2.3, this function may return -1 if the
  * output was truncated, and the truncated string may not be nul-terminated.
@@ -152,13 +152,13 @@ g_sprintf (gchar       *string,
  *
  * The return value of g_snprintf() conforms to the snprintf()
  * function as standardized in ISO C99. Note that this is different from
- * traditional snprintf(), which returns the length of the output string.
+ * traditional `snprintf()`, which returns the length of the output string.
  *
  * The format string may contain positional parameters, as specified in
  * the Single Unix Specification.
  *
- * Returns: the number of bytes which would be produced if the buffer 
- *     was large enough.
+ * Returns: the number of bytes which would be produced if the buffer
+ *   was large enough
  **/
 gint
 g_snprintf (gchar      *string,
@@ -178,16 +178,16 @@ g_snprintf (gchar *string,
 
 /**
  * g_vprintf:
- * @format: a standard printf() format string, but notice 
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @args: the list of arguments to insert in the output.
+ * @format: a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @args: the list of arguments to insert in the output
  *
- * An implementation of the standard vprintf() function which supports 
+ * An implementation of the standard `vprintf()` function which supports
  * positional parameters, as specified in the Single Unix Specification.
  *
  * `glib/gprintf.h` must be explicitly included in order to use this function.
  *
- * Returns: the number of bytes printed.
+ * Returns: the number of bytes printed
  *
  * Since: 2.2
  **/
@@ -202,17 +202,17 @@ g_vprintf (gchar const *format,
 
 /**
  * g_vfprintf:
- * @file: (not nullable): the stream to write to.
- * @format: a standard printf() format string, but notice 
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @args: the list of arguments to insert in the output.
+ * @file: (not nullable): the stream to write to
+ * @format: a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @args: the list of arguments to insert in the output
  *
- * An implementation of the standard fprintf() function which supports 
+ * An implementation of the standard `fprintf()` function which supports
  * positional parameters, as specified in the Single Unix Specification.
  *
  * `glib/gprintf.h` must be explicitly included in order to use this function.
  *
- * Returns: the number of bytes printed.
+ * Returns: the number of bytes printed
  *
  * Since: 2.2
  **/
@@ -228,17 +228,17 @@ g_vfprintf (FILE        *file,
 
 /**
  * g_vsprintf:
- * @string: the buffer to hold the output.
- * @format: a standard printf() format string, but notice 
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @args: the list of arguments to insert in the output.
+ * @string: the buffer to hold the output
+ * @format: a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @args: the list of arguments to insert in the output
  *
- * An implementation of the standard vsprintf() function which supports 
+ * An implementation of the standard `vsprintf()` function which supports
  * positional parameters, as specified in the Single Unix Specification.
  *
  * `glib/gprintf.h` must be explicitly included in order to use this function.
  *
- * Returns: the number of bytes printed.
+ * Returns: the number of bytes printed
  *
  * Since: 2.2
  **/
@@ -253,35 +253,35 @@ g_vsprintf (gchar  *string,
   return _g_vsprintf (string, format, args);
 }
 
-/** 
+/**
  * g_vsnprintf:
- * @string: the buffer to hold the output.
- * @n: the maximum number of bytes to produce (including the 
- *     terminating nul character).
- * @format: a standard printf() format string, but notice 
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @args: the list of arguments to insert in the output.
- *
- * A safer form of the standard vsprintf() function. The output is guaranteed
- * to not exceed @n characters (including the terminating nul character), so 
+ * @string: the buffer to hold the output
+ * @n: the maximum number of bytes to produce (including the
+ *   terminating nul character)
+ * @format: a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @args: the list of arguments to insert in the output
+ *
+ * A safer form of the standard `vsprintf()` function. The output is guaranteed
+ * to not exceed @n characters (including the terminating nul character), so
  * it is easy to ensure that a buffer overflow cannot occur.
  *
- * See also g_strdup_vprintf().
+ * See also [func@GLib.strdup_vprintf].
  *
- * In versions of GLib prior to 1.2.3, this function may return -1 if the 
+ * In versions of GLib prior to 1.2.3, this function may return -1 if the
  * output was truncated, and the truncated string may not be nul-terminated.
- * In versions prior to 1.3.12, this function returns the length of the output 
+ * In versions prior to 1.3.12, this function returns the length of the output
  * string.
  *
- * The return value of g_vsnprintf() conforms to the vsnprintf() function 
- * as standardized in ISO C99. Note that this is different from traditional 
- * vsnprintf(), which returns the length of the output string.
+ * The return value of `g_vsnprintf()` conforms to the `vsnprintf()` function
+ * as standardized in ISO C99. Note that this is different from traditional
+ * `vsnprintf()`, which returns the length of the output string.
  *
- * The format string may contain positional parameters, as specified in 
+ * The format string may contain positional parameters, as specified in
  * the Single Unix Specification.
  *
- * Returns: the number of bytes which would be produced if the buffer 
- *  was large enough.
+ * Returns: the number of bytes which would be produced if the buffer
+ *  was large enough
  */
 gint
 g_vsnprintf (gchar      *string,
@@ -297,25 +297,26 @@ g_vsnprintf (gchar         *string,
 
 /**
  * g_vasprintf:
- * @string: (not optional) (nullable): the return location for the newly-allocated string,
- *   which will be %NULL if (and only if) this function fails
- * @format: (not nullable): a standard printf() format string, but notice
- *          [string precision pitfalls](string-utils.html#string-precision-pitfalls)
- * @args: the list of arguments to insert in the output.
- *
- * An implementation of the GNU vasprintf() function which supports 
+ * @string: (out) (not optional) (nullable): the return location for the
+ *   newly-allocated string, which will be `NULL` if (and only if)
+ *   this function fails
+ * @format: (not nullable): a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @args: the list of arguments to insert in the output
+ *
+ * An implementation of the GNU `vasprintf()` function which supports
  * positional parameters, as specified in the Single Unix Specification.
- * This function is similar to g_vsprintf(), except that it allocates a 
- * string to hold the output, instead of putting the output in a buffer 
+ * This function is similar to [func@GLib.vsprintf], except that it allocates a
+ * string to hold the output, instead of putting the output in a buffer
  * you allocate in advance.
  *
- * The returned value in @string is guaranteed to be non-NULL, unless
+ * The returned value in @string is guaranteed to be non-`NULL`, unless
  * @format contains `%lc` or `%ls` conversions, which can fail if no
  * multibyte representation is available for the given character.
  *
  * `glib/gprintf.h` must be explicitly included in order to use this function.
  *
- * Returns: the number of bytes printed, or `-1` on failure
+ * Returns: the number of bytes printed, or -1 on failure
  *
  * Since: 2.4
  **/
index eb67786..cfa528e 100644 (file)
@@ -107,10 +107,12 @@ gint64   g_slice_get_config          (GSliceConfig ckey);
 GLIB_DEPRECATED_IN_2_34
 gint64*  g_slice_get_config_state  (GSliceConfig ckey, gint64 address, guint *n_values);
 
+#ifndef __GI_SCANNER__
 #ifdef G_ENABLE_DEBUG
 GLIB_AVAILABLE_IN_ALL
 void     g_slice_debug_tree_statistics (void);
 #endif
+#endif
 
 G_END_DECLS
 
index 0720cc3..0ddd532 100644 (file)
@@ -1194,13 +1194,10 @@ write_err_and_exit (gint fd, gint msg)
 
 /* This function is called between fork() and exec() and hence must be
  * async-signal-safe (see signal-safety(7)). */
-static int
-set_cloexec (void *data, gint fd)
+static void
+set_cloexec (int fd)
 {
-  if (fd >= GPOINTER_TO_INT (data))
-    fcntl (fd, F_SETFD, FD_CLOEXEC);
-
-  return 0;
+  fcntl (fd, F_SETFD, FD_CLOEXEC);
 }
 
 /* This function is called between fork() and exec() and hence must be
@@ -1265,337 +1262,6 @@ dupfd_cloexec (int old_fd, int new_fd_min)
   return fd;
 }
 
-/* fdwalk()-compatible callback to close a fd for non-compliant
- * implementations of fdwalk() that potentially pass already
- * closed fds.
- *
- * It is not an error to pass an invalid fd to this function.
- *
- * This function is called between fork() and exec() and hence must be
- * async-signal-safe (see signal-safety(7)).
- */
-G_GNUC_UNUSED static int
-close_func_with_invalid_fds (void *data, int fd)
-{
-  /* We use close and not g_close here because on some platforms, we
-   * don't know how to close only valid, open file descriptors, so we
-   * have to pass bad fds to close too. g_close warns if given a bad
-   * fd.
-   *
-   * This function returns no error, because there is nothing that the caller
-   * could do with that information. That is even the case for EINTR. See
-   * g_close() about the specialty of EINTR and why that is correct.
-   * If g_close() ever gets extended to handle EINTR specially, then this place
-   * should get updated to do the same handling.
-   */
-  if (fd >= GPOINTER_TO_INT (data))
-    close (fd);
-
-  return 0;
-}
-
-#ifdef __linux__
-struct linux_dirent64
-{
-  guint64        d_ino;    /* 64-bit inode number */
-  guint64        d_off;    /* 64-bit offset to next structure */
-  unsigned short d_reclen; /* Size of this dirent */
-  unsigned char  d_type;   /* File type */
-  char           d_name[]; /* Filename (null-terminated) */
-};
-
-/* This function is called between fork() and exec() and hence must be
- * async-signal-safe (see signal-safety(7)). */
-static gint
-filename_to_fd (const char *p)
-{
-  char c;
-  int fd = 0;
-  const int cutoff = G_MAXINT / 10;
-  const int cutlim = G_MAXINT % 10;
-
-  if (*p == '\0')
-    return -1;
-
-  while ((c = *p++) != '\0')
-    {
-      if (c < '0' || c > '9')
-        return -1;
-      c -= '0';
-
-      /* Check for overflow. */
-      if (fd > cutoff || (fd == cutoff && c > cutlim))
-        return -1;
-
-      fd = fd * 10 + c;
-    }
-
-  return fd;
-}
-#endif
-
-static int safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data);
-
-/* This function is called between fork() and exec() and hence must be
- * async-signal-safe (see signal-safety(7)). */
-static int
-safe_fdwalk (int (*cb)(void *data, int fd), void *data)
-{
-#if 0
-  /* Use fdwalk function provided by the system if it is known to be
-   * async-signal safe.
-   *
-   * Currently there are no operating systems known to provide a safe
-   * implementation, so this section is not used for now.
-   */
-  return fdwalk (cb, data);
-#else
-  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
-   * may fail on non-Linux operating systems. See safe_fdwalk_with_invalid_fds
-   * for a slower alternative.
-   */
-
-#ifdef __linux__
-  gint fd;
-  gint res = 0;
-
-  /* Avoid use of opendir/closedir since these are not async-signal-safe. */
-  int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
-  if (dir_fd >= 0)
-    {
-      /* buf needs to be aligned correctly to receive linux_dirent64.
-       * C11 has _Alignof for this purpose, but for now a
-       * union serves the same purpose. */
-      union
-      {
-        char buf[4096];
-        struct linux_dirent64 alignment;
-      } u;
-      int pos, nread;
-      struct linux_dirent64 *de;
-
-      while ((nread = syscall (SYS_getdents64, dir_fd, u.buf, sizeof (u.buf))) > 0)
-        {
-          for (pos = 0; pos < nread; pos += de->d_reclen)
-            {
-              de = (struct linux_dirent64 *) (u.buf + pos);
-
-              fd = filename_to_fd (de->d_name);
-              if (fd < 0 || fd == dir_fd)
-                  continue;
-
-              if ((res = cb (data, fd)) != 0)
-                  break;
-            }
-        }
-
-      g_close (dir_fd, NULL);
-      return res;
-    }
-
-  /* If /proc is not mounted or not accessible we fail here and rely on
-   * safe_fdwalk_with_invalid_fds to fall back to the old
-   * rlimit trick. */
-
-#endif
-
-#if defined(__sun__) && defined(F_PREVFD) && defined(F_NEXTFD)
-/*
- * Solaris 11.4 has a signal-safe way which allows
- * us to find all file descriptors in a process.
- *
- * fcntl(fd, F_NEXTFD, maxfd)
- * - returns the first allocated file descriptor <= maxfd  > fd.
- *
- * fcntl(fd, F_PREVFD)
- * - return highest allocated file descriptor < fd.
- */
-  gint fd;
-  gint res = 0;
-
-  open_max = fcntl (INT_MAX, F_PREVFD); /* find the maximum fd */
-  if (open_max < 0) /* No open files */
-    return 0;
-
-  for (fd = -1; (fd = fcntl (fd, F_NEXTFD, open_max)) != -1; )
-    if ((res = cb (data, fd)) != 0 || fd == open_max)
-      break;
-
-  return res;
-#endif
-
-  return safe_fdwalk_with_invalid_fds (cb, data);
-#endif
-}
-
-/* This function is called between fork() and exec() and hence must be
- * async-signal-safe (see signal-safety(7)). */
-static int
-safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data)
-{
-  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
-   * may be slow, especially on systems allowing very high number of open file
-   * descriptors.
-   */
-  gint open_max = -1;
-  gint fd;
-  gint res = 0;
-
-#if 0 && defined(HAVE_SYS_RESOURCE_H)
-  struct rlimit rl;
-
-  /* Use getrlimit() function provided by the system if it is known to be
-   * async-signal safe.
-   *
-   * Currently there are no operating systems known to provide a safe
-   * implementation, so this section is not used for now.
-   */
-  if (getrlimit (RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
-    open_max = rl.rlim_max;
-#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
-  /* Use sysconf() function provided by the system if it is known to be
-   * async-signal safe.
-   *
-   * FreeBSD: sysconf() is included in the list of async-signal safe functions
-   * found in https://man.freebsd.org/sigaction(2).
-   *
-   * OpenBSD: sysconf() is included in the list of async-signal safe functions
-   * found in https://man.openbsd.org/sigaction.2.
-   * 
-   * Apple: sysconf() is included in the list of async-signal safe functions
-   * found in https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man2/sigaction.2
-   */
-  if (open_max < 0)
-    open_max = sysconf (_SC_OPEN_MAX);
-#endif
-  /* Hardcoded fallback: the default process hard limit in Linux as of 2020 */
-  if (open_max < 0)
-    open_max = 4096;
-
-#if defined(__APPLE__) && defined(HAVE_LIBPROC_H)
-  /* proc_pidinfo isn't documented as async-signal-safe but looking at the implementation
-   * in the darwin tree here:
-   *
-   * https://opensource.apple.com/source/Libc/Libc-498/darwin/libproc.c.auto.html
-   *
-   * It's just a thin wrapper around a syscall, so it's probably okay.
-   */
-  {
-    char buffer[4096 * PROC_PIDLISTFD_SIZE];
-    ssize_t buffer_size;
-
-    buffer_size = proc_pidinfo (getpid (), PROC_PIDLISTFDS, 0, buffer, sizeof (buffer));
-
-    if (buffer_size > 0 &&
-        sizeof (buffer) >= (size_t) buffer_size &&
-        (buffer_size % PROC_PIDLISTFD_SIZE) == 0)
-      {
-        const struct proc_fdinfo *fd_info = (const struct proc_fdinfo *) buffer;
-        size_t number_of_fds = (size_t) buffer_size / PROC_PIDLISTFD_SIZE;
-
-        for (size_t i = 0; i < number_of_fds; i++)
-          if ((res = cb (data, fd_info[i].proc_fd)) != 0)
-            break;
-
-        return res;
-      }
-  }
-#endif
-
-  for (fd = 0; fd < open_max; fd++)
-      if ((res = cb (data, fd)) != 0)
-          break;
-
-  return res;
-}
-
-/* This function is called between fork() and exec() and hence must be
- * async-signal-safe (see signal-safety(7)). */
-static int
-safe_fdwalk_set_cloexec (int lowfd)
-{
-  int ret;
-
-#if defined(HAVE_CLOSE_RANGE) && defined(CLOSE_RANGE_CLOEXEC)
-  /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at
-   * around the same time. It was designed for use in async-signal-safe
-   * situations: https://bugs.python.org/issue38061
-   *
-   * The `CLOSE_RANGE_CLOEXEC` flag was added in Linux 5.11, and is not yet
-   * present in FreeBSD.
-   *
-   * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
-   * fall back to safe_fdwalk(). Handle EINVAL in case `CLOSE_RANGE_CLOEXEC`
-   * is not supported. */
-  ret = close_range (lowfd, G_MAXUINT, CLOSE_RANGE_CLOEXEC);
-  if (ret == 0 || !(errno == ENOSYS || errno == EINVAL))
-    return ret;
-#endif  /* HAVE_CLOSE_RANGE */
-
-  ret = safe_fdwalk (set_cloexec, GINT_TO_POINTER (lowfd));
-
-  return ret;
-}
-
-/* This function is called between fork() and exec() and hence must be
- * async-signal-safe (see signal-safety(7)).
- *
- * On failure, `-1` will be returned and errno will be set. */
-static int
-safe_closefrom (int lowfd)
-{
-  int ret;
-
-#if defined(HAVE_CLOSE_RANGE)
-  /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at
-   * around the same time. It was designed for use in async-signal-safe
-   * situations: https://bugs.python.org/issue38061
-   *
-   * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
-   * fall back to safe_fdwalk(). */
-  ret = close_range (lowfd, G_MAXUINT, 0);
-  if (ret == 0 || errno != ENOSYS)
-    return ret;
-#endif  /* HAVE_CLOSE_RANGE */
-
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || \
-  (defined(__sun__) && defined(F_CLOSEFROM))
-  /* Use closefrom function provided by the system if it is known to be
-   * async-signal safe.
-   *
-   * FreeBSD: closefrom is included in the list of async-signal safe functions
-   * found in https://man.freebsd.org/sigaction(2).
-   *
-   * OpenBSD: closefrom is not included in the list, but a direct system call
-   * should be safe to use.
-   *
-   * In Solaris as of 11.3 SRU 31, closefrom() is also a direct system call.
-   * On such systems, F_CLOSEFROM is defined.
-   */
-  (void) closefrom (lowfd);
-  return 0;
-#elif defined(__DragonFly__)
-  /* It is unclear whether closefrom function included in DragonFlyBSD libc_r
-   * is safe to use because it calls a lot of library functions. It is also
-   * unclear whether libc_r itself is still being used. Therefore, we do a
-   * direct system call here ourselves to avoid possible issues.
-   */
-  (void) syscall (SYS_closefrom, lowfd);
-  return 0;
-#elif defined(F_CLOSEM)
-  /* NetBSD and AIX have a special fcntl command which does the same thing as
-   * closefrom. NetBSD also includes closefrom function, which seems to be a
-   * simple wrapper of the fcntl command.
-   */
-  return fcntl (lowfd, F_CLOSEM);
-#else
-  ret = safe_fdwalk (close_func_with_invalid_fds, GINT_TO_POINTER (lowfd));
-
-  return ret;
-#endif
-}
-
 /* This function is called between fork() and exec() and hence must be
  * async-signal-safe (see signal-safety(7)). */
 static gint
@@ -1727,7 +1393,7 @@ do_exec (gint                  child_err_report_fd,
         write_err_and_exit (child_err_report_fd,
                             CHILD_DUPFD_FAILED);
 
-      set_cloexec (GINT_TO_POINTER(0), stdin_fd);
+      set_cloexec (stdin_fd);
     }
   else if (!child_inherits_stdin)
     {
@@ -1765,7 +1431,7 @@ do_exec (gint                  child_err_report_fd,
         write_err_and_exit (child_err_report_fd,
                             CHILD_DUPFD_FAILED);
 
-      set_cloexec (GINT_TO_POINTER(0), stdout_fd);
+      set_cloexec (stdout_fd);
     }
   else if (stdout_to_null)
     {
@@ -1797,7 +1463,7 @@ do_exec (gint                  child_err_report_fd,
         write_err_and_exit (child_err_report_fd,
                             CHILD_DUPFD_FAILED);
 
-      set_cloexec (GINT_TO_POINTER(0), stderr_fd);
+      set_cloexec (stderr_fd);
     }
   else if (stderr_to_null)
     {
@@ -1822,21 +1488,21 @@ do_exec (gint                  child_err_report_fd,
         {
           if (safe_dup2 (child_err_report_fd, 3) < 0)
             write_err_and_exit (child_err_report_fd, CHILD_DUPFD_FAILED);
-          set_cloexec (GINT_TO_POINTER (0), 3);
-          if (safe_closefrom (4) < 0)
+          set_cloexec (3);
+          if (g_closefrom (4) < 0)
             write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED);
           child_err_report_fd = 3;
         }
       else
         {
-          if (safe_fdwalk_set_cloexec (3) < 0)
+          if (g_fdwalk_set_cloexec (3) < 0)
             write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED);
         }
     }
   else
     {
       /* We need to do child_err_report_fd anyway */
-      set_cloexec (GINT_TO_POINTER (0), child_err_report_fd);
+      set_cloexec (child_err_report_fd);
     }
 
   /*
index 8dd722a..c6faa03 100644 (file)
  *
  * Determines whether a character is alphanumeric.
  *
- * Unlike the standard C library isalnum() function, this only
+ * Unlike the standard C library `isalnum()` function, this only
  * recognizes standard ASCII letters and ignores the locale,
- * returning %FALSE for all non-ASCII characters. Also, unlike
- * the standard library function, this takes a char, not an int,
- * so don't call it on %EOF, but no need to cast to #guchar before
+ * returning false for all non-ASCII characters. Also, unlike
+ * the standard library function, this takes a `char`, not an `int`,
+ * so don't call it on `EOF`, but no need to cast to `guchar` before
  * passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII alphanumeric character
+ * Returns: true if @c is an ASCII alphanumeric character
  */
 
 /**
  *
  * Determines whether a character is alphabetic (i.e. a letter).
  *
- * Unlike the standard C library isalpha() function, this only
+ * Unlike the standard C library `isalpha()` function, this only
  * recognizes standard ASCII letters and ignores the locale,
- * returning %FALSE for all non-ASCII characters. Also, unlike
- * the standard library function, this takes a char, not an int,
- * so don't call it on %EOF, but no need to cast to #guchar before
+ * returning false for all non-ASCII characters. Also, unlike
+ * the standard library function, this takes a `char`, not an `int`,
+ * so don't call it on `EOF`, but no need to cast to `guchar` before
  * passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII alphabetic character
+ * Returns: true if @c is an ASCII alphabetic character
  */
 
 /**
  *
  * Determines whether a character is a control character.
  *
- * Unlike the standard C library iscntrl() function, this only
+ * Unlike the standard C library `iscntrl()` function, this only
  * recognizes standard ASCII control characters and ignores the
- * locale, returning %FALSE for all non-ASCII characters. Also,
- * unlike the standard library function, this takes a char, not
- * an int, so don't call it on %EOF, but no need to cast to #guchar
+ * locale, returning false for all non-ASCII characters. Also,
+ * unlike the standard library function, this takes a `char`, not
+ * an `int`, so don't call it on `EOF`, but no need to cast to `guchar`
  * before passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII control character.
+ * Returns: true if @c is an ASCII control character
  */
 
 /**
  *
  * Determines whether a character is digit (0-9).
  *
- * Unlike the standard C library isdigit() function, this takes
- * a char, not an int, so don't call it  on %EOF, but no need to
- * cast to #guchar before passing a possibly non-ASCII character in.
+ * Unlike the standard C library `isdigit()` function, this takes
+ * a `char`, not an `int`, so don't call it  on `EOF`, but no need to
+ * cast to `guchar` before passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII digit.
+ * Returns: true if @c is an ASCII digit
  */
 
 /**
  *
  * Determines whether a character is a printing character and not a space.
  *
- * Unlike the standard C library isgraph() function, this only
+ * Unlike the standard C library `isgraph()` function, this only
  * recognizes standard ASCII characters and ignores the locale,
- * returning %FALSE for all non-ASCII characters. Also, unlike
- * the standard library function, this takes a char, not an int,
- * so don't call it on %EOF, but no need to cast to #guchar before
+ * returning false for all non-ASCII characters. Also, unlike
+ * the standard library function, this takes a `char`, not an `int`,
+ * so don't call it on `EOF`, but no need to cast to `guchar` before
  * passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII printing character other than space.
+ * Returns: true if @c is an ASCII printing character other than space
  */
 
 /**
  *
  * Determines whether a character is an ASCII lower case letter.
  *
- * Unlike the standard C library islower() function, this only
+ * Unlike the standard C library `islower()` function, this only
  * recognizes standard ASCII letters and ignores the locale,
- * returning %FALSE for all non-ASCII characters. Also, unlike
- * the standard library function, this takes a char, not an int,
- * so don't call it on %EOF, but no need to worry about casting
- * to #guchar before passing a possibly non-ASCII character in.
+ * returning false for all non-ASCII characters. Also, unlike
+ * the standard library function, this takes a `char`, not an `int`,
+ * so don't call it on `EOF`, but no need to worry about casting
+ * to `guchar` before passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII lower case letter
+ * Returns: true if @c is an ASCII lower case letter
  */
 
 /**
  *
  * Determines whether a character is a printing character.
  *
- * Unlike the standard C library isprint() function, this only
+ * Unlike the standard C library `isprint()` function, this only
  * recognizes standard ASCII characters and ignores the locale,
- * returning %FALSE for all non-ASCII characters. Also, unlike
- * the standard library function, this takes a char, not an int,
- * so don't call it on %EOF, but no need to cast to #guchar before
+ * returning false for all non-ASCII characters. Also, unlike
+ * the standard library function, this takes a `char`, not an `int`,
+ * so don't call it on `EOF`, but no need to cast to `guchar` before
  * passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII printing character.
+ * Returns: true if @c is an ASCII printing character
  */
 
 /**
  *
  * Determines whether a character is a punctuation character.
  *
- * Unlike the standard C library ispunct() function, this only
+ * Unlike the standard C library `ispunct()` function, this only
  * recognizes standard ASCII letters and ignores the locale,
- * returning %FALSE for all non-ASCII characters. Also, unlike
- * the standard library function, this takes a char, not an int,
- * so don't call it on %EOF, but no need to cast to #guchar before
+ * returning false for all non-ASCII characters. Also, unlike
+ * the standard library function, this takes a `char`, not an `int`,
+ * so don't call it on `EOF`, but no need to cast to `guchar` before
  * passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII punctuation character.
+ * Returns: true if @c is an ASCII punctuation character
  */
 
 /**
  *
  * Determines whether a character is a white-space character.
  *
- * Unlike the standard C library isspace() function, this only
+ * Unlike the standard C library `isspace()` function, this only
  * recognizes standard ASCII white-space and ignores the locale,
- * returning %FALSE for all non-ASCII characters. Also, unlike
- * the standard library function, this takes a char, not an int,
- * so don't call it on %EOF, but no need to cast to #guchar before
+ * returning false for all non-ASCII characters. Also, unlike
+ * the standard library function, this takes a `char`, not an `int`,
+ * so don't call it on `EOF`, but no need to cast to `guchar` before
  * passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII white-space character
+ * Returns: true if @c is an ASCII white-space character
  */
 
 /**
  *
  * Determines whether a character is an ASCII upper case letter.
  *
- * Unlike the standard C library isupper() function, this only
+ * Unlike the standard C library `isupper()` function, this only
  * recognizes standard ASCII letters and ignores the locale,
- * returning %FALSE for all non-ASCII characters. Also, unlike
- * the standard library function, this takes a char, not an int,
- * so don't call it on %EOF, but no need to worry about casting
- * to #guchar before passing a possibly non-ASCII character in.
+ * returning false for all non-ASCII characters. Also, unlike
+ * the standard library function, this takes a `char`, not an `int`,
+ * so don't call it on `EOF`, but no need to worry about casting
+ * to `guchar` before passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII upper case letter
+ * Returns: true if @c is an ASCII upper case letter
  */
 
 /**
  *
  * Determines whether a character is a hexadecimal-digit character.
  *
- * Unlike the standard C library isxdigit() function, this takes
- * a char, not an int, so don't call it on %EOF, but no need to
- * cast to #guchar before passing a possibly non-ASCII character in.
+ * Unlike the standard C library `isxdigit()` function, this takes
+ * a `char`, not an `int`, so don't call it on `EOF`, but no need to
+ * cast to `guchar` before passing a possibly non-ASCII character in.
  *
- * Returns: %TRUE if @c is an ASCII hexadecimal-digit character.
+ * Returns: true if @c is an ASCII hexadecimal-digit character
  */
 
 /**
  * G_ASCII_DTOSTR_BUF_SIZE:
  *
- * A good size for a buffer to be passed into g_ascii_dtostr().
+ * A good size for a buffer to be passed into [func@GLib.ascii_dtostr].
  * It is guaranteed to be enough for all output of that function
  * on systems with 64bit IEEE-compatible doubles.
  *
  * The typical usage would be something like:
- * |[<!-- language="C" --> 
- *   char buf[G_ASCII_DTOSTR_BUF_SIZE];
+ * ```C
+ * char buf[G_ASCII_DTOSTR_BUF_SIZE];
  *
- *   fprintf (out, "value=%s\n", g_ascii_dtostr (buf, sizeof (buf), value));
- * ]|
+ * fprintf (out, "value=%s\n", g_ascii_dtostr (buf, sizeof (buf), value));
+ * ```
  */
 
 /**
  * @string: a string to remove the leading and trailing whitespace from
  *
  * Removes leading and trailing whitespace from a string.
- * See g_strchomp() and g_strchug().
+ *
+ * See [func@GLib.strchomp] and [func@GLib.strchug].
  *
  * Returns: @string
  */
 /**
  * G_STR_DELIMITERS:
  *
- * The standard delimiters, used in g_strdelimit().
+ * The standard delimiters, used in [func@GLib.strdelimit].
  */
 
 static const guint16 ascii_table_data[256] = {
@@ -306,9 +307,7 @@ get_C_locale (void)
  * g_strdup:
  * @str: (nullable): the string to duplicate
  *
- * Duplicates a string. If @str is %NULL it returns %NULL.
- * The returned string should be freed with g_free()
- * when no longer needed.
+ * Duplicates a string. If @str is `NULL` it returns `NULL`.
  *
  * Returns: a newly-allocated copy of @str
  */
@@ -332,17 +331,17 @@ gchar*
 
 /**
  * g_memdup:
- * @mem: the memory to copy.
- * @byte_size: the number of bytes to copy.
+ * @mem: the memory to copy
+ * @byte_size: the number of bytes to copy
  *
  * Allocates @byte_size bytes of memory, and copies @byte_size bytes into it
- * from @mem. If @mem is %NULL it returns %NULL.
+ * from @mem. If @mem is `NULL` it returns `NULL`.
+ *
+ * Returns: (transfer full) (nullable): a pointer to the newly-allocated copy of the memory
  *
- * Returns: a pointer to the newly-allocated copy of the memory, or %NULL if @mem
- *  is %NULL.
- * Deprecated: 2.68: Use g_memdup2() instead, as it accepts a #gsize argument
- *     for @byte_size, avoiding the possibility of overflow in a #gsize → #guint
- *     conversion
+ * Deprecated: 2.68: Use [func@GLib.memdup2] instead, as it accepts a gsize argument
+ *   for @byte_size, avoiding the possibility of overflow in a `gsize` → `guint`
+ *   conversion
  */
 gpointer
 g_memdup (gconstpointer mem,
@@ -363,17 +362,17 @@ g_memdup (gconstpointer mem,
 
 /**
  * g_memdup2:
- * @mem: (nullable): the memory to copy.
- * @byte_size: the number of bytes to copy.
+ * @mem: (nullable): the memory to copy
+ * @byte_size: the number of bytes to copy
  *
  * Allocates @byte_size bytes of memory, and copies @byte_size bytes into it
- * from @mem. If @mem is %NULL it returns %NULL.
+ * from @mem. If @mem is `NULL` it returns `NULL`.
+ *
+ * This replaces [func@GLib.memdup], which was prone to integer overflows when
+ * converting the argument from a `gsize` to a `guint`.
  *
- * This replaces g_memdup(), which was prone to integer overflows when
- * converting the argument from a #gsize to a #guint.
+ * Returns: (transfer full) (nullable): a pointer to the newly-allocated copy of the memory
  *
- * Returns: (nullable): a pointer to the newly-allocated copy of the memory,
- *    or %NULL if @mem is %NULL.
  * Since: 2.68
  */
 gpointer
@@ -395,20 +394,19 @@ g_memdup2 (gconstpointer mem,
 
 /**
  * g_strndup:
- * @str: the string to duplicate
+ * @str: (nullable): the string to duplicate
  * @n: the maximum number of bytes to copy from @str
  *
  * Duplicates the first @n bytes of a string, returning a newly-allocated
  * buffer @n + 1 bytes long which will always be nul-terminated. If @str
  * is less than @n bytes long the buffer is padded with nuls. If @str is
- * %NULL it returns %NULL. The returned value should be freed when no longer
- * needed.
+ * `NULL` it returns `NULL`.
  *
  * To copy a number of characters from a UTF-8 encoded string,
- * use g_utf8_strncpy() instead.
+ * use [func@GLib.utf8_strncpy] instead.
  *
- * Returns: a newly-allocated buffer containing the first @n bytes
- *     of @str, nul-terminated
+ * Returns: (nullable): a newly-allocated buffer containing the first
+ *    @n bytes of @str
  */
 gchar*
 g_strndup (const gchar *str,
@@ -434,9 +432,8 @@ g_strndup (const gchar *str,
  * @fill_char: the byte to fill the string with
  *
  * Creates a new string @length bytes long filled with @fill_char.
- * The returned string should be freed when no longer needed.
  *
- * Returns: a newly-allocated string filled the @fill_char
+ * Returns: a newly-allocated string filled with @fill_char
  */
 gchar*
 g_strnfill (gsize length,
@@ -453,15 +450,15 @@ g_strnfill (gsize length,
 
 /**
  * g_stpcpy:
- * @dest: destination buffer.
- * @src: source string.
+ * @dest: destination buffer
+ * @src: source string
  *
  * Copies a nul-terminated string into the destination buffer, including
  * the trailing nul byte, and returns a pointer to the trailing nul byte
  * in `dest`.  The return value is useful for concatenating multiple
  * strings without having to repeatedly scan for the end.
  *
- * Returns: a pointer to the trailing nul byte in `dest`.
+ * Returns: a pointer to the trailing nul byte in `dest`
  **/
 gchar *
 g_stpcpy (gchar       *dest,
@@ -487,23 +484,23 @@ g_stpcpy (gchar       *dest,
 
 /**
  * g_strdup_vprintf:
- * @format: (not nullable): a standard printf() format string, but notice
- *     [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @format: (not nullable): a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
  * @args: the list of parameters to insert into the format string
  *
- * Similar to the standard C vsprintf() function but safer, since it
+ * Similar to the standard C `vsprintf()` function but safer, since it
  * calculates the maximum space required and allocates memory to hold
- * the result. The returned string should be freed with g_free() when
- * no longer needed.
+ * the result.
  *
  * The returned string is guaranteed to be non-NULL, unless @format
  * contains `%lc` or `%ls` conversions, which can fail if no multibyte
  * representation is available for the given character.
  *
- * See also g_vasprintf(), which offers the same functionality, but
+ * See also [func@GLib.vasprintf], which offers the same functionality, but
  * additionally returns the length of the allocated string.
  *
- * Returns: (nullable) (transfer full): a newly-allocated string holding the result
+ * Returns: (nullable) (transfer full): a newly-allocated string holding the
+ *   result
  */
 gchar*
 g_strdup_vprintf (const gchar *format,
@@ -518,20 +515,20 @@ g_strdup_vprintf (const gchar *format,
 
 /**
  * g_strdup_printf:
- * @format: (not nullable): a standard printf() format string, but notice
- *     [string precision pitfalls](string-utils.html#string-precision-pitfalls)
+ * @format: (not nullable): a standard `printf()` format string, but notice
+ *   [string precision pitfalls](string-utils.html#string-precision-pitfalls)
  * @...: the parameters to insert into the format string
  *
- * Similar to the standard C sprintf() function but safer, since it
+ * Similar to the standard C `sprintf()` function but safer, since it
  * calculates the maximum space required and allocates memory to hold
- * the result. The returned string should be freed with g_free() when no
- * longer needed.
+ * the result.
  *
  * The returned string is guaranteed to be non-NULL, unless @format
  * contains `%lc` or `%ls` conversions, which can fail if no multibyte
  * representation is available for the given character.
  *
- * Returns: (nullable) (transfer full): a newly-allocated string holding the result
+ * Returns: (nullable) (transfer full): a newly-allocated string holding the
+ *   result
  */
 gchar*
 g_strdup_printf (const gchar *format,
@@ -549,14 +546,13 @@ g_strdup_printf (const gchar *format,
 
 /**
  * g_strconcat:
- * @string1: the first string to add, which must not be %NULL
- * @...: a %NULL-terminated list of strings to append to the string
+ * @string1: the first string to add, which must not be `NULL`
+ * @...: a `NULL`-terminated list of strings to append to the string
  *
- * Concatenates all of the given strings into one long string. The
- * returned string should be freed with g_free() when no longer needed.
+ * Concatenates all of the given strings into one long string.
  *
- * The variable argument list must end with %NULL. If you forget the %NULL,
- * g_strconcat() will start appending random memory junk to your string.
+ * The variable argument list must end with `NULL`. If you forget the `NULL`,
+ * `g_strconcat()` will start appending random memory junk to your string.
  *
  * Note that this function is usually not the right function to use to
  * assemble a translated message from pieces, since proper translation
@@ -604,23 +600,24 @@ g_strconcat (const gchar *string1, ...)
 
 /**
  * g_strtod:
- * @nptr:    the string to convert to a numeric value.
- * @endptr:  (out) (transfer none) (optional): if non-%NULL, it returns the
- *           character after the last character used in the conversion.
+ * @nptr: the string to convert to a numeric value
+ * @endptr: (out) (transfer none) (optional): if non-`NULL`, it returns the
+ *   character after the last character used in the conversion
+ *
+ * Converts a string to a floating point value.
  *
- * Converts a string to a #gdouble value.
- * It calls the standard strtod() function to handle the conversion, but
+ * It calls the standard `strtod()` function to handle the conversion, but
  * if the string is not completely converted it attempts the conversion
- * again with g_ascii_strtod(), and returns the best match.
+ * again with [func@GLib.ascii_strtod], and returns the best match.
  *
  * This function should seldom be used. The normal situation when reading
- * numbers not for human consumption is to use g_ascii_strtod(). Only when
+ * numbers not for human consumption is to use [func@GLib.ascii_strtod]. Only when
  * you know that you must expect both locale formatted and C formatted numbers
  * should you use this. Make sure that you don't pass strings such as comma
  * separated lists of values, since the commas may be interpreted as a decimal
  * point in some locales, causing unexpected results.
  *
- * Returns: the #gdouble value.
+ * Returns: the converted value
  **/
 gdouble
 g_strtod (const gchar *nptr,
@@ -657,13 +654,13 @@ g_strtod (const gchar *nptr,
 
 /**
  * g_ascii_strtod:
- * @nptr:    the string to convert to a numeric value.
- * @endptr:  (out) (transfer none) (optional): if non-%NULL, it returns the
- *           character after the last character used in the conversion.
+ * @nptr: the string to convert to a numeric value
+ * @endptr: (out) (transfer none) (optional): if non-`NULL`, it returns the
+ *   character after the last character used in the conversion
  *
- * Converts a string to a #gdouble value.
+ * Converts a string to a floating point value.
  *
- * This function behaves like the standard strtod() function
+ * This function behaves like the standard `strtod()` function
  * does in the C locale. It does this without actually changing
  * the current locale, since that would not be thread-safe.
  * A limitation of the implementation is that this function
@@ -672,20 +669,20 @@ g_strtod (const gchar *nptr,
  * This function is typically used when reading configuration
  * files or other non-user input that should be locale independent.
  * To handle input from the user you should normally use the
- * locale-sensitive system strtod() function.
+ * locale-sensitive system `strtod()` function.
  *
- * To convert from a #gdouble to a string in a locale-insensitive
- * way, use g_ascii_dtostr().
+ * To convert from a gdouble to a string in a locale-insensitive
+ * way, use [func@GLib.ascii_dtostr].
  *
- * If the correct value would cause overflow, plus or minus %HUGE_VAL
- * is returned (according to the sign of the value), and %ERANGE is
- * stored in %errno. If the correct value would cause underflow,
- * zero is returned and %ERANGE is stored in %errno.
+ * If the correct value would cause overflow, plus or minus `HUGE_VAL`
+ * is returned (according to the sign of the value), and `ERANGE` is
+ * stored in `errno`. If the correct value would cause underflow,
+ * zero is returned and `ERANGE` is stored in `errno`.
  *
- * This function resets %errno before calling strtod() so that
+ * This function resets `errno` before calling `strtod()` so that
  * you can reliably detect overflow and underflow.
  *
- * Returns: the #gdouble value.
+ * Returns: the converted value
  */
 gdouble
 g_ascii_strtod (const gchar *nptr,
@@ -858,21 +855,21 @@ g_ascii_strtod (const gchar *nptr,
 
 /**
  * g_ascii_dtostr:
- * @buffer: A buffer to place the resulting string in
- * @buf_len: The length of the buffer.
- * @d: The #gdouble to convert
+ * @buffer: a buffer to place the resulting string in
+ * @buf_len: the length of the buffer
+ * @d: the value to convert
  *
- * Converts a #gdouble to a string, using the '.' as
+ * Converts a `gdouble` to a string, using the '.' as
  * decimal point.
  *
  * This function generates enough precision that converting
- * the string back using g_ascii_strtod() gives the same machine-number
+ * the string back using [func@GLib.ascii_strtod] gives the same machine-number
  * (on machines with IEEE compatible 64bit doubles). It is
  * guaranteed that the size of the resulting string will never
- * be larger than %G_ASCII_DTOSTR_BUF_SIZE bytes, including the terminating
+ * be larger than [const@GLib.ASCII_DTOSTR_BUF_SIZE] bytes, including the terminating
  * nul character, which is always added.
  *
- * Returns: The pointer to the buffer with the converted string.
+ * Returns: the pointer to the buffer with the converted string
  **/
 gchar *
 g_ascii_dtostr (gchar       *buffer,
@@ -887,26 +884,26 @@ g_ascii_dtostr (gchar       *buffer,
 
 /**
  * g_ascii_formatd:
- * @buffer: A buffer to place the resulting string in
- * @buf_len: The length of the buffer.
- * @format: The printf()-style format to use for the
+ * @buffer: a buffer to place the resulting string in
+ * @buf_len: the length of the buffer
+ * @format: the `printf()`-style format to use for the
  *   code to use for converting
- * @d: The #gdouble to convert
+ * @d: the value to convert
  *
- * Converts a #gdouble to a string, using the '.' as
+ * Converts a `gdouble` to a string, using the '.' as
  * decimal point. To format the number you pass in
- * a printf()-style format string. Allowed conversion
+ * a `printf()`-style format string. Allowed conversion
  * specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'.
  *
  * The @format must just be a single format specifier
- * starting with `%`, expecting a #gdouble argument.
+ * starting with `%`, expecting a `gdouble` argument.
  *
  * The returned buffer is guaranteed to be nul-terminated.
  *
  * If you just want to want to serialize the value into a
- * string, use g_ascii_dtostr().
+ * string, use [func@GLib.ascii_dtostr].
  *
- * Returns: The pointer to the buffer with the converted string.
+ * Returns: the pointer to the buffer with the converted string
  */
 gchar *
 g_ascii_formatd (gchar       *buffer,
@@ -1140,35 +1137,36 @@ g_parse_long_long (const gchar  *nptr,
 
 /**
  * g_ascii_strtoull:
- * @nptr:    the string to convert to a numeric value.
- * @endptr:  (out) (transfer none) (optional): if non-%NULL, it returns the
- *           character after the last character used in the conversion.
- * @base:    to be used for the conversion, 2..36 or 0
+ * @nptr: the string to convert to a numeric value
+ * @endptr: (out) (transfer none) (optional): if non-`NULL`, it returns the
+ *   character after the last character used in the conversion
+ * @base: to be used for the conversion, 2..36 or 0
  *
- * Converts a string to a #guint64 value.
- * This function behaves like the standard strtoull() function
+ * Converts a string to a `guint64` value.
+ *
+ * This function behaves like the standard `strtoull()` function
  * does in the C locale. It does this without actually
  * changing the current locale, since that would not be
  * thread-safe.
  *
  * Note that input with a leading minus sign (`-`) is accepted, and will return
- * the negation of the parsed number, unless that would overflow a #guint64.
+ * the negation of the parsed number, unless that would overflow a `guint64`.
  * Critically, this means you cannot assume that a short fixed length input will
- * never result in a low return value, as the input could have a leading `-`.
+ * result in a low return value, as the input could have a leading `-`.
  *
  * This function is typically used when reading configuration
  * files or other non-user input that should be locale independent.
  * To handle input from the user you should normally use the
- * locale-sensitive system strtoull() function.
+ * locale-sensitive system `strtoull()` function.
  *
- * If the correct value would cause overflow, %G_MAXUINT64
+ * If the correct value would cause overflow, [const@GLib.MAXUINT64]
  * is returned, and `ERANGE` is stored in `errno`.
  * If the base is outside the valid range, zero is returned, and
  * `EINVAL` is stored in `errno`.
  * If the string conversion fails, zero is returned, and @endptr returns
- * @nptr (if @endptr is non-%NULL).
+ * @nptr (if @endptr is non-`NULL`).
  *
- * Returns: the #guint64 value or zero on error.
+ * Returns: the converted value, or zero on error
  *
  * Since: 2.2
  */
@@ -1192,13 +1190,14 @@ g_ascii_strtoull (const gchar *nptr,
 
 /**
  * g_ascii_strtoll:
- * @nptr:    the string to convert to a numeric value.
- * @endptr:  (out) (transfer none) (optional): if non-%NULL, it returns the
- *           character after the last character used in the conversion.
- * @base:    to be used for the conversion, 2..36 or 0
+ * @nptr: the string to convert to a numeric value
+ * @endptr: (out) (transfer none) (optional): if non-`NULL`, it returns the
+ *   character after the last character used in the conversion
+ * @base: to be used for the conversion, 2..36 or 0
+ *
+ * Converts a string to a `gint64` value.
  *
- * Converts a string to a #gint64 value.
- * This function behaves like the standard strtoll() function
+ * This function behaves like the standard `strtoll()` function
  * does in the C locale. It does this without actually
  * changing the current locale, since that would not be
  * thread-safe.
@@ -1206,16 +1205,16 @@ g_ascii_strtoull (const gchar *nptr,
  * This function is typically used when reading configuration
  * files or other non-user input that should be locale independent.
  * To handle input from the user you should normally use the
- * locale-sensitive system strtoll() function.
+ * locale-sensitive system `strtoll()` function.
  *
- * If the correct value would cause overflow, %G_MAXINT64 or %G_MININT64
- * is returned, and `ERANGE` is stored in `errno`.
+ * If the correct value would cause overflow, [const@GLib.MAXINT64] or
+ * [const@GLib.MININT64] is returned, and `ERANGE` is stored in `errno`.
  * If the base is outside the valid range, zero is returned, and
  * `EINVAL` is stored in `errno`. If the
  * string conversion fails, zero is returned, and @endptr returns @nptr
- * (if @endptr is non-%NULL).
+ * (if @endptr is non-`NULL`).
  *
- * Returns: the #gint64 value or zero on error.
+ * Returns: the converted value, or zero on error
  *
  * Since: 2.12
  */
@@ -1251,30 +1250,31 @@ g_ascii_strtoll (const gchar *nptr,
 
 /**
  * g_strerror:
- * @errnum: the system error number. See the standard C %errno
- *     documentation
+ * @errnum: the system error number. See the standard C `errno` documentation
  *
  * Returns a string corresponding to the given error code, e.g. "no
- * such process". Unlike strerror(), this always returns a string in
+ * such process".
+ *
+ * Unlike `strerror()`, this always returns a string in
  * UTF-8 encoding, and the pointer is guaranteed to remain valid for
- * the lifetime of the process.
+ * the lifetime of the process. If the error code is unknown, it returns a
+ * string like “Unknown error <code\>”.
  *
  * Note that the string may be translated according to the current locale.
  *
- * The value of %errno will not be changed by this function. However, it may
+ * The value of `errno` will not be changed by this function. However, it may
  * be changed by intermediate function calls, so you should save its value
  * as soon as the call returns:
- * |[
- *   int saved_errno;
+ * ```C
+ * int saved_errno;
  *
- *   ret = read (blah);
- *   saved_errno = errno;
+ * ret = read (blah);
+ * saved_errno = errno;
  *
- *   g_strerror (saved_errno);
- * ]|
+ * g_strerror (saved_errno);
+ * ```
  *
- * Returns: a UTF-8 string describing the error code. If the error code
- *     is unknown, it returns a string like "Unknown error: <code>".
+ * Returns: the string describing the error code
  */
 const gchar *
 g_strerror (gint errnum)
@@ -1351,12 +1351,13 @@ g_strerror (gint errnum)
  * @signum: the signal number. See the `signal` documentation
  *
  * Returns a string describing the given signal, e.g. "Segmentation fault".
- * You should use this function in preference to strsignal(), because it
+ * If the signal is unknown, it returns “unknown signal (<signum\>)”.
+ *
+ * You should use this function in preference to `strsignal()`, because it
  * returns a string in UTF-8 encoding, and since not all platforms support
- * the strsignal() function.
+ * the `strsignal()` function.
  *
- * Returns: a UTF-8 string describing the signal. If the signal is unknown,
- *     it returns "unknown signal (<signum>)".
+ * Returns: the string describing the signal
  */
 const gchar *
 g_strsignal (gint signum)
@@ -1383,7 +1384,7 @@ g_strsignal (gint signum)
 
 /* Functions g_strlcpy and g_strlcat were originally developed by
  * Todd C. Miller <Todd.Miller@courtesan.com> to simplify writing secure code.
- * See http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy 
+ * See http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy
  * for more information.
  */
 
@@ -1418,19 +1419,19 @@ g_strlcat (gchar       *dest,
  * @src: source buffer
  * @dest_size: length of @dest in bytes
  *
- * Portability wrapper that calls strlcpy() on systems which have it,
- * and emulates strlcpy() otherwise. Copies @src to @dest; @dest is
+ * Portability wrapper that calls `strlcpy()` on systems which have it,
+ * and emulates `strlcpy()` otherwise. Copies @src to @dest; @dest is
  * guaranteed to be nul-terminated; @src must be nul-terminated;
  * @dest_size is the buffer size, not the number of bytes to copy.
  *
  * At most @dest_size - 1 characters will be copied. Always nul-terminates
  * (unless @dest_size is 0). This function does not allocate memory. Unlike
- * strncpy(), this function doesn't pad @dest (so it's often faster). It
- * returns the size of the attempted result, strlen (src), so if
+ * `strncpy()`, this function doesn't pad @dest (so it's often faster). It
+ * returns the size of the attempted result, `strlen (src)`, so if
  * @retval >= @dest_size, truncation occurred.
  *
- * Caveat: strlcpy() is supposedly more secure than strcpy() or strncpy(),
- * but if you really want to avoid screwups, g_strdup() is an even better
+ * Caveat: `strlcpy()` is supposedly more secure than `strcpy()` or `strncpy()`,
+ * but if you really want to avoid screwups, [func@GLib.strdup] is an even better
  * idea.
  *
  * Returns: length of @src
@@ -1476,25 +1477,25 @@ g_strlcpy (gchar       *dest,
  * @dest: destination buffer, already containing one nul-terminated string
  * @src: source buffer
  * @dest_size: length of @dest buffer in bytes (not length of existing string
- *     inside @dest)
+ *   inside @dest)
  *
- * Portability wrapper that calls strlcat() on systems which have it,
+ * Portability wrapper that calls `strlcat()` on systems which have it,
  * and emulates it otherwise. Appends nul-terminated @src string to @dest,
  * guaranteeing nul-termination for @dest. The total size of @dest won't
  * exceed @dest_size.
  *
- * At most @dest_size - 1 characters will be copied. Unlike strncat(),
+ * At most @dest_size - 1 characters will be copied. Unlike `strncat()`,
  * @dest_size is the full size of dest, not the space left over. This
  * function does not allocate memory. It always nul-terminates (unless
  * @dest_size == 0 or there were no nul characters in the @dest_size
  * characters of dest to start with).
  *
- * Caveat: this is supposedly a more secure alternative to strcat() or
- * strncat(), but for real security g_strconcat() is harder to mess up.
+ * Caveat: this is supposedly a more secure alternative to `strcat()` or
+ * `strncat()`, but for real security [func@GLib.strconcat] is harder to mess up.
  *
- * Returns: size of attempted result, which is MIN (dest_size, strlen
- *     (original dest)) + strlen (src), so if retval >= dest_size,
- *     truncation occurred.
+ * Returns: size of attempted result, which is `MIN (dest_size, strlen
+ *   (original dest)) + strlen (src)`, so if @retval >= @dest_size,
+ *   truncation occurred
  */
 gsize
 g_strlcat (gchar       *dest,
@@ -1536,14 +1537,14 @@ g_strlcat (gchar       *dest,
 /**
  * g_ascii_strdown:
  * @str: a string
- * @len: length of @str in bytes, or -1 if @str is nul-terminated
+ * @len: length of @str in bytes, or `-1` if @str is nul-terminated
  *
- * Converts all upper case ASCII letters to lower case ASCII letters.
+ * Converts all upper case ASCII letters to lower case ASCII letters, with
+ * semantics that exactly match [func@GLib.ascii_tolower].
  *
- * Returns: a newly-allocated string, with all the upper case
- *     characters in @str converted to lower case, with semantics that
- *     exactly match g_ascii_tolower(). (Note that this is unlike the
- *     old g_strdown(), which modified the string in place.)
+ * Returns: a newly-allocated string, with all the upper case characters in
+ *   @str converted to lower case. (Note that this is unlike the old
+ *   [func@GLib.strdown], which modified the string in place.)
  */
 gchar*
 g_ascii_strdown (const gchar *str,
@@ -1554,9 +1555,10 @@ g_ascii_strdown (const gchar *str,
   g_return_val_if_fail (str != NULL, NULL);
 
   if (len < 0)
-    len = (gssize) strlen (str);
+    result = g_strdup (str);
+  else
+    result = g_strndup (str, (gsize) len);
 
-  result = g_strndup (str, (gsize) len);
   for (s = result; *s; s++)
     *s = g_ascii_tolower (*s);
 
@@ -1566,14 +1568,14 @@ g_ascii_strdown (const gchar *str,
 /**
  * g_ascii_strup:
  * @str: a string
- * @len: length of @str in bytes, or -1 if @str is nul-terminated
+ * @len: length of @str in bytes, or `-1` if @str is nul-terminated
  *
- * Converts all lower case ASCII letters to upper case ASCII letters.
+ * Converts all lower case ASCII letters to upper case ASCII letters, with
+ * semantics that exactly match [func@GLib.ascii_toupper].
  *
- * Returns: a newly allocated string, with all the lower case
- *     characters in @str converted to upper case, with semantics that
- *     exactly match g_ascii_toupper(). (Note that this is unlike the
- *     old g_strup(), which modified the string in place.)
+ * Returns: a newly-allocated string, with all the lower case characters
+ *   in @str converted to upper case. (Note that this is unlike the old
+ *   [func@GLib.strup], which modified the string in place.)
  */
 gchar*
 g_ascii_strup (const gchar *str,
@@ -1584,9 +1586,10 @@ g_ascii_strup (const gchar *str,
   g_return_val_if_fail (str != NULL, NULL);
 
   if (len < 0)
-    len = (gssize) strlen (str);
+    result = g_strdup (str);
+  else
+    result = g_strndup (str, (gsize) len);
 
-  result = g_strndup (str, (gsize) len);
   for (s = result; *s; s++)
     *s = g_ascii_toupper (*s);
 
@@ -1600,7 +1603,7 @@ g_ascii_strup (const gchar *str,
  * Determines if a string is pure ASCII. A string is pure ASCII if it
  * contains no bytes with the high bit set.
  *
- * Returns: %TRUE if @str is ASCII
+ * Returns: true if @str is ASCII
  *
  * Since: 2.40
  */
@@ -1618,15 +1621,15 @@ g_str_is_ascii (const gchar *str)
 
 /**
  * g_strdown:
- * @string: the string to convert.
+ * @string: the string to convert
  *
  * Converts a string to lower case.
  *
  * Returns: the string
  *
- * Deprecated:2.2: This function is totally broken for the reasons discussed
- * in the g_strncasecmp() docs - use g_ascii_strdown() or g_utf8_strdown()
- * instead.
+ * Deprecated: 2.2: This function is totally broken for the reasons discussed
+ *   in the [func@GLib.strncasecmp] docs — use [func@GLib.ascii_strdown] or
+ *   [func@GLib.utf8_strdown] instead.
  **/
 gchar*
 g_strdown (gchar *string)
@@ -1655,9 +1658,9 @@ g_strdown (gchar *string)
  *
  * Returns: the string
  *
- * Deprecated:2.2: This function is totally broken for the reasons
- *     discussed in the g_strncasecmp() docs - use g_ascii_strup()
- *     or g_utf8_strup() instead.
+ * Deprecated: 2.2: This function is totally broken for the reasons discussed
+ *   in the [func@GLib.strncasecmp] docs — use [func@GLib.ascii_strup] or
+ *   [func@GLib.utf8_strup] instead.
  */
 gchar*
 g_strup (gchar *string)
@@ -1685,11 +1688,11 @@ g_strup (gchar *string)
  * Reverses all of the bytes in a string. For example,
  * `g_strreverse ("abcdef")` will result in "fedcba".
  *
- * Note that g_strreverse() doesn't work on UTF-8 strings
+ * Note that `g_strreverse()` doesn't work on UTF-8 strings
  * containing multibyte characters. For that purpose, use
- * g_utf8_strreverse().
+ * [func@GLib.utf8_strreverse].
  *
- * Returns: the same pointer passed in as @string
+ * Returns: the @string, reversed in place
  */
 gchar*
 g_strreverse (gchar *string)
@@ -1722,18 +1725,18 @@ g_strreverse (gchar *string)
  * g_ascii_tolower:
  * @c: any character
  *
- * Convert a character to ASCII lower case.
+ * Convert a character to ASCII lower case. If the character is not an
+ * ASCII upper case letter, it is returned unchanged.
  *
- * Unlike the standard C library tolower() function, this only
+ * Unlike the standard C library `tolower()` function, this only
  * recognizes standard ASCII letters and ignores the locale, returning
  * all non-ASCII characters unchanged, even if they are lower case
  * letters in a particular character set. Also unlike the standard
  * library function, this takes and returns a char, not an int, so
- * don't call it on %EOF but no need to worry about casting to #guchar
+ * don't call it on `EOF` but no need to worry about casting to `guchar`
  * before passing a possibly non-ASCII character in.
  *
- * Returns: the result of converting @c to lower case. If @c is
- *     not an ASCII upper case letter, @c is returned unchanged.
+ * Returns: the result of the conversion
  */
 gchar
 g_ascii_tolower (gchar c)
@@ -1745,18 +1748,18 @@ g_ascii_tolower (gchar c)
  * g_ascii_toupper:
  * @c: any character
  *
- * Convert a character to ASCII upper case.
+ * Convert a character to ASCII upper case. If the character is not an
+ * ASCII lower case letter, it is returned unchanged.
  *
- * Unlike the standard C library toupper() function, this only
+ * Unlike the standard C library `toupper()` function, this only
  * recognizes standard ASCII letters and ignores the locale, returning
  * all non-ASCII characters unchanged, even if they are upper case
  * letters in a particular character set. Also unlike the standard
  * library function, this takes and returns a char, not an int, so
- * don't call it on %EOF but no need to worry about casting to #guchar
+ * don't call it on `EOF` but no need to worry about casting to `guchar`
  * before passing a possibly non-ASCII character in.
  *
- * Returns: the result of converting @c to upper case. If @c is not
- *    an ASCII lower case letter, @c is returned unchanged.
+ * Returns: the result of the conversion
  */
 gchar
 g_ascii_toupper (gchar c)
@@ -1768,12 +1771,14 @@ g_ascii_toupper (gchar c)
  * g_ascii_digit_value:
  * @c: an ASCII character
  *
- * Determines the numeric value of a character as a decimal digit.
- * Differs from g_unichar_digit_value() because it takes a char, so
+ * Determines the numeric value of a character as a decimal digit. If the
+ * character is not a decimal digit according to [func@GLib.ascii_isdigit],
+ * `-1` is returned.
+ *
+ * Differs from [func@GLib.unichar_digit_value] because it takes a char, so
  * there's no worry about sign extension if characters are signed.
  *
- * Returns: If @c is a decimal digit (according to g_ascii_isdigit()),
- *    its numeric value. Otherwise, -1.
+ * Returns: the numerical value of @c if it is a decimal digit, `-1` otherwise
  */
 int
 g_ascii_digit_value (gchar c)
@@ -1785,15 +1790,19 @@ g_ascii_digit_value (gchar c)
 
 /**
  * g_ascii_xdigit_value:
- * @c: an ASCII character.
+ * @c: an ASCII character
  *
- * Determines the numeric value of a character as a hexadecimal
- * digit. Differs from g_unichar_xdigit_value() because it takes
- * a char, so there's no worry about sign extension if characters
- * are signed.
+ * Determines the numeric value of a character as a hexadecimal digit. If the
+ * character is not a hex digit according to [func@GLib.ascii_isxdigit],
+ * `-1` is returned.
  *
- * Returns: If @c is a hex digit (according to g_ascii_isxdigit()),
- *     its numeric value. Otherwise, -1.
+ * Differs from [func@GLib.unichar_xdigit_value] because it takes a char, so
+ * there's no worry about sign extension if characters are signed.
+ *
+ * Differs from [func@GLib.unichar_xdigit_value] because it takes a char, so
+ * there's no worry about sign extension if characters are signed.
+ *
+ * Returns: the numerical value of @c if it is a hex digit, `-1` otherwise
  */
 int
 g_ascii_xdigit_value (gchar c)
@@ -1812,7 +1821,7 @@ g_ascii_xdigit_value (gchar c)
  *
  * Compare two strings, ignoring the case of ASCII characters.
  *
- * Unlike the BSD strcasecmp() function, this only recognizes standard
+ * Unlike the BSD `strcasecmp()` function, this only recognizes standard
  * ASCII letters and ignores the locale, treating all non-ASCII
  * bytes as if they are not letters.
  *
@@ -1824,10 +1833,10 @@ g_ascii_xdigit_value (gchar c)
  * characters include all ASCII letters. If you compare two CP932
  * strings using this function, you will get false matches.
  *
- * Both @s1 and @s2 must be non-%NULL.
+ * Both @s1 and @s2 must be non-`NULL`.
  *
  * Returns: 0 if the strings match, a negative value if @s1 < @s2,
- *     or a positive value if @s1 > @s2.
+ *   or a positive value if @s1 > @s2
  */
 gint
 g_ascii_strcasecmp (const gchar *s1,
@@ -1861,16 +1870,16 @@ g_ascii_strcasecmp (const gchar *s1,
  * less than @n bytes long, comparison will stop at the first nul byte
  * encountered.
  *
- * Unlike the BSD strcasecmp() function, this only recognizes standard
+ * Unlike the BSD `strncasecmp()` function, this only recognizes standard
  * ASCII letters and ignores the locale, treating all non-ASCII
  * characters as if they are not letters.
  *
- * The same warning as in g_ascii_strcasecmp() applies: Use this
+ * The same warning as in [func@GLib.ascii_strcasecmp] applies: Use this
  * function only on strings known to be in encodings where bytes
  * corresponding to ASCII letters always represent themselves.
  *
  * Returns: 0 if the strings match, a negative value if @s1 < @s2,
- *     or a positive value if @s1 > @s2.
+ *   or a positive value if @s1 > @s2
  */
 gint
 g_ascii_strncasecmp (const gchar *s1,
@@ -1900,17 +1909,17 @@ g_ascii_strncasecmp (const gchar *s1,
 
 /**
  * g_strcasecmp:
- * @s1: a string
- * @s2: string to compare with @s1
+ * @s1: string to compare with @s2
+ * @s2: string to compare with @s1
  *
  * A case-insensitive string comparison, corresponding to the standard
- * strcasecmp() function on platforms which support it.
+ * `strcasecmp()` function on platforms which support it.
  *
  * Returns: 0 if the strings match, a negative value if @s1 < @s2,
- *     or a positive value if @s1 > @s2.
+ *   or a positive value if @s1 > @s2
  *
- * Deprecated:2.2: See g_strncasecmp() for a discussion of why this
- *     function is deprecated and how to replace it.
+ * Deprecated: 2.2: See [func@GLib.strncasecmp] for a discussion of why this
+ *   function is deprecated and how to replace it.
  */
 gint
 g_strcasecmp (const gchar *s1,
@@ -1945,32 +1954,32 @@ g_strcasecmp (const gchar *s1,
 
 /**
  * g_strncasecmp:
- * @s1: a string
- * @s2: string to compare with @s1
+ * @s1: string to compare with @s2
+ * @s2: string to compare with @s1
  * @n: the maximum number of characters to compare
  *
  * A case-insensitive string comparison, corresponding to the standard
- * strncasecmp() function on platforms which support it. It is similar
- * to g_strcasecmp() except it only compares the first @n characters of
+ * `strncasecmp()` function on platforms which support it. It is similar
+ * to [func@GLib.strcasecmp] except it only compares the first @n characters of
  * the strings.
  *
  * Returns: 0 if the strings match, a negative value if @s1 < @s2,
- *     or a positive value if @s1 > @s2.
- *
- * Deprecated:2.2: The problem with g_strncasecmp() is that it does
- *     the comparison by calling toupper()/tolower(). These functions
- *     are locale-specific and operate on single bytes. However, it is
- *     impossible to handle things correctly from an internationalization
- *     standpoint by operating on bytes, since characters may be multibyte.
- *     Thus g_strncasecmp() is broken if your string is guaranteed to be
- *     ASCII, since it is locale-sensitive, and it's broken if your string
- *     is localized, since it doesn't work on many encodings at all,
- *     including UTF-8, EUC-JP, etc.
- *
- *     There are therefore two replacement techniques: g_ascii_strncasecmp(),
- *     which only works on ASCII and is not locale-sensitive, and
- *     g_utf8_casefold() followed by strcmp() on the resulting strings,
- *     which is good for case-insensitive sorting of UTF-8.
+ *   or a positive value if @s1 > @s2
+ *
+ * Deprecated: 2.2: The problem with `g_strncasecmp()` is that it does
+ *   the comparison by calling `toupper()`/`tolower()`. These functions
+ *   are locale-specific and operate on single bytes. However, it is
+ *   impossible to handle things correctly from an internationalization
+ *   standpoint by operating on bytes, since characters may be multibyte.
+ *   Thus `g_strncasecmp()` is broken if your string is guaranteed to be
+ *   ASCII, since it is locale-sensitive, and it's broken if your string
+ *   is localized, since it doesn't work on many encodings at all,
+ *   including UTF-8, EUC-JP, etc.
+ *
+ *   There are therefore two replacement techniques: [func@GLib.ascii_strncasecmp],
+ *   which only works on ASCII and is not locale-sensitive, and
+ *   [func@GLib.utf8_casefold] followed by `strcmp()` on the resulting strings,
+ *   which is good for case-insensitive sorting of UTF-8.
  */
 gint
 g_strncasecmp (const gchar *s1,
@@ -2008,8 +2017,8 @@ g_strncasecmp (const gchar *s1,
 /**
  * g_strdelimit:
  * @string: the string to convert
- * @delimiters: (nullable): a string containing the current delimiters,
- *     or %NULL to use the standard delimiters defined in %G_STR_DELIMITERS
+ * @delimiters: (nullable): a string containing the current delimiters, or
+ *   `NULL` to use the standard delimiters defined in [const@GLib.STR_DELIMITERS]
  * @new_delimiter: the new delimiter character
  *
  * Converts any delimiter characters in @string to @new_delimiter.
@@ -2019,18 +2028,16 @@ g_strncasecmp (const gchar *s1,
  * and returns @string itself, not a copy.
  *
  * The return value is to allow nesting such as:
+ * ```C
+ * g_ascii_strup (g_strdelimit (str, "abc", '?'))
+ * ```
  *
- * |[<!-- language="C" -->
- *   g_ascii_strup (g_strdelimit (str, "abc", '?'))
- * ]|
- *
- * In order to modify a copy, you may use g_strdup():
- *
- * |[<!-- language="C" -->
- *   reformatted = g_strdelimit (g_strdup (const_str), "abc", '?');
- *   ...
- *   g_free (reformatted);
- * ]|
+ * In order to modify a copy, you may use [func@GLib.strdup]:
+ * ```C
+ * reformatted = g_strdelimit (g_strdup (const_str), "abc", '?');
+ * …
+ * g_free (reformatted);
+ * ```
  *
  * Returns: the modified @string
  */
@@ -2066,18 +2073,16 @@ g_strdelimit (gchar       *string,
  *
  * Modifies @string in place, and return @string itself, not a copy. The
  * return value is to allow nesting such as:
+ * ```C
+ * g_ascii_strup (g_strcanon (str, "abc", '?'))
+ * ```
  *
- * |[<!-- language="C" -->
- *   g_ascii_strup (g_strcanon (str, "abc", '?'))
- * ]|
- *
- * In order to modify a copy, you may use g_strdup():
- *
- * |[<!-- language="C" -->
- *   reformatted = g_strcanon (g_strdup (const_str), "abc", '?');
- *   ...
- *   g_free (reformatted);
- * ]|
+ * In order to modify a copy, you may use [func@GLib.strdup]:
+ * ```C
+ * reformatted = g_strcanon (g_strdup (const_str), "abc", '?');
+ * …
+ * g_free (reformatted);
+ * ```
  *
  * Returns: the modified @string
  */
@@ -2106,10 +2111,10 @@ g_strcanon (gchar       *string,
  *
  * Replaces all escaped characters with their one byte equivalent.
  *
- * This function does the reverse conversion of g_strescape().
+ * This function does the reverse conversion of [func@GLib.strescape].
  *
  * Returns: a newly-allocated copy of @source with all escaped
- *     character compressed
+ *   character compressed
  */
 gchar *
 g_strcompress (const gchar *source)
@@ -2190,10 +2195,9 @@ out:
  * replaced with a '\' followed by their octal representation.
  * Characters supplied in @exceptions are not escaped.
  *
- * g_strcompress() does the reverse conversion.
+ * [func@GLib.strcompress] does the reverse conversion.
  *
- * Returns: a newly-allocated copy of @source with certain
- *     characters escaped. See above.
+ * Returns: a newly-allocated copy of @source with special characters escaped
  */
 gchar *
 g_strescape (const gchar *source,
@@ -2294,9 +2298,9 @@ g_strescape (const gchar *source,
  *
  * The pointer to @string is returned to allow the nesting of functions.
  *
- * Also see g_strchomp() and g_strstrip().
+ * Also see [func@GLib.strchomp] and [func@GLib.strstrip].
  *
- * Returns: @string
+ * Returns: the modified @string
  */
 gchar *
 g_strchug (gchar *string)
@@ -2325,9 +2329,9 @@ g_strchug (gchar *string)
  *
  * The pointer to @string is returned to allow the nesting of functions.
  *
- * Also see g_strchug() and g_strstrip().
+ * Also see [func@GLib.strchug] and [func@GLib.strstrip].
  *
- * Returns: @string
+ * Returns: the modified @string
  */
 gchar *
 g_strchomp (gchar *string)
@@ -2352,28 +2356,27 @@ g_strchomp (gchar *string)
  * g_strsplit:
  * @string: a string to split
  * @delimiter: a string which specifies the places at which to split
- *     the string. The delimiter is not included in any of the resulting
- *     strings, unless @max_tokens is reached.
- * @max_tokens: the maximum number of pieces to split @string into.
- *     If this is less than 1, the string is split completely.
+ *   the string. The delimiter is not included in any of the resulting
+ *   strings, unless @max_tokens is reached.
+ * @max_tokens: the maximum number of pieces to split @string into
+ *   If this is less than 1, the string is split completely
  *
  * Splits a string into a maximum of @max_tokens pieces, using the given
  * @delimiter. If @max_tokens is reached, the remainder of @string is
  * appended to the last token.
  *
- * As an example, the result of g_strsplit (":a:bc::d:", ":", -1) is a
- * %NULL-terminated vector containing the six strings "", "a", "bc", "", "d"
- * and "".
+ * As an example, the result of `g_strsplit (":a:bc::d:", ":", -1)` is an array
+ * containing the six strings "", "a", "bc", "", "d" and "".
  *
  * As a special case, the result of splitting the empty string "" is an empty
- * vector, not a vector containing a single string. The reason for this
- * special case is that being able to represent an empty vector is typically
+ * array, not an array containing a single string. The reason for this
+ * special case is that being able to represent an empty array is typically
  * more useful than consistent handling of empty elements. If you do need
  * to represent empty elements, you'll need to check for the empty string
- * before calling g_strsplit().
+ * before calling `g_strsplit()`.
  *
- * Returns: (transfer full): a newly-allocated %NULL-terminated array of
- *    strings. Use g_strfreev() to free it.
+ * Returns: (transfer full): a newly-allocated array of strings, freed with
+ *   [func@GLib.strfreev]
  */
 gchar**
 g_strsplit (const gchar *string,
@@ -2424,37 +2427,35 @@ g_strsplit (const gchar *string,
 
 /**
  * g_strsplit_set:
- * @string: The string to be tokenized
- * @delimiters: A nul-terminated string containing bytes that are used
- *     to split the string (it can accept an empty string, which will result
- *     in no string splitting).
- * @max_tokens: The maximum number of tokens to split @string into.
- *     If this is less than 1, the string is split completely
+ * @string: a string to split
+ * @delimiters: a string containing characters that are used to split the
+ *   string. Can be empty, which will result in no string splitting
+ * @max_tokens: the maximum number of tokens to split @string into.
+ *   If this is less than 1, the string is split completely
  *
  * Splits @string into a number of tokens not containing any of the characters
- * in @delimiter. A token is the (possibly empty) longest string that does not
+ * in @delimiters. A token is the (possibly empty) longest string that does not
  * contain any of the characters in @delimiters. If @max_tokens is reached, the
  * remainder is appended to the last token.
  *
- * For example the result of g_strsplit_set ("abc:def/ghi", ":/", -1) is a
- * %NULL-terminated vector containing the three strings "abc", "def",
- * and "ghi".
+ * For example, the result of g_strsplit_set ("abc:def/ghi", ":/", -1) is an
+ * array containing the three strings "abc", "def", and "ghi".
  *
- * The result of g_strsplit_set (":def/ghi:", ":/", -1) is a %NULL-terminated
- * vector containing the four strings "", "def", "ghi", and "".
+ * The result of g_strsplit_set (":def/ghi:", ":/", -1) is an array containing
+ * the four strings "", "def", "ghi", and "".
  *
  * As a special case, the result of splitting the empty string "" is an empty
- * vector, not a vector containing a single string. The reason for this
- * special case is that being able to represent an empty vector is typically
+ * array, not an array containing a single string. The reason for this
+ * special case is that being able to represent an empty array is typically
  * more useful than consistent handling of empty elements. If you do need
  * to represent empty elements, you'll need to check for the empty string
- * before calling g_strsplit_set().
+ * before calling `g_strsplit_set()`.
  *
  * Note that this function works on bytes not characters, so it can't be used
  * to delimit UTF-8 strings for anything but ASCII characters.
  *
- * Returns: (transfer full): a newly-allocated %NULL-terminated array of
- *    strings. Use g_strfreev() to free it.
+ * Returns: (transfer full): a newly-allocated array of strings. Use
+ *   [func@GLib.strfreev] to free it.
  *
  * Since: 2.4
  **/
@@ -2528,17 +2529,17 @@ g_strsplit_set (const gchar *string,
  * GStrv:
  *
  * A typedef alias for gchar**. This is mostly useful when used together with
- * g_auto().
+ * `g_auto()`.
  */
 
 /**
  * g_strfreev:
- * @str_array: (nullable) (transfer full): a %NULL-terminated array of strings to free
+ * @str_array: (array zero-terminated=1) (nullable) (transfer full): an
+ *   array of strings to free
  *
- * Frees a %NULL-terminated array of strings, as well as each
- * string it contains.
+ * Frees an array of strings, as well as each string it contains.
  *
- * If @str_array is %NULL, this function simply returns.
+ * If @str_array is `NULL`, this function simply returns.
  */
 void
 g_strfreev (gchar **str_array)
@@ -2556,14 +2557,15 @@ g_strfreev (gchar **str_array)
 
 /**
  * g_strdupv:
- * @str_array: (nullable): a %NULL-terminated array of strings
+ * @str_array: (array zero-terminated=1) (nullable): an array of strings to copy
+ *
+ * Copies an array of strings. The copy is a deep copy; each string is also
+ * copied.
  *
- * Copies %NULL-terminated array of strings. The copy is a deep copy;
- * the new array should be freed by first freeing each string, then
- * the array itself. g_strfreev() does this for you. If called
- * on a %NULL value, g_strdupv() simply returns %NULL.
+ * If called on a `NULL` value, `g_strdupv()` simply returns `NULL`.
  *
- * Returns: (nullable) (transfer full): a new %NULL-terminated array of strings.
+ * Returns: (array zero-terminated=1) (nullable) (transfer full): a
+ *   newly-allocated array of strings. Use [func@GLib.strfreev] to free it.
  */
 gchar**
 g_strdupv (gchar **str_array)
@@ -2595,20 +2597,18 @@ g_strdupv (gchar **str_array)
 
 /**
  * g_strjoinv:
- * @separator: (nullable): a string to insert between each of the
- *     strings, or %NULL
- * @str_array: a %NULL-terminated array of strings to join
+ * @separator: (nullable): a string to insert between each of the strings
+ * @str_array: (array zero-terminated=1): an array of strings to join
  *
- * Joins a number of strings together to form one long string, with the
- * optional @separator inserted between each of them. The returned string
- * should be freed with g_free().
+ * Joins an array of strings together to form one long string, with the
+ * optional @separator inserted between each of them.
  *
  * If @str_array has no items, the return value will be an
  * empty string. If @str_array contains a single item, @separator will not
  * appear in the resulting string.
  *
  * Returns: a newly-allocated string containing all of the strings joined
- *     together, with @separator between them
+ *   together, with @separator between them
  */
 gchar*
 g_strjoinv (const gchar  *separator,
@@ -2652,16 +2652,14 @@ g_strjoinv (const gchar  *separator,
 
 /**
  * g_strjoin:
- * @separator: (nullable): a string to insert between each of the
- *     strings, or %NULL
- * @...: a %NULL-terminated list of strings to join
+ * @separator: (nullable): a string to insert between each of the strings
+ * @...: a `NULL`-terminated list of strings to join
  *
  * Joins a number of strings together to form one long string, with the
- * optional @separator inserted between each of them. The returned string
- * should be freed with g_free().
+ * optional @separator inserted between each of them.
  *
  * Returns: a newly-allocated string containing all of the strings joined
- *     together, with @separator between them
+ *   together, with @separator between them
  */
 gchar*
 g_strjoin (const gchar *separator,
@@ -2722,17 +2720,19 @@ g_strjoin (const gchar *separator,
 
 /**
  * g_strstr_len:
- * @haystack: a nul-terminated string
- * @haystack_len: the maximum length of @haystack in bytes. A length of -1
- *     can be used to mean "search the entire string", like `strstr()`.
+ * @haystack: a string to search in
+ * @haystack_len: the maximum length of @haystack in bytes, or `-1` to
+ *   search it entirely
  * @needle: the string to search for
  *
  * Searches the string @haystack for the first occurrence
  * of the string @needle, limiting the length of the search
  * to @haystack_len or a nul terminator byte (whichever is reached first).
  *
- * Returns: a pointer to the found occurrence, or
- *    %NULL if not found.
+ * A length of `-1` can be used to mean “search the entire string”, like
+ * `strstr()`.
+ *
+ * Returns: a pointer to the found occurrence, or `NULL` if not found
  */
 gchar *
 g_strstr_len (const gchar *haystack,
@@ -2778,14 +2778,13 @@ g_strstr_len (const gchar *haystack,
 
 /**
  * g_strrstr:
- * @haystack: a nul-terminated string
- * @needle: the nul-terminated string to search for
+ * @haystack: a string to search in
+ * @needle: the string to search for
  *
  * Searches the string @haystack for the last occurrence
  * of the string @needle.
  *
- * Returns: a pointer to the found occurrence, or
- *    %NULL if not found.
+ * Returns: a pointer to the found occurrence, or `NULL` if not found
  */
 gchar *
 g_strrstr (const gchar *haystack,
@@ -2827,17 +2826,16 @@ g_strrstr (const gchar *haystack,
 
 /**
  * g_strrstr_len:
- * @haystack: a nul-terminated string
- * @haystack_len: the maximum length of @haystack in bytes. A length of -1
- *     can be used to mean "search the entire string", like g_strrstr().
- * @needle: the nul-terminated string to search for
+ * @haystack: a string to search in
+ * @haystack_len: the maximum length of @haystack in bytes. A length of `-1`
+ *   can be used to mean "search the entire string", like [func@GLib.strrstr]
+ * @needle: the string to search for
  *
  * Searches the string @haystack for the last occurrence
  * of the string @needle, limiting the length of the search
  * to @haystack_len.
  *
- * Returns: a pointer to the found occurrence, or
- *    %NULL if not found.
+ * Returns: a pointer to the found occurrence, or `NULL` if not found
  */
 gchar *
 g_strrstr_len (const gchar *haystack,
@@ -2883,12 +2881,12 @@ g_strrstr_len (const gchar *haystack,
 
 /**
  * g_str_has_suffix:
- * @str: a nul-terminated string
- * @suffix: the nul-terminated suffix to look for
+ * @str: a string to look in
+ * @suffix: the suffix to look for
  *
- * Looks whether the string @str ends with @suffix.
+ * Looks whether a string ends with @suffix.
  *
- * Returns: %TRUE if @str end with @suffix, %FALSE otherwise.
+ * Returns: true if @str ends with @suffix, false otherwise
  *
  * Since: 2.2
  */
@@ -2912,12 +2910,12 @@ gboolean (g_str_has_suffix) (const gchar *str,
 
 /**
  * g_str_has_prefix:
- * @str: a nul-terminated string
- * @prefix: the nul-terminated prefix to look for
+ * @str: a string to look in
+ * @prefix: the prefix to look for
  *
  * Looks whether the string @str begins with @prefix.
  *
- * Returns: %TRUE if @str begins with @prefix, %FALSE otherwise.
+ * Returns: true if @str begins with @prefix, false otherwise
  *
  * Since: 2.2
  */
@@ -2932,12 +2930,11 @@ gboolean (g_str_has_prefix) (const gchar *str,
 
 /**
  * g_strv_length:
- * @str_array: a %NULL-terminated array of strings
+ * @str_array: (array zero-terminated=1): an array of strings
  *
- * Returns the length of the given %NULL-terminated
- * string array @str_array. @str_array must not be %NULL.
+ * Returns the length of an array of strings. @str_array must not be `NULL`.
  *
- * Returns: length of @str_array.
+ * Returns: length of @str_array
  *
  * Since: 2.6
  */
@@ -3041,21 +3038,21 @@ split_words (const gchar *value)
 
 /**
  * g_str_tokenize_and_fold:
- * @string: a string
+ * @string: a string to tokenize
  * @translit_locale: (nullable): the language code (like 'de' or
  *   'en_GB') from which @string originates
- * @ascii_alternates: (out) (transfer full) (array zero-terminated=1): a
- *   return location for ASCII alternates
+ * @ascii_alternates: (out) (optional) (transfer full) (array zero-terminated=1):
+ *   return location for ASCII alternates
  *
- * Tokenises @string and performs folding on each token.
+ * Tokenizes @string and performs folding on each token.
  *
  * A token is a non-empty sequence of alphanumeric characters in the
  * source string, separated by non-alphanumeric characters.  An
  * "alphanumeric" character for this purpose is one that matches
- * g_unichar_isalnum() or g_unichar_ismark().
+ * [func@GLib.unichar_isalnum] or [func@GLib.unichar_ismark].
  *
  * Each token is then (Unicode) normalised and case-folded.  If
- * @ascii_alternates is non-%NULL and some of the returned tokens
+ * @ascii_alternates is non-`NULL` and some of the returned tokens
  * contain non-ASCII characters, ASCII alternatives will be generated.
  *
  * The number of ASCII alternatives that are generated and the method
@@ -3129,13 +3126,13 @@ g_str_tokenize_and_fold (const gchar   *string,
  * g_str_match_string:
  * @search_term: the search term from the user
  * @potential_hit: the text that may be a hit
- * @accept_alternates: %TRUE to accept ASCII alternates
+ * @accept_alternates: if true, ASCII alternates are accepted
  *
  * Checks if a search conducted for @search_term should match
  * @potential_hit.
  *
- * This function calls g_str_tokenize_and_fold() on both
- * @search_term and @potential_hit.  ASCII alternates are never taken
+ * This function calls [func@GLib.str_tokenize_and_fold] on both
+ * @search_term and @potential_hit. ASCII alternates are never taken
  * for @search_term but will be taken for @potential_hit according to
  * the value of @accept_alternates.
  *
@@ -3143,9 +3140,9 @@ g_str_tokenize_and_fold (const gchar   *string,
  * folded token from @potential_hit.
  *
  * Depending on how you're performing the search, it will typically be
- * faster to call g_str_tokenize_and_fold() on each string in
+ * faster to call `g_str_tokenize_and_fold()` on each string in
  * your corpus and build an index on the returned folded tokens, then
- * call g_str_tokenize_and_fold() on the search term and
+ * call `g_str_tokenize_and_fold()` on the search term and
  * perform lookups into that index.
  *
  * As some examples, searching for ‘fred’ would match the potential hit
@@ -3154,7 +3151,7 @@ g_str_tokenize_and_fold (const gchar   *string,
  * accent matching).  Searching ‘fo’ would match ‘Foo’ and ‘Bar Foo
  * Baz’, but not ‘SFO’ (because no word has ‘fo’ as a prefix).
  *
- * Returns: %TRUE if @potential_hit is a hit
+ * Returns: true if @potential_hit is a hit
  *
  * Since: 2.40
  **/
@@ -3204,12 +3201,13 @@ one_matched:
 
 /**
  * g_strv_contains:
- * @strv: a %NULL-terminated array of strings
- * @str: a string
+ * @strv: (array zero-terminated=1): an array of strings to search in
+ * @str: the string to search for
  *
- * Checks if @strv contains @str. @strv must not be %NULL.
+ * Checks if an array of strings contains the string @str according to
+ * [func@GLib.str_equal]. @strv must not be `NULL`.
  *
- * Returns: %TRUE if @str is an element of @strv, according to g_str_equal().
+ * Returns: true if @str is an element of @strv
  *
  * Since: 2.44
  */
@@ -3231,17 +3229,24 @@ g_strv_contains (const gchar * const *strv,
 
 /**
  * g_strv_equal:
- * @strv1: a %NULL-terminated array of strings
- * @strv2: another %NULL-terminated array of strings
+ * @strv1: (array zero-terminated=1): an array of strings to compare to @strv2
+ * @strv2: (array zero-terminated=1): an array of strings to compare to @strv1
+ *
+ * Checks if two arrays of strings contain exactly the same elements in
+ * exactly the same order.
  *
- * Checks if @strv1 and @strv2 contain exactly the same elements in exactly the
- * same order. Elements are compared using g_str_equal(). To match independently
- * of order, sort the arrays first (using g_qsort_with_data() or similar).
+ * Elements are compared using [func@GLib.str_equal]. To match independently
+ * of order, sort the arrays first (using [func@GLib.qsort_with_data]
+ * or similar).
  *
- * Two empty arrays are considered equal. Neither @strv1 not @strv2 may be
- * %NULL.
+ * Elements are compared using [func@GLib.str_equal]. To match independently
+ * of order, sort the arrays first (using [func@GLib.qsort_with_data]
+ * or similar).
  *
- * Returns: %TRUE if @strv1 and @strv2 are equal
+ * Two empty arrays are considered equal. Neither @strv1 nor @strv2 may be
+ * `NULL`.
+ *
+ * Returns: true if @strv1 and @strv2 are equal
  * Since: 2.60
  */
 gboolean
@@ -3277,7 +3282,7 @@ str_has_hex_prefix (const gchar *str)
 
 /**
  * g_ascii_string_to_signed:
- * @str: a string
+ * @str: a string to convert
  * @base: base of a parsed number
  * @min: a lower bound (inclusive)
  * @max: an upper bound (inclusive)
@@ -3297,16 +3302,16 @@ str_has_hex_prefix (const gchar *str)
  * for octal numbers, since they were usually prefixed with a zero
  * which does not change the value of the parsed number.
  *
- * Parsing failures result in an error with the %G_NUMBER_PARSER_ERROR
+ * Parsing failures result in an error with the `G_NUMBER_PARSER_ERROR`
  * domain. If the input is invalid, the error code will be
- * %G_NUMBER_PARSER_ERROR_INVALID. If the parsed number is out of
- * bounds - %G_NUMBER_PARSER_ERROR_OUT_OF_BOUNDS.
+ * [error@GLib.NumberParserError.INVALID]. If the parsed number is out of
+ * bounds - [error@GLib.NumberParserError.OUT_OF_BOUNDS].
  *
- * See g_ascii_strtoll() if you have more complex needs such as
+ * See [func@GLib.ascii_strtoll] if you have more complex needs such as
  * parsing a string which starts with a number, but then has other
  * characters.
  *
- * Returns: %TRUE if @str was a number, otherwise %FALSE.
+ * Returns: true if @str was a number, false otherwise
  *
  * Since: 2.54
  */
@@ -3399,16 +3404,16 @@ g_ascii_string_to_signed (const gchar  *str,
  * for octal numbers, since they were usually prefixed with a zero
  * which does not change the value of the parsed number.
  *
- * Parsing failures result in an error with the %G_NUMBER_PARSER_ERROR
+ * Parsing failures result in an error with the `G_NUMBER_PARSER_ERROR`
  * domain. If the input is invalid, the error code will be
- * %G_NUMBER_PARSER_ERROR_INVALID. If the parsed number is out of
- * bounds - %G_NUMBER_PARSER_ERROR_OUT_OF_BOUNDS.
+ * [error@GLib.NumberParserError.INVALID]. If the parsed number is out of
+ * bounds - [error@GLib.NumberParserError.OUT_OF_BOUNDS].
  *
- * See g_ascii_strtoull() if you have more complex needs such as
+ * See [func@GLib.ascii_strtoull] if you have more complex needs such as
  * parsing a string which starts with a number, but then has other
  * characters.
  *
- * Returns: %TRUE if @str was a number, otherwise %FALSE.
+ * Returns: true if @str was a number, false otherwise
  *
  * Since: 2.54
  */
index 19ba95c..bd6a2c1 100644 (file)
@@ -412,8 +412,8 @@ gboolean              g_strv_equal     (const gchar * const *strv1,
 
 /**
  * GNumberParserError:
- * @G_NUMBER_PARSER_ERROR_INVALID: String was not a valid number.
- * @G_NUMBER_PARSER_ERROR_OUT_OF_BOUNDS: String was a number, but out of bounds.
+ * @G_NUMBER_PARSER_ERROR_INVALID: string was not a valid number
+ * @G_NUMBER_PARSER_ERROR_OUT_OF_BOUNDS: string was a number, but out of bounds
  *
  * Error codes returned by functions converting a string to a number.
  *
index 704f5ce..5c5f905 100644 (file)
@@ -2885,12 +2885,13 @@ g_test_queue_free (gpointer gfree_pointer)
  * @destroy_func:       Destroy callback for teardown phase.
  * @destroy_data:       Destroy callback data.
  *
- * This function enqueus a callback @destroy_func to be executed
- * during the next test case teardown phase. This is most useful
- * to auto destruct allocated test resources at the end of a test run.
- * Resources are released in reverse queue order, that means enqueueing
- * callback A before callback B will cause B() to be called before
- * A() during teardown.
+ * Enqueues a callback @destroy_func to be executed during the next test case
+ * teardown phase.
+ *
+ * This is most useful to auto destroy allocated test resources at the end of a
+ * test run. Resources are released in reverse queue order, that means
+ * enqueueing callback `A` before callback `B` will cause `B()` to be called
+ * before `A()` during teardown.
  *
  * Since: 2.16
  */
@@ -2925,17 +2926,30 @@ test_has_prefix (gconstpointer a,
     return g_strcmp0 (test_run_name_local, test_path_skipped_local);
 }
 
+static gboolean test_should_run (const char *test_path,
+                                 const char *cmp_path);
+
 static gboolean
-test_case_run (GTestCase *tc)
+test_case_run (GTestCase  *tc,
+               const char *test_run_name,
+               const char *path)
 {
-  gchar *old_base = g_strdup (test_uri_base);
+  gchar *old_base = NULL;
   GSList **old_free_list, *filename_free_list = NULL;
   gboolean success = G_TEST_RUN_SUCCESS;
+  gboolean free_test_data = TRUE;
 
+  old_base = g_strdup (test_uri_base);
   old_free_list = test_filename_free_list;
   test_filename_free_list = &filename_free_list;
 
-  if (++test_run_count <= test_startup_skip_count)
+  if (!test_should_run (test_run_name, path))
+    {
+      /* Silently skip the test and return success. This happens if it’s a
+       * /subprocess path. */
+      success = G_TEST_RUN_SKIPPED;
+    }
+  else if (++test_run_count <= test_startup_skip_count)
     g_test_log (G_TEST_LOG_SKIP_CASE, test_run_name, NULL, 0, NULL);
   else if (test_run_list)
     {
@@ -2982,6 +2996,7 @@ test_case_run (GTestCase *tc)
                 }
               if (tc->fixture_teardown)
                 tc->fixture_teardown (fixture, tc->test_data);
+              free_test_data = FALSE;
               if (tc->fixture_size)
                 g_free (fixture);
               g_timer_stop (test_run_timer);
@@ -2999,6 +3014,13 @@ test_case_run (GTestCase *tc)
       g_timer_destroy (test_run_timer);
     }
 
+  /* In case the test didn’t run (due to being skipped or an error), the test
+   * data may still need to be freed, as the client’s main() function may have
+   * passed ownership of it into g_test_add_data_func_full() with a
+   * #GDestroyNotify. */
+  if (free_test_data && tc->fixture_size == 0 && tc->fixture_teardown != NULL)
+    tc->fixture_teardown (tc->test_data, tc->test_data);
+
   g_slist_free_full (filename_free_list, g_free);
   test_filename_free_list = old_free_list;
   g_free (test_uri_base);
@@ -3064,11 +3086,10 @@ g_test_run_suite_internal (GTestSuite *suite,
 
       test_run_name = g_build_path ("/", old_name, tc->name, NULL);
       test_run_name_path = g_build_path (G_DIR_SEPARATOR_S, old_name_path, tc->name, NULL);
-      if (test_should_run (test_run_name, path))
-        {
-          if (!test_case_run (tc))
-            n_bad++;
-        }
+
+      if (!test_case_run (tc, test_run_name, path))
+        n_bad++;
+
       g_free (test_run_name);
       g_free (test_run_name_path);
     }
index a44c4c8..e38a7f9 100644 (file)
@@ -33,7 +33,7 @@
 
 #include <glib/gtypes.h>
 
-#if defined(G_PLATFORM_WIN32) || defined(__GI_SCANNER__)
+#ifdef G_PLATFORM_WIN32
 
 G_BEGIN_DECLS
 
@@ -41,7 +41,7 @@ G_BEGIN_DECLS
 #define MAXPATHLEN 1024
 #endif
 
-#if defined(G_OS_WIN32) || defined(__GI_SCANNER__)
+#ifdef G_OS_WIN32
 
 /*
  * To get prototypes for the following POSIXish functions, you have to
@@ -68,7 +68,7 @@ G_BEGIN_DECLS
 GLIB_AVAILABLE_IN_ALL
 gint           g_win32_ftruncate       (gint            f,
                                         guint           size);
-#endif /* G_OS_WIN32 || __GI_SCANNER__ */
+#endif /* G_OS_WIN32 */
 
 /* The MS setlocale uses locale names of the form "English_United
  * States.1252" etc. We want the Unixish standard form "en", "zh_TW"
@@ -137,6 +137,6 @@ gboolean g_win32_check_windows_version (const gint major,
 
 G_END_DECLS
 
-#endif  /* G_PLATFORM_WIN32 || __GI_SCANNER__ */
+#endif  /* G_PLATFORM_WIN32 */
 
 #endif /* __G_WIN32_H__ */
index 95e863e..d2efeba 100644 (file)
@@ -145,19 +145,19 @@ endif
 
 glib_headers = files(
   'glib.h',
-  'glib-unix.h',
   'glib-object.h',
 )
 install_headers(glib_headers, install_dir : glib_includedir)
 
+if host_system != 'windows'
+  glib_unix_headers = files('glib-unix.h')
+  install_headers(glib_unix_headers, install_dir : glib_includedir)
+endif
+
 if host_system == 'windows'
   install_headers([ 'msvc_recommended_pragmas.h' ], install_dir : glib_includedir)
 endif
 
-# Expose as variable to be used by gobject-introspection
-# when it includes GLib as a subproject
-glib_unix_h = files('glib-unix.h')
-
 glib_deprecated_headers = files(
   'deprecated/gallocator.h',
   'deprecated/gcache.h',
@@ -247,12 +247,16 @@ glib_sub_headers = files(
   'gvarianttype.h',
   'gvariant.h',
   'gversion.h',
-  'gwin32.h',
   'gprintf.h',
 )
 
 install_headers(glib_sub_headers, install_dir : glib_sub_includedir)
 
+if host_system == 'windows'
+  glib_win32_headers = files('gwin32.h')
+  install_headers(glib_win32_headers, install_dir : glib_sub_includedir)
+endif
+
 glib_deprecated_sources = files(
   'deprecated/gallocator.c',
   'deprecated/gcache.c',
@@ -327,7 +331,6 @@ glib_sources += files(
   'gtimer.c',
   'gtimezone.c',
   'gtrace.c',
-  'gtrace-private.h',
   'gtranslit.c',
   'gtrashstack.c',
   'gtree.c',
@@ -337,9 +340,7 @@ glib_sources += files(
   'gunicollate.c',
   'gunidecomp.c',
   'guri.c',
-  'guriprivate.h',
   'gutils.c',
-  'gutilsprivate.h',
   'guuid.c',
   'gvariant.c',
   'gvariant-core.c',
@@ -370,7 +371,7 @@ if host_system == 'windows'
     glib_sources += files('dirent/wdirent.c')
   endif
 else
-  glib_sources += files('glib-unix.c', 'glib-unixprivate.h', 'gspawn.c', 'giounix.c')
+  glib_sources += files('glib-unix.c', 'gspawn.c', 'giounix.c')
   platform_deps = []
 endif
 
index 7541268..8c47985 100644 (file)
@@ -1,6 +1,8 @@
 #include <glib.h>
 #include <stdlib.h>
 
+#include "glib/glib-private.h"
+
 static void
 test_quark_basic (void)
 {
@@ -284,6 +286,83 @@ test_datalist_id_remove_multiple (void)
 }
 
 static void
+test_datalist_id_remove_multiple_resize (void)
+{
+  GQuark *quarks;
+  GQuark *quarks2;
+  const guint N = 1000;
+  const guint PRIME = 1048583u;
+  guint i;
+  char sbuf[100];
+  GData *list = NULL;
+  guint i_run;
+
+  quarks = g_new (GQuark, N);
+  quarks2 = g_new (GQuark, N);
+
+  for (i = 0; i < N; i++)
+    {
+      g_snprintf (sbuf, sizeof (sbuf), "%d", i);
+      quarks[i] = g_quark_from_string (sbuf);
+    }
+
+  for (i = 0; i < N; i++)
+    g_datalist_id_set_data (&list, quarks[i], GINT_TO_POINTER (i));
+
+  /* Now we perform a list of random operations (remove/add quarks). */
+  for (i_run = 0; TRUE; i_run++)
+    {
+      int MODE = ((guint) g_test_rand_int ()) % 4;
+      guint n;
+      guint j;
+
+      n = ((guint) g_test_rand_int ()) % (N + 1);
+      j = ((guint) g_test_rand_int ()) % N;
+
+      if (i_run > 20)
+        {
+          /* After a few runs, we only remove elements, until the list
+           * is empty. */
+          if (!list)
+            break;
+          MODE = 0;
+          if (i_run > 30)
+            n = N;
+        }
+
+      switch (MODE)
+        {
+        case 0:
+        case 1:
+        case 2:
+          /* Mode: add or remove a number of random quarks. */
+          for (i = 0; i < n; i++)
+            {
+              j = (j + PRIME) % N;
+              if (MODE == 0)
+                g_datalist_id_remove_data (&list, quarks[j]);
+              else
+                g_datalist_id_set_data (&list, quarks[j], GINT_TO_POINTER (j));
+            }
+          break;
+        case 3:
+          /* Mode: remove a list of (random) quarks. */
+          for (i = 0; i < n; i++)
+            {
+              j = (j + PRIME) % N;
+              quarks2[i] = quarks[j];
+            }
+
+          g_datalist_id_remove_multiple (&list, quarks2, n);
+          break;
+        }
+    }
+
+  g_free (quarks);
+  g_free (quarks2);
+}
+
+static void
 destroy_func (gpointer data)
 {
   destroy_count++;
@@ -319,6 +398,58 @@ test_datalist_id_remove_multiple_destroy_order (void)
   g_assert_cmpint (destroy_count, ==, 3);
 }
 
+static gpointer
+_update_atomic_cb (GQuark key_id,
+                   gpointer *data,
+                   GDestroyNotify *destroy_notify,
+                   gpointer user_data)
+{
+  const char *op = user_data;
+  char *data_entry = *data;
+
+  g_assert_nonnull (op);
+
+  if (strcmp (op, "create") == 0)
+    {
+      g_assert_cmpstr (data_entry, ==, NULL);
+      g_assert_null (*destroy_notify);
+
+      *data = g_strdup ("hello");
+      *destroy_notify = g_free;
+    }
+  else if (strcmp (op, "remove") == 0)
+    {
+      g_assert_cmpstr (data_entry, ==, "hello");
+      g_assert_true (*destroy_notify == g_free);
+
+      g_free (data_entry);
+      *data = NULL;
+    }
+  else
+    g_assert_not_reached ();
+
+  return "result";
+}
+
+static void
+test_datalist_update_atomic (void)
+{
+  GQuark one = g_quark_from_static_string ("one");
+  GData *list = NULL;
+  const char *result;
+
+  result = _g_datalist_id_update_atomic (&list, one, _update_atomic_cb, "create");
+  g_assert_cmpstr (result, ==, "result");
+  g_assert_cmpstr ((const char *) g_datalist_id_get_data (&list, one), ==, "hello");
+
+  g_datalist_id_set_data_full (&list, one, g_strdup ("hello"), g_free);
+
+  result = _g_datalist_id_update_atomic (&list, one, _update_atomic_cb, "remove");
+  g_assert_cmpstr (result, ==, "result");
+
+  g_assert_null (list);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -335,8 +466,10 @@ main (int argc, char *argv[])
   g_test_add_func ("/datalist/id", test_datalist_id);
   g_test_add_func ("/datalist/recursive-clear", test_datalist_clear);
   g_test_add_func ("/datalist/id-remove-multiple", test_datalist_id_remove_multiple);
+  g_test_add_func ("/datalist/id-remove-multiple/resize", test_datalist_id_remove_multiple_resize);
   g_test_add_func ("/datalist/id-remove-multiple-destroy-order",
                    test_datalist_id_remove_multiple_destroy_order);
+  g_test_add_func ("/datalist/update-atomic", test_datalist_update_atomic);
 
   return g_test_run ();
 }
index ea3495c..99cb8ee 100644 (file)
@@ -189,8 +189,6 @@ test_private (void)
 
   /* Cleaning left over files */
   g_remove ("maptest");
-
-  g_test_message ("test_private: ok");
 }
 
 static void
@@ -283,8 +281,6 @@ test_child_private (void)
   /* Cleaning left over files */
   g_remove ("mapchild");
   g_remove ("maptest");
-
-  g_test_message ("test_child_private: ok");
 }
 
 int
@@ -303,6 +299,7 @@ main (int argc,
     }
 #endif
 
+  g_test_init (&argc, &argv, NULL);
   local_argv = argv;
 
   if (argc > 1)
@@ -311,8 +308,6 @@ main (int argc,
       return EXIT_SUCCESS;
     }
 
-  g_test_init (&argc, &argv, NULL);
-
   g_test_add_func ("/mapping/flags", test_mapping_flags);
   g_test_add_func ("/mapping/private", test_private);
   g_test_add_func ("/mapping/private-child", test_child_private);
index 83aaad9..c4179eb 100644 (file)
@@ -395,11 +395,11 @@ test_subprocess_timeout (void)
     {
       /* loop and sleep forever */
       while (TRUE)
-        g_usleep (1000 * 1000);
+        g_usleep (G_USEC_PER_SEC);
       return;
     }
   /* allow child to run for only a fraction of a second */
-  g_test_trap_subprocess (NULL, 0.11 * 1000000, G_TEST_SUBPROCESS_DEFAULT);
+  g_test_trap_subprocess (NULL, 0.05 * G_USEC_PER_SEC, G_TEST_SUBPROCESS_DEFAULT);
   g_test_trap_assert_failed ();
   g_assert_true (g_test_trap_reached_timeout ());
 }
@@ -901,18 +901,6 @@ test_incomplete (void)
 }
 
 static void
-test_subprocess_timed_out (void)
-{
-  if (g_test_subprocess ())
-    {
-      g_usleep (1000000);
-      return;
-    }
-  g_test_trap_subprocess (NULL, 50000, G_TEST_SUBPROCESS_DEFAULT);
-  g_assert_true (g_test_trap_reached_timeout ());
-}
-
-static void
 test_path_first (void)
 {
   g_assert_cmpstr (g_test_get_path (), ==, "/misc/path/first");
@@ -2926,8 +2914,7 @@ main (int   argc,
 
   g_test_add_func ("/trap_subprocess/fail", test_subprocess_fail);
   g_test_add_func ("/trap_subprocess/no-such-test", test_subprocess_no_such_test);
-  if (g_test_slow ())
-    g_test_add_func ("/trap_subprocess/timeout", test_subprocess_timeout);
+  g_test_add_func ("/trap_subprocess/timeout", test_subprocess_timeout);
   g_test_add_func ("/trap_subprocess/envp", test_subprocess_envp);
 
   g_test_add_func ("/trap_subprocess/patterns", test_subprocess_patterns);
@@ -2971,7 +2958,6 @@ main (int   argc,
   g_test_add_func ("/misc/combining/subprocess/pass", test_pass);
   g_test_add_func ("/misc/fail", test_fail);
   g_test_add_func ("/misc/incomplete", test_incomplete);
-  g_test_add_func ("/misc/timeout", test_subprocess_timed_out);
 
   g_test_add_func ("/misc/path/first", test_path_first);
   g_test_add_func ("/misc/path/second", test_path_second);
index e4003f7..1defb3d 100644 (file)
 #include "testutils.h"
 
 static void
+async_signal_safe_message (const char *message)
+{
+  if (write (2, message, strlen (message)) < 0 ||
+      write (2, "\n", 1) < 0)
+    {
+      /* ignore: not much we can do */
+    }
+}
+
+static void
+test_closefrom (void)
+{
+  /* Enough file descriptors to be confident that we're operating on
+   * all of them */
+  const int N_FDS = 20;
+  int *fds;
+  int fd;
+  int i;
+  pid_t child;
+  int wait_status;
+
+  /* The loop that populates @fds with pipes assumes this */
+  g_assert (N_FDS % 2 == 0);
+
+  g_test_summary ("Test g_closefrom(), g_fdwalk_set_cloexec()");
+  g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3247");
+
+  for (fd = 0; fd <= 2; fd++)
+    {
+      int flags;
+
+      g_assert_no_errno ((flags = fcntl (fd, F_GETFD)));
+      g_assert_no_errno (fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC));
+    }
+
+  fds = g_new0 (int, N_FDS);
+
+  for (i = 0; i < N_FDS; i += 2)
+    {
+      GError *error = NULL;
+      int pipefd[2];
+      int res;
+
+      /* Intentionally neither O_CLOEXEC nor FD_CLOEXEC */
+      res = g_unix_open_pipe (pipefd, 0, &error);
+      g_assert (res);
+      g_assert_no_error (error);
+      g_clear_error (&error);
+      fds[i] = pipefd[0];
+      fds[i + 1] = pipefd[1];
+    }
+
+  child = fork ();
+
+  /* Child process exits with status = 100 + the first wrong fd,
+   * or 0 if all were correct */
+  if (child == 0)
+    {
+      for (i = 0; i < N_FDS; i++)
+        {
+          int flags = fcntl (fds[i], F_GETFD);
+
+          if (flags == -1)
+            {
+              async_signal_safe_message ("fd should not have been closed");
+              _exit (100 + fds[i]);
+            }
+
+          if (flags & FD_CLOEXEC)
+            {
+              async_signal_safe_message ("fd should not have been close-on-exec yet");
+              _exit (100 + fds[i]);
+            }
+        }
+
+      g_fdwalk_set_cloexec (3);
+
+      for (i = 0; i < N_FDS; i++)
+        {
+          int flags = fcntl (fds[i], F_GETFD);
+
+          if (flags == -1)
+            {
+              async_signal_safe_message ("fd should not have been closed");
+              _exit (100 + fds[i]);
+            }
+
+          if (!(flags & FD_CLOEXEC))
+            {
+              async_signal_safe_message ("fd should have been close-on-exec");
+              _exit (100 + fds[i]);
+            }
+        }
+
+      g_closefrom (3);
+
+      for (fd = 0; fd <= 2; fd++)
+        {
+          int flags = fcntl (fd, F_GETFD);
+
+          if (flags == -1)
+            {
+              async_signal_safe_message ("fd should not have been closed");
+              _exit (100 + fd);
+            }
+
+          if (flags & FD_CLOEXEC)
+            {
+              async_signal_safe_message ("fd should not have been close-on-exec");
+              _exit (100 + fd);
+            }
+        }
+
+      for (i = 0; i < N_FDS; i++)
+        {
+          if (fcntl (fds[i], F_GETFD) != -1 || errno != EBADF)
+            {
+              async_signal_safe_message ("fd should have been closed");
+              _exit (100 + fds[i]);
+            }
+        }
+
+      _exit (0);
+    }
+
+  g_assert_no_errno (waitpid (child, &wait_status, 0));
+
+  if (WIFEXITED (wait_status))
+    {
+      int exit_status = WEXITSTATUS (wait_status);
+
+      if (exit_status != 0)
+        g_test_fail_printf ("File descriptor %d in incorrect state", exit_status - 100);
+    }
+  else
+    {
+      g_test_fail_printf ("Unexpected wait status %d", wait_status);
+    }
+
+  for (i = 0; i < N_FDS; i++)
+    {
+      GError *error = NULL;
+
+      g_close (fds[i], &error);
+      g_assert_no_error (error);
+      g_clear_error (&error);
+    }
+
+  g_free (fds);
+
+  if (g_test_undefined ())
+    {
+      g_test_trap_subprocess ("/glib-unix/closefrom/subprocess/einval",
+                              0, G_TEST_SUBPROCESS_DEFAULT);
+      g_test_trap_assert_passed ();
+    }
+}
+
+static void
+test_closefrom_subprocess_einval (void)
+{
+  int res;
+  int errsv;
+
+  g_log_set_always_fatal (G_LOG_FATAL_MASK);
+  g_log_set_fatal_mask ("GLib", G_LOG_FATAL_MASK);
+
+  errno = 0;
+  res = g_closefrom (-1);
+  errsv = errno;
+  g_assert_cmpint (res, ==, -1);
+  g_assert_cmpint (errsv, ==, EINVAL);
+
+  errno = 0;
+  res = g_fdwalk_set_cloexec (-42);
+  errsv = errno;
+  g_assert_cmpint (res, ==, -1);
+  g_assert_cmpint (errsv, ==, EINVAL);
+}
+
+static void
 test_pipe (void)
 {
   GError *error = NULL;
@@ -140,7 +321,7 @@ test_pipe_struct (void)
 
   g_test_summary ("Test GUnixPipe structure");
 
-  res = g_unix_pipe_open (&pair, FD_CLOEXEC, &error);
+  res = g_unix_pipe_open (&pair, O_CLOEXEC, &error);
   g_assert_no_error (error);
   g_assert_true (res);
 
@@ -199,7 +380,7 @@ test_pipe_struct_auto (void)
           GError *error = NULL;
           gboolean res;
 
-          res = g_unix_pipe_open (&pair, FD_CLOEXEC, &error);
+          res = g_unix_pipe_open (&pair, O_CLOEXEC, &error);
           g_assert_no_error (error);
           g_assert_true (res);
 
@@ -613,6 +794,9 @@ main (int   argc,
 {
   g_test_init (&argc, &argv, NULL);
 
+  g_test_add_func ("/glib-unix/closefrom", test_closefrom);
+  g_test_add_func ("/glib-unix/closefrom/subprocess/einval",
+                   test_closefrom_subprocess_einval);
   g_test_add_func ("/glib-unix/pipe", test_pipe);
   g_test_add_func ("/glib-unix/pipe/fd-cloexec", test_pipe_fd_cloexec);
   g_test_add_func ("/glib-unix/pipe-stdio-overwrite", test_pipe_stdio_overwrite);
index 0f31c60..7ec5902 100644 (file)
@@ -108,6 +108,7 @@ enum {
 #define OPTIONAL_FLAG_HAS_SIGNAL_HANDLER (1 << 1) /* Set if object ever had a signal handler */
 #define OPTIONAL_FLAG_HAS_NOTIFY_HANDLER (1 << 2) /* Same, specifically for "notify" */
 #define OPTIONAL_FLAG_LOCK               (1 << 3) /* _OPTIONAL_BIT_LOCK */
+#define OPTIONAL_FLAG_EVER_HAD_WEAK_REF  (1 << 4) /* whether on the object ever g_weak_ref_set() was called. */
 
 /* We use g_bit_lock(), which only supports one lock per integer.
  *
@@ -206,10 +207,11 @@ static void       g_object_dispatch_properties_changed    (GObject        *object,
                                                         GParamSpec    **pspecs);
 static guint               object_floating_flag_handler (GObject        *object,
                                                          gint            job);
+static inline void object_set_optional_flags (GObject *object,
+                                              guint flags);
 
 static void object_interface_check_properties           (gpointer        check_data,
                                                         gpointer        g_iface);
-static void                weak_locations_free_unlocked (GSList **weak_locations);
 
 /* --- typedefs --- */
 typedef struct _GObjectNotifyQueue            GObjectNotifyQueue;
@@ -229,9 +231,7 @@ static GQuark               quark_notify_queue;
 static GParamSpecPool      *pspec_pool = NULL;
 static gulong              gobject_signals[LAST_SIGNAL] = { 0, };
 static guint (*floating_flag_handler) (GObject*, gint) = object_floating_flag_handler;
-/* qdata pointing to GSList<GWeakRef *>, protected by weak_locations_lock */
 static GQuark              quark_weak_locations = 0;
-static GRWLock              weak_locations_lock;
 
 #if HAVE_PRIVATE
 G_ALWAYS_INLINE static inline GObjectPrivate *
@@ -251,6 +251,365 @@ object_get_optional_flags_p (GObject *object)
 #endif
 }
 
+/*****************************************************************************/
+
+/* For GWeakRef, we need to take a lock per-object. However, in various cases
+ * we cannot take a strong reference on the object to keep it alive. So the
+ * mutex cannot be in the object itself, because when we want to release the
+ * lock, we can no longer access object.
+ *
+ * Instead, the mutex is on the WeakRefData, which is itself ref-counted
+ * and has a separate lifetime from the object. */
+typedef struct
+{
+  /* This is both an atomic ref-count and bit 30 (WEAK_REF_DATA_LOCK_BIT) is
+   * used for g_bit_lock(). */
+  gint atomic_field;
+
+  guint16 len;
+
+  /* Only relevant when len > 1. In that case, it's the allocated size of
+   * "list.many" array.  */
+  guint16 alloc;
+
+  /* Only relevant when len > 0. In that case, either "one" or "many" union
+   * field is in use. */
+  union
+  {
+    GWeakRef *one;
+    GWeakRef **many;
+  } list;
+} WeakRefData;
+
+/* We choose bit 30, and not bit 31. Bit 31 would be the sign for gint, so it
+ * a bit awkward to use. Note that it probably also would work fine.
+ *
+ * But 30 is ok, because it still leaves us space for 2^30-1 references, which
+ * is more than we ever need. */
+#define WEAK_REF_DATA_LOCK_BIT 30
+
+static void weak_ref_data_clear_list (WeakRefData *wrdata, GObject *object);
+
+static WeakRefData *
+weak_ref_data_ref (WeakRefData *wrdata)
+{
+  gint ref;
+
+#if G_ENABLE_DEBUG
+  g_assert (wrdata);
+#endif
+
+  ref = g_atomic_int_add (&wrdata->atomic_field, 1);
+
+#if G_ENABLE_DEBUG
+  /* Overflow is almost impossible to happen, because the user would need to
+   * spawn that many operating system threads, that all call
+   * g_weak_ref_{set,get}() in parallel.
+   *
+   * Still, assert in debug mode. */
+  g_assert (ref < G_MAXINT32);
+
+  /* the real ref-count would be the following: */
+  ref = (ref + 1) & ~(1 << WEAK_REF_DATA_LOCK_BIT);
+
+  /* assert that the ref-count is still in the valid range. */
+  g_assert (ref > 0 && ref < (1 << WEAK_REF_DATA_LOCK_BIT));
+#endif
+  (void) ref;
+
+  return wrdata;
+}
+
+static void
+weak_ref_data_unref (WeakRefData *wrdata)
+{
+  if (!wrdata)
+    return;
+
+  /* Note that we also use WEAK_REF_DATA_LOCK_BIT on "atomic_field" as a bit
+   * lock. However, we will always keep the @wrdata alive (having a reference)
+   * while holding a lock (otherwise, we couldn't unlock anymore). Thus, at the
+   * point when we decrement the ref-count to zero, we surely also have the
+   * @wrdata unlocked.
+   *
+   * This means, using "aomit_field" both as ref-count and the lock bit is
+   * fine. */
+
+  if (!g_atomic_int_dec_and_test (&wrdata->atomic_field))
+    return;
+
+#if G_ENABLE_DEBUG
+  /* We expect that the list of weak locations is empty at this point.
+   * During g_object_unref() (_object_unref_clear_weak_locations()) it
+   * should have been cleared.
+   *
+   * Calling weak_ref_data_clear_list() should be unnecessary. */
+  g_assert (wrdata->len == 0);
+#endif
+
+  g_free_sized (wrdata, sizeof (WeakRefData));
+}
+
+static void
+weak_ref_data_lock (WeakRefData *wrdata)
+{
+  /* Note that while holding a _weak_ref_lock() on the @weak_ref, we MUST not acquire a
+   * weak_ref_data_lock() on the @wrdata. The other way around! */
+  if (wrdata)
+    g_bit_lock (&wrdata->atomic_field, WEAK_REF_DATA_LOCK_BIT);
+}
+
+static void
+weak_ref_data_unlock (WeakRefData *wrdata)
+{
+  if (wrdata)
+    g_bit_unlock (&wrdata->atomic_field, WEAK_REF_DATA_LOCK_BIT);
+}
+
+static gpointer
+weak_ref_data_get_or_create_cb (GQuark key_id,
+                                gpointer *data,
+                                GDestroyNotify *destroy_notify,
+                                gpointer user_data)
+{
+  WeakRefData *wrdata = *data;
+  GObject *object = user_data;
+
+  if (!wrdata)
+    {
+      wrdata = g_new (WeakRefData, 1);
+
+      /* The initial ref-count is 1. This one is owned by the GData until the
+       * object gets destroyed.
+       *
+       * The WEAK_REF_DATA_LOCK_BIT bit is of course initially unset.  */
+      wrdata->atomic_field = 1;
+      wrdata->len = 0;
+      /* Other fields are left uninitialized. They are only considered with a positive @len. */
+
+      *data = wrdata;
+      *destroy_notify = (GDestroyNotify) weak_ref_data_unref;
+
+      /* Mark the @object that it was ever involved with GWeakRef. This flag
+       * will stick until @object gets destroyed, just like the WeakRefData
+       * also won't be freed for the remainder of the life of @object. */
+      object_set_optional_flags (object, OPTIONAL_FLAG_EVER_HAD_WEAK_REF);
+    }
+
+  return wrdata;
+}
+
+static WeakRefData *
+weak_ref_data_get_or_create (GObject *object)
+{
+  if (!object)
+    return NULL;
+
+  return _g_datalist_id_update_atomic (&object->qdata,
+                                       quark_weak_locations,
+                                       weak_ref_data_get_or_create_cb,
+                                       object);
+}
+
+static WeakRefData *
+weak_ref_data_get (GObject *object)
+{
+  return g_datalist_id_get_data (&object->qdata, quark_weak_locations);
+}
+
+static WeakRefData *
+weak_ref_data_get_surely (GObject *object)
+{
+  WeakRefData *wrdata;
+
+  /* The "surely" part is about that we expect to have a WeakRefData.
+   *
+   * Note that once a GObject gets a WeakRefData (during g_weak_ref_set() and
+   * weak_ref_data_get_or_create()), it sticks and is not freed until the
+   * object gets destroyed.
+   *
+   * Maybe we could release the unused WeakRefData in g_weak_ref_set(), but
+   * then we would always need to take a reference during weak_ref_data_get().
+   * That is likely not worth it. */
+
+  wrdata = weak_ref_data_get (object);
+#if G_ENABLE_DEBUG
+  g_assert (wrdata);
+#endif
+  return wrdata;
+}
+
+static gint32
+weak_ref_data_list_find (WeakRefData *wrdata, GWeakRef *weak_ref)
+{
+  if (wrdata->len == 1u)
+    {
+      if (wrdata->list.one == weak_ref)
+        return 0;
+    }
+  else
+    {
+      guint16 i;
+
+      for (i = 0; i < wrdata->len; i++)
+        {
+          if (wrdata->list.many[i] == weak_ref)
+            return i;
+        }
+    }
+
+  return -1;
+}
+
+static gboolean
+weak_ref_data_list_add (WeakRefData *wrdata, GWeakRef *weak_ref)
+{
+  if (wrdata->len == 0u)
+    wrdata->list.one = weak_ref;
+  else
+    {
+      if (wrdata->len == 1u)
+        {
+          GWeakRef *weak_ref2 = wrdata->list.one;
+
+          wrdata->alloc = 4u;
+          wrdata->list.many = g_new (GWeakRef *, wrdata->alloc);
+          wrdata->list.many[0] = weak_ref2;
+        }
+      else if (wrdata->len == wrdata->alloc)
+        {
+          guint16 alloc;
+
+          alloc = wrdata->alloc * 2u;
+          if (G_UNLIKELY (alloc < wrdata->len))
+            {
+              if (wrdata->len == G_MAXUINT16)
+                return FALSE;
+              alloc = G_MAXUINT16;
+            }
+          wrdata->list.many = g_renew (GWeakRef *, wrdata->list.many, alloc);
+          wrdata->alloc = alloc;
+        }
+
+      wrdata->list.many[wrdata->len] = weak_ref;
+    }
+
+  wrdata->len++;
+  return TRUE;
+}
+
+static GWeakRef *
+weak_ref_data_list_remove (WeakRefData *wrdata, guint16 idx, gboolean allow_shrink)
+{
+  GWeakRef *weak_ref;
+
+#if G_ENABLE_DEBUG
+  g_assert (idx < wrdata->len);
+#endif
+
+  wrdata->len--;
+
+  if (wrdata->len == 0u)
+    {
+      weak_ref = wrdata->list.one;
+    }
+  else
+    {
+      weak_ref = wrdata->list.many[idx];
+
+      if (wrdata->len == 1u)
+        {
+          GWeakRef *weak_ref2 = wrdata->list.many[idx == 0 ? 1 : 0];
+
+          g_free (wrdata->list.many);
+          wrdata->list.one = weak_ref2;
+        }
+      else
+        {
+          wrdata->list.many[idx] = wrdata->list.many[wrdata->len];
+
+          if (allow_shrink && G_UNLIKELY (wrdata->len <= wrdata->alloc / 4u))
+            {
+              /* Shrink the buffer. When 75% are empty, shrink it to 50%. */
+              if (wrdata->alloc == G_MAXUINT16)
+                wrdata->alloc = ((guint32) G_MAXUINT16 + 1u) / 2u;
+              else
+                wrdata->alloc /= 2u;
+              wrdata->list.many = g_renew (GWeakRef *, wrdata->list.many, wrdata->alloc);
+            }
+        }
+    }
+
+  return weak_ref;
+}
+
+static gboolean
+weak_ref_data_has (GObject *object, WeakRefData *wrdata, WeakRefData **out_new_wrdata)
+{
+  WeakRefData *wrdata2;
+
+  /* Check whether @object has @wrdata as WeakRefData. Note that an GObject's
+   * WeakRefData never changes (until destruction, once it's allocated).
+   *
+   * If you thus hold a reference to a @wrdata, you can check that the @object
+   * is still the same as the object where we got the @wrdata originally from.
+   *
+   * You couldn't do this check by using pointer equality of the GObject pointers,
+   * when you cannot hold strong references on the objects involved. Because then
+   * the object pointer might be dangling (and even destroyed and recreated as another
+   * object at the same memory location).
+   *
+   * Basically, weak_ref_data_has() is to compare for equality of two GObject pointers,
+   * when we cannot hold a strong reference on both. Instead, we earlier took a reference
+   * on the @wrdata and compare that instead.
+   */
+
+  if (!object)
+    {
+      /* If @object is NULL, then it does have a NULL @wrdata, and we return
+       * TRUE in the case.  That's a convenient special case for some callers.
+       *
+       * In other words, weak_ref_data_has(NULL, NULL, out_new_wrdata) is TRUE.
+       */
+#if G_ENABLE_DEBUG
+      g_assert (!out_new_wrdata);
+#endif
+      return !wrdata;
+    }
+
+  if (!wrdata)
+    {
+      /* We only call this function with an @object that was previously
+       * registered as GWeakRef.
+       *
+       * That means, our @object will have a wrdata, and the result of the
+       * evaluation will be %FALSE. */
+      if (out_new_wrdata)
+        *out_new_wrdata = weak_ref_data_ref (weak_ref_data_get (object));
+#if G_ENABLE_DEBUG
+      g_assert (out_new_wrdata
+                    ? *out_new_wrdata
+                    : weak_ref_data_get (object));
+#endif
+      return FALSE;
+    }
+
+  wrdata2 = weak_ref_data_get_surely (object);
+
+  if (wrdata == wrdata2)
+    {
+      if (out_new_wrdata)
+        *out_new_wrdata = NULL;
+      return TRUE;
+    }
+
+  if (out_new_wrdata)
+    *out_new_wrdata = weak_ref_data_ref (wrdata2);
+  return FALSE;
+}
+
+/*****************************************************************************/
+
 #if defined(G_ENABLE_DEBUG) && defined(G_THREAD_LOCAL)
 /* Using this thread-local global is sufficient to guard the per-object
  * locking, because while the current thread holds a lock on one object, it
@@ -613,9 +972,7 @@ g_object_base_class_finalize (GObjectClass *class)
 static void
 g_object_do_class_init (GObjectClass *class)
 {
-  /* read the comment about typedef struct CArray; on why not to change this quark */
   quark_closure_array = g_quark_from_static_string ("GObject-closure-array");
-
   quark_weak_notifies = g_quark_from_static_string ("GObject-weak-notifies");
   quark_weak_locations = g_quark_from_static_string ("GObject-weak-locations");
   quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");
@@ -1471,14 +1828,25 @@ g_object_dispatch_properties_changed (GObject     *object,
 void
 g_object_run_dispose (GObject *object)
 {
+  WeakRefData *wrdata;
+
   g_return_if_fail (G_IS_OBJECT (object));
   g_return_if_fail (g_atomic_int_get (&object->ref_count) > 0);
 
   g_object_ref (object);
+
   TRACE (GOBJECT_OBJECT_DISPOSE(object,G_TYPE_FROM_INSTANCE(object), 0));
   G_OBJECT_GET_CLASS (object)->dispose (object);
   TRACE (GOBJECT_OBJECT_DISPOSE_END(object,G_TYPE_FROM_INSTANCE(object), 0));
-  g_datalist_id_remove_data (&object->qdata, quark_weak_locations);
+
+  if ((object_get_optional_flags (object) & OPTIONAL_FLAG_EVER_HAD_WEAK_REF))
+    {
+      wrdata = weak_ref_data_get_surely (object);
+      weak_ref_data_lock (wrdata);
+      weak_ref_data_clear_list (wrdata, object);
+      weak_ref_data_unlock (wrdata);
+    }
+
   g_object_unref (object);
 }
 
@@ -3841,77 +4209,56 @@ gpointer
 static gboolean
 _object_unref_clear_weak_locations (GObject *object, gint *p_old_ref, gboolean do_unref)
 {
-  GSList **weak_locations;
+  WeakRefData *wrdata;
+  gboolean success;
 
-  if (do_unref)
+  /* Fast path, for objects that never had a GWeakRef registered. */
+  if (!(object_get_optional_flags (object) & OPTIONAL_FLAG_EVER_HAD_WEAK_REF))
     {
-      gboolean unreffed = FALSE;
-
-      /* Fast path for the final unref using a read-lck only. We check whether
-       * we have weak_locations and drop ref count to zero under a reader lock. */
-
-      g_rw_lock_reader_lock (&weak_locations_lock);
-
-      weak_locations = g_datalist_id_get_data (&object->qdata, quark_weak_locations);
-      if (!weak_locations)
+      /* The caller previously just checked atomically that the ref-count was
+       * one.
+       *
+       * At this point still, @object never ever had a GWeakRef registered.
+       * That means, nobody else holds a strong reference and also nobody else
+       * can hold a weak reference, to race against obtaining another
+       * reference. We are good to proceed. */
+      if (do_unref)
         {
-          unreffed = g_atomic_int_compare_and_exchange_full ((int *) &object->ref_count,
-                                                             1, 0,
-                                                             p_old_ref);
-          g_rw_lock_reader_unlock (&weak_locations_lock);
-          return unreffed;
+          if (!g_atomic_int_compare_and_exchange ((gint *) &object->ref_count, 1, 0))
+            {
+#if G_ENABLE_DEBUG
+              g_assert_not_reached ();
+#endif
+            }
         }
+      return TRUE;
+    }
 
-      g_rw_lock_reader_unlock (&weak_locations_lock);
-
-      /* We have weak-locations. Note that we are here already after dispose(). That
-       * means, during dispose a GWeakRef was registered (very unusual). */
+  /* Slow path. We must obtain a lock on the @wrdata, to atomically release
+   * weak references and check that the ref count is as expected. */
 
-      g_rw_lock_writer_lock (&weak_locations_lock);
+  wrdata = weak_ref_data_get_surely (object);
 
-      if (!g_atomic_int_compare_and_exchange_full ((int *) &object->ref_count,
-                                                   1, 0,
-                                                   p_old_ref))
-        {
-          g_rw_lock_writer_unlock (&weak_locations_lock);
-          return FALSE;
-        }
+  weak_ref_data_lock (wrdata);
 
-      weak_locations = g_datalist_id_remove_no_notify (&object->qdata, quark_weak_locations);
-      g_clear_pointer (&weak_locations, weak_locations_free_unlocked);
-
-      g_rw_lock_writer_unlock (&weak_locations_lock);
-      return TRUE;
+  if (do_unref)
+    {
+      success = g_atomic_int_compare_and_exchange_full ((gint *) &object->ref_count,
+                                                        1, 0,
+                                                        p_old_ref);
     }
-
-  weak_locations = g_datalist_id_get_data (&object->qdata, quark_weak_locations);
-  if (weak_locations != NULL)
+  else
     {
-      g_rw_lock_writer_lock (&weak_locations_lock);
-
-      *p_old_ref = g_atomic_int_get (&object->ref_count);
-      if (*p_old_ref != 1)
-        {
-          g_rw_lock_writer_unlock (&weak_locations_lock);
-          return FALSE;
-        }
+      *p_old_ref = g_atomic_int_get ((gint *) &object->ref_count);
+      success = (*p_old_ref == 1);
+    }
 
-      weak_locations = g_datalist_id_remove_no_notify (&object->qdata, quark_weak_locations);
-      g_clear_pointer (&weak_locations, weak_locations_free_unlocked);
+  if (success)
+    weak_ref_data_clear_list (wrdata, object);
 
-      g_rw_lock_writer_unlock (&weak_locations_lock);
-      return TRUE;
-    }
+  weak_ref_data_unlock (wrdata);
 
-  /* We don't need to re-fetch p_old_ref or check that it's still 1. The caller
-   * did that already. We are good.
-   *
-   * Note that in this case we fetched old_ref and weak_locations separately,
-   * without a lock. But this is fine. We are still before calling dispose().
-   * If there is a race at this point, the same race can happen between
-   * _object_unref_clear_weak_locations() and dispose() call. That is handled
-   * just fine. */
-  return TRUE;
+  return success;
 }
 
 /**
@@ -4034,6 +4381,10 @@ retry_beginning:
   G_OBJECT_GET_CLASS (object)->dispose (object);
   TRACE (GOBJECT_OBJECT_DISPOSE_END (object, G_TYPE_FROM_INSTANCE (object), 1));
 
+  /* Must re-fetch old-ref. _object_unref_clear_weak_locations() relies on
+   * that.  */
+  old_ref = g_atomic_int_get (&object->ref_count);
+
 retry_decrement:
   /* Here, old_ref is 1 if we just come from dispose(). If the object was resurrected,
    * we can hit `goto retry_decrement` and be here with a larger old_ref. */
@@ -4044,6 +4395,15 @@ retry_decrement:
        * queue. */
       g_object_notify_queue_thaw (object, nqueue, FALSE);
       nqueue = NULL;
+
+      /* Note at this point, @old_ref might be wrong.
+       *
+       * Also note that _object_unref_clear_weak_locations() requires that we
+       * atomically checked that @old_ref is 1. However, as @old_ref is larger
+       * than 1, that will not be called. Instead, all other code paths below,
+       * handle the possibility of a bogus @old_ref.
+       *
+       * No need to re-fetch. */
     }
 
   if (old_ref > 2)
@@ -4082,9 +4442,8 @@ retry_decrement:
       return;
     }
 
-  /* old_ref is 1, we are about to drop the reference count to zero. That is
-   * done by _object_unref_clear_weak_locations() under a weak_locations_lock
-   * so that there is no race with g_weak_ref_set(). */
+  /* old_ref is (atomically!) checked to be 1, we are about to drop the
+   * reference count to zero in _object_unref_clear_weak_locations(). */
   if (!_object_unref_clear_weak_locations (object, &old_ref, TRUE))
     goto retry_decrement;
 
@@ -4801,24 +5160,6 @@ typedef struct {
   guint     n_closures;
   GClosure *closures[1]; /* flexible array */
 } CArray;
-/* don't change this structure without supplying an accessor for
- * watched closures, e.g.:
- * GSList* g_object_list_watched_closures (GObject *object)
- * {
- *   CArray *carray;
- *   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
- *   carray = g_object_get_data (object, "GObject-closure-array");
- *   if (carray)
- *     {
- *       GSList *slist = NULL;
- *       guint i;
- *       for (i = 0; i < carray->n_closures; i++)
- *         slist = g_slist_prepend (slist, carray->closures[i]);
- *       return slist;
- *     }
- *   return NULL;
- * }
- */
 
 static void
 object_remove_closure (gpointer  data,
@@ -5064,6 +5405,203 @@ g_initially_unowned_class_init (GInitiallyUnownedClass *klass)
  * without first having or creating a strong reference to the object.
  */
 
+#define WEAK_REF_LOCK_BIT 0
+
+static GObject *
+_weak_ref_clean_pointer (gpointer ptr)
+{
+  /* Drop the lockbit WEAK_REF_LOCK_BIT from @ptr (if set). */
+  return g_pointer_bit_lock_mask_ptr (ptr, WEAK_REF_LOCK_BIT, FALSE, 0, NULL);
+}
+
+static void
+_weak_ref_lock (GWeakRef *weak_ref, GObject **out_object)
+{
+  /* Note that while holding a _weak_ref_lock() on the @weak_ref, we MUST not acquire a
+   * weak_ref_data_lock() on the @wrdata. The other way around! */
+
+  if (out_object)
+    {
+      guintptr ptr;
+
+      g_pointer_bit_lock_and_get (&weak_ref->priv.p, WEAK_REF_LOCK_BIT, &ptr);
+      *out_object = _weak_ref_clean_pointer ((gpointer) ptr);
+    }
+  else
+    g_pointer_bit_lock (&weak_ref->priv.p, WEAK_REF_LOCK_BIT);
+}
+
+static void
+_weak_ref_unlock (GWeakRef *weak_ref)
+{
+  g_pointer_bit_unlock (&weak_ref->priv.p, WEAK_REF_LOCK_BIT);
+}
+
+static void
+_weak_ref_unlock_and_set (GWeakRef *weak_ref, GObject *object)
+{
+  g_pointer_bit_unlock_and_set (&weak_ref->priv.p, WEAK_REF_LOCK_BIT, object, 0);
+}
+
+static void
+weak_ref_data_clear_list (WeakRefData *wrdata, GObject *object)
+{
+  while (wrdata->len > 0u)
+    {
+      GWeakRef *weak_ref;
+      gpointer ptr;
+
+      /* pass "allow_shrink=FALSE", so we don't reallocate needlessly. We
+       * anyway are about to clear the entire list. */
+      weak_ref = weak_ref_data_list_remove (wrdata, wrdata->len - 1u, FALSE);
+
+      /* Fast-path. Most likely @weak_ref is currently not locked, so we can
+       * just atomically set the pointer to NULL. */
+      ptr = g_atomic_pointer_get (&weak_ref->priv.p);
+#if G_ENABLE_DEBUG
+      g_assert (G_IS_OBJECT (_weak_ref_clean_pointer (ptr)));
+      g_assert (!object || object == _weak_ref_clean_pointer (ptr));
+#endif
+      if (G_LIKELY (ptr == _weak_ref_clean_pointer (ptr)))
+        {
+          /* The pointer is unlocked. Try an atomic compare-and-exchange... */
+          if (g_atomic_pointer_compare_and_exchange (&weak_ref->priv.p, ptr, NULL))
+            {
+              /* Done. Go to the next. */
+              continue;
+            }
+        }
+
+      /* The @weak_ref is locked. Acquire the lock to set the pointer to NULL. */
+      _weak_ref_lock (weak_ref, NULL);
+      _weak_ref_unlock_and_set (weak_ref, NULL);
+    }
+}
+
+static void
+_weak_ref_set (GWeakRef *weak_ref,
+               GObject *new_object,
+               gboolean called_by_init)
+{
+  WeakRefData *old_wrdata;
+  WeakRefData *new_wrdata;
+  GObject *old_object;
+
+  new_wrdata = weak_ref_data_get_or_create (new_object);
+
+#if G_ENABLE_DEBUG
+  g_assert (!new_object || object_get_optional_flags (new_object) & OPTIONAL_FLAG_EVER_HAD_WEAK_REF);
+#endif
+
+  if (called_by_init)
+    {
+      /* The caller is g_weak_ref_init(). We know that the weak_ref should be
+       * NULL. We thus set @old_wrdata to NULL without checking.
+       *
+       * Also important, the caller ensured that @new_object is not NULL. So we
+       * are expected to set @weak_ref from NULL to a non-NULL @new_object. */
+      old_wrdata = NULL;
+#if G_ENABLE_DEBUG
+      g_assert (new_object);
+#endif
+    }
+  else
+    {
+      /* We must get a wrdata object @old_wrdata for the current @old_object. */
+      _weak_ref_lock (weak_ref, &old_object);
+
+      if (old_object == new_object)
+        {
+          /* Already set. We are done. */
+          _weak_ref_unlock (weak_ref);
+          return;
+        }
+
+      old_wrdata = old_object
+                       ? weak_ref_data_ref (weak_ref_data_get (old_object))
+                       : NULL;
+      _weak_ref_unlock (weak_ref);
+    }
+
+  /* We need a lock on @old_wrdata, @new_wrdata and @weak_ref. We need to take
+   * these locks in a certain order to avoid deadlock. We sort them by pointer
+   * value.
+   *
+   * Note that @old_wrdata or @new_wrdata may be NULL, which is handled
+   * correctly.
+   *
+   * Note that @old_wrdata and @new_wrdata are never identical at this point.
+   */
+  if (new_wrdata && old_wrdata && (((guintptr) (gpointer) old_wrdata) < ((guintptr) ((gpointer) new_wrdata))))
+    {
+      weak_ref_data_lock (old_wrdata);
+      weak_ref_data_lock (new_wrdata);
+    }
+  else
+    {
+      weak_ref_data_lock (new_wrdata);
+      weak_ref_data_lock (old_wrdata);
+    }
+  _weak_ref_lock (weak_ref, &old_object);
+
+  if (!weak_ref_data_has (old_object, old_wrdata, NULL))
+    {
+      /* A race. @old_object no longer has the expected @old_wrdata after
+       * getting all the locks. */
+      if (old_object)
+        {
+          /* We lost the race and find a different object set. It's fine, our
+           * action was lost in the race and we are done. No need to retry. */
+          weak_ref_data_unlock (old_wrdata);
+          weak_ref_data_unlock (new_wrdata);
+          _weak_ref_unlock (weak_ref);
+          weak_ref_data_unref (old_wrdata);
+          return;
+        }
+
+      /* @old_object is NULL after a race. We didn't expect that, but it's
+       * fine. Proceed to set @new_object... */
+    }
+
+  if (old_object)
+    {
+      gint32 idx;
+
+      idx = weak_ref_data_list_find (old_wrdata, weak_ref);
+      if (idx < 0)
+        g_critical ("unexpected missing GWeakRef data");
+      else
+        weak_ref_data_list_remove (old_wrdata, idx, TRUE);
+    }
+
+  weak_ref_data_unlock (old_wrdata);
+
+  if (new_object)
+    {
+#if G_ENABLE_DEBUG
+      g_assert (weak_ref_data_list_find (new_wrdata, weak_ref) < 0);
+#endif
+      if (g_atomic_int_get (&new_object->ref_count) < 1)
+        {
+          g_critical ("calling g_weak_ref_set() with already destroyed object");
+          new_object = NULL;
+        }
+      else
+        {
+          if (!weak_ref_data_list_add (new_wrdata, weak_ref))
+            {
+              g_critical ("Too many GWeakRef registered");
+              new_object = NULL;
+            }
+        }
+    }
+
+  _weak_ref_unlock_and_set (weak_ref, new_object);
+  weak_ref_data_unlock (new_wrdata);
+
+  weak_ref_data_unref (old_wrdata);
+}
+
 /**
  * g_weak_ref_init: (skip)
  * @weak_ref: (inout): uninitialized or empty location for a weak
@@ -5084,12 +5622,19 @@ g_initially_unowned_class_init (GInitiallyUnownedClass *klass)
  */
 void
 g_weak_ref_init (GWeakRef *weak_ref,
-                 gpointer  object)
+                 gpointer object)
 {
-  weak_ref->priv.p = NULL;
+  g_return_if_fail (weak_ref);
+  g_return_if_fail (object == NULL || G_IS_OBJECT (object));
 
+  g_atomic_pointer_set (&weak_ref->priv.p, NULL);
   if (object)
-    g_weak_ref_set (weak_ref, object);
+    {
+      /* We give a hint that the weak_ref is currently NULL. Unlike
+       * g_weak_ref_set(), we then don't need the extra lock just to
+       * find out that we have no object. */
+      _weak_ref_set (weak_ref, object, TRUE);
+    }
 }
 
 /**
@@ -5136,54 +5681,91 @@ g_weak_ref_clear (GWeakRef *weak_ref)
 gpointer
 g_weak_ref_get (GWeakRef *weak_ref)
 {
+  WeakRefData *wrdata;
+  WeakRefData *new_wrdata;
   GToggleNotify toggle_notify = NULL;
   gpointer toggle_data = NULL;
   GObject *object;
 
   g_return_val_if_fail (weak_ref, NULL);
 
-  g_rw_lock_reader_lock (&weak_locations_lock);
-
-  object = weak_ref->priv.p;
-
-  if (object)
-    object = object_ref (object, &toggle_notify, &toggle_data);
+  /* We cannot take the strong reference on @object yet. Otherwise,
+   * _object_unref_clear_weak_locations() might have just taken the lock on
+   * @wrdata, see that the ref-count is 1 and plan to proceed clearing weak
+   * locations. If we then take a strong reference here, the object becomes
+   * alive and well, but _object_unref_clear_weak_locations() would proceed and
+   * clear the @weak_ref.
+   *
+   * We avoid that, by can only taking the strong reference when having a lock
+   * on @wrdata, so we are in sync with _object_unref_clear_weak_locations().
+   *
+   * But first we must get a reference to the @wrdata.
+   */
+  _weak_ref_lock (weak_ref, &object);
+  wrdata = object
+               ? weak_ref_data_ref (weak_ref_data_get (object))
+               : NULL;
+  _weak_ref_unlock (weak_ref);
 
-  g_rw_lock_reader_unlock (&weak_locations_lock);
+  if (!wrdata)
+    {
+      /* There is no @wrdata and no object. We are done. */
+      return NULL;
+    }
 
-  if (toggle_notify)
-    toggle_notify (toggle_data, object, FALSE);
+retry:
 
-  return object;
-}
+  /* Now proceed to get the strong reference. This time with acquiring a lock
+   * on the per-object @wrdata and on @weak_ref.
+   *
+   * As the order in which locks are taken is important, we previously had to
+   * get a _weak_ref_lock(), to obtain the @wrdata. Now we have to lock on the
+   * @wrdata first, and the @weak_ref again. */
+  weak_ref_data_lock (wrdata);
+  _weak_ref_lock (weak_ref, &object);
 
-static void
-weak_locations_free_unlocked (GSList **weak_locations)
-{
-  if (*weak_locations)
+  if (!object)
     {
-      GSList *weak_location;
-
-      for (weak_location = *weak_locations; weak_location;)
+      /* Object is gone in the meantime. That is fine. */
+      new_wrdata = NULL;
+    }
+  else
+    {
+      /* Check that @object still refers to the same object as before. We do
+       * that by comparing the @wrdata object. A GObject keeps its (unique!)
+       * wrdata instance until the end, and since @wrdata is still alive,
+       * @object is the same as before, if-and-only-if its @wrdata is the same.
+       */
+      if (weak_ref_data_has (object, wrdata, &new_wrdata))
         {
-          GWeakRef *weak_ref_location = weak_location->data;
-
-          weak_ref_location->priv.p = NULL;
-          weak_location = g_slist_delete_link (weak_location, weak_location);
+          /* We are (still) good. Take a strong ref while holding the necessary locks. */
+          object = object_ref (object, &toggle_notify, &toggle_data);
+        }
+      else
+        {
+          /* The @object changed and has no longer the same @wrdata. In this
+           * case, we need to start over.
+           *
+           * Note that @new_wrdata references the wrdata of the now current
+           * @object. We will use that during the retry. */
         }
     }
 
-  g_free (weak_locations);
-}
+  _weak_ref_unlock (weak_ref);
+  weak_ref_data_unlock (wrdata);
+  weak_ref_data_unref (wrdata);
 
-static void
-weak_locations_free (gpointer data)
-{
-  GSList **weak_locations = data;
+  if (new_wrdata)
+    {
+      /* There was a race. The object changed. Retry, with @new_wrdata. */
+      wrdata = new_wrdata;
+      goto retry;
+    }
 
-  g_rw_lock_writer_lock (&weak_locations_lock);
-  weak_locations_free_unlocked (weak_locations);
-  g_rw_lock_writer_unlock (&weak_locations_lock);
+  if (toggle_notify)
+    toggle_notify (toggle_data, object, FALSE);
+
+  return object;
 }
 
 /**
@@ -5201,82 +5783,10 @@ weak_locations_free (gpointer data)
  */
 void
 g_weak_ref_set (GWeakRef *weak_ref,
-                gpointer  object)
+                gpointer object)
 {
-  GSList **weak_locations;
-  GObject *new_object;
-  GObject *old_object;
-
   g_return_if_fail (weak_ref != NULL);
   g_return_if_fail (object == NULL || G_IS_OBJECT (object));
 
-  new_object = object;
-
-  g_rw_lock_writer_lock (&weak_locations_lock);
-
-  /* We use the extra level of indirection here so that if we have ever
-   * had a weak pointer installed at any point in time on this object,
-   * we can see that there is a non-NULL value associated with the
-   * weak-pointer quark and know that this value will not change at any
-   * point in the object's lifetime.
-   *
-   * Both properties are important for reducing the amount of times we
-   * need to acquire locks and for decreasing the duration of time the
-   * lock is held while avoiding some rather tricky races.
-   *
-   * Specifically: we can avoid having to do an extra unconditional lock
-   * in g_object_unref() without worrying about some extremely tricky
-   * races.
-   */
-
-  old_object = weak_ref->priv.p;
-  if (new_object != old_object)
-    {
-      weak_ref->priv.p = new_object;
-
-      /* Remove the weak ref from the old object */
-      if (old_object != NULL)
-        {
-          weak_locations = g_datalist_id_get_data (&old_object->qdata, quark_weak_locations);
-          if (weak_locations == NULL)
-            {
-              g_critical ("unexpected missing GWeakRef");
-            }
-          else
-            {
-              *weak_locations = g_slist_remove (*weak_locations, weak_ref);
-
-              if (!*weak_locations)
-                {
-                  weak_locations_free_unlocked (weak_locations);
-                  g_datalist_id_remove_no_notify (&old_object->qdata, quark_weak_locations);
-                }
-            }
-        }
-
-      /* Add the weak ref to the new object */
-      if (new_object != NULL)
-        {
-          if (g_atomic_int_get (&new_object->ref_count) < 1)
-            {
-              weak_ref->priv.p = NULL;
-              g_rw_lock_writer_unlock (&weak_locations_lock);
-              g_critical ("calling g_weak_ref_set() with already destroyed object");
-              return;
-            }
-
-          weak_locations = g_datalist_id_get_data (&new_object->qdata, quark_weak_locations);
-
-          if (weak_locations == NULL)
-            {
-              weak_locations = g_new0 (GSList *, 1);
-              g_datalist_id_set_data_full (&new_object->qdata, quark_weak_locations,
-                                           weak_locations, weak_locations_free);
-            }
-
-          *weak_locations = g_slist_prepend (*weak_locations, weak_ref);
-        }
-    }
-
-  g_rw_lock_writer_unlock (&weak_locations_lock);
+  _weak_ref_set (weak_ref, object, FALSE);
 }
index 7696fc4..9d22135 100644 (file)
@@ -1157,7 +1157,6 @@ test_interface_default_init (TestInterfaceInterface *iface)
             g_object_interface_install_property (iface, pspec);
             g_test_assert_expected_messages ();
 
-            g_param_spec_unref (pspec);
             continue;
           }
 
@@ -1398,6 +1397,13 @@ test_param_implement (void)
 {
   gchar *test_path;
 
+  /* This test is slow. */
+  if (!g_test_slow ())
+    {
+      g_test_skip ("Skipping slow /param/implement test");
+      return;
+    }
+
   for (change_this_flag = 0; change_this_flag < 16; change_this_flag++)
     for (change_this_type = 0; change_this_type < 3; change_this_type++)
       for (use_this_flag = 0; use_this_flag < 16; use_this_flag++)
@@ -1432,7 +1438,7 @@ test_param_implement (void)
 
               case 'i':
                 g_test_trap_assert_failed ();
-                g_test_trap_assert_stderr ("*g_object_class_install_property*");
+                g_test_trap_assert_stderr ("*pspec->flags*");
                 continue;
 
               case 'f':
@@ -1659,8 +1665,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/param/validate", test_param_validate);
   g_test_add_func ("/param/convert", test_param_convert);
 
-  if (g_test_slow ())
-    g_test_add_func ("/param/implement", test_param_implement);
+  g_test_add_func ("/param/implement", test_param_implement);
 
   for (data.change_this_flag = 0; data.change_this_flag < 16; data.change_this_flag++)
     for (data.change_this_type = 0; data.change_this_type < 3; data.change_this_type++)
@@ -1671,8 +1676,7 @@ main (int argc, char *argv[])
                                          data.change_this_flag, data.change_this_type,
                                          data.use_this_flag, data.use_this_type);
             test_data = g_memdup2 (&data, sizeof (TestParamImplementData));
-            g_test_add_data_func_full (test_path, test_data, test_param_implement_child, g_free);
-            g_free (test_data);
+            g_test_add_data_func_full (test_path, g_steal_pointer (&test_data), test_param_implement_child, g_free);
             g_free (test_path);
           }
 
index 1fbe7e7..0fe655e 100644 (file)
@@ -767,6 +767,59 @@ test_weak_ref_in_toggle_notify (void)
   g_assert_null (g_weak_ref_get (&weak));
 }
 
+static void
+test_weak_ref_many (void)
+{
+  const guint N = g_test_slow ()
+                      ? G_MAXUINT16
+                      : 211;
+  const guint PRIME = 1048583;
+  GObject *obj;
+  GWeakRef *weak_refs;
+  GWeakRef weak_ref1;
+  guint j;
+  guint n;
+  guint i;
+
+  obj = g_object_new (G_TYPE_OBJECT, NULL);
+
+  weak_refs = g_new (GWeakRef, N);
+
+  /* We register them in a somewhat juggled order. That's because below, we will clear them
+   * again, and we don't want to always clear them in the same order as they were registered.
+   * For that, we calculate the actual index by jumping around by adding a prime number. */
+  j = (g_test_rand_int () % (N + 1));
+  for (i = 0; i < N; i++)
+    {
+      j = (j + PRIME) % N;
+      g_weak_ref_init (&weak_refs[j], obj);
+    }
+
+  if (N == G_MAXUINT16)
+    {
+      g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL, "*Too many GWeakRef registered");
+      g_weak_ref_init (&weak_ref1, obj);
+      g_test_assert_expected_messages ();
+      g_assert_null (g_weak_ref_get (&weak_ref1));
+    }
+
+  n = g_test_rand_int () % (N + 1u);
+  for (i = 0; i < N; i++)
+    g_weak_ref_set (&weak_refs[i], i < n ? NULL : obj);
+
+  g_object_unref (obj);
+
+  for (i = 0; i < N; i++)
+    g_assert_null (g_weak_ref_get (&weak_refs[i]));
+
+  /* The API would expect us to also call g_weak_ref_clear() on all references
+   * to clean up.  In practice, they are already all NULL, so we don't need
+   * that (it would have no effect, with the current implementation of
+   * GWeakRef). */
+
+  g_free (weak_refs);
+}
+
 /*****************************************************************************/
 
 #define CONCURRENT_N_OBJS 5
@@ -1584,6 +1637,7 @@ main (int argc, char **argv)
   g_test_add_func ("/object/weak-ref/on-run-dispose", test_weak_ref_on_run_dispose);
   g_test_add_func ("/object/weak-ref/on-toggle-notify", test_weak_ref_on_toggle_notify);
   g_test_add_func ("/object/weak-ref/in-toggle-notify", test_weak_ref_in_toggle_notify);
+  g_test_add_func ("/object/weak-ref/many", test_weak_ref_many);
   g_test_add_data_func ("/object/weak-ref/concurrent/0", GINT_TO_POINTER (0), test_weak_ref_concurrent);
   g_test_add_data_func ("/object/weak-ref/concurrent/1", GINT_TO_POINTER (1), test_weak_ref_concurrent);
   g_test_add_func ("/object/toggle-ref", test_toggle_ref);
index 7b8aa9b..2eb994c 100644 (file)
@@ -106,6 +106,17 @@ weak_ref2 (gpointer data,
 }
 
 static void
+weak_ref3 (gpointer data,
+           GObject *object)
+{
+  GWeakRef *weak_ref = data;
+
+  g_assert_null (g_weak_ref_get (weak_ref));
+
+  weak_ref2_notified = TRUE;
+}
+
+static void
 toggle_ref1 (gpointer data,
              GObject *object,
              gboolean is_last_ref)
@@ -154,6 +165,7 @@ static void
 test_references (void)
 {
   GObject *object;
+  GWeakRef weak_ref;
 
   /* Test basic weak reference operation */
   global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
@@ -191,6 +203,17 @@ test_references (void)
   g_assert_true (weak_ref2_notified);
   g_assert_true (object_destroyed);
 
+  /* Test that within a GWeakNotify the GWeakRef is NULL already. */
+  weak_ref2_notified = FALSE;
+  object = g_object_new (G_TYPE_OBJECT, NULL);
+  g_weak_ref_init (&weak_ref, object);
+  g_assert_true (object == g_weak_ref_get (&weak_ref));
+  g_object_weak_ref (object, weak_ref3, &weak_ref);
+  g_object_unref (object);
+  g_object_unref (object);
+  g_assert_true (weak_ref2_notified);
+  g_weak_ref_clear (&weak_ref);
+
   /* Test basic toggle reference operation */
   global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
 
diff --git a/introspection/meson.build b/introspection/meson.build
deleted file mode 100644 (file)
index 3e99389..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-
-gi_identifier_prefix = 'G'
-gi_symbol_prefix = 'g'
-
-gi_gen_shared_sources = [
-  # Required to compile gdump
-  gmodule_visibility_h,
-]
-
-gi_gen_env_variables = environment()
-
-if get_option('b_sanitize') != ''
-  gi_gen_env_variables.append(
-    'ASAN_OPTIONS', 'verify_asan_link_order=0', separator: ',')
-endif
-
-# GLib
-glib_gir = gnome.generate_gir(libglib,
-  sources: [
-    gi_gen_shared_sources,
-    glibconfig_h,
-    gversionmacros_h,
-    glib_visibility_h,
-    glib_headers,
-    glib_deprecated_headers,
-    glib_sub_headers,
-    glib_enumtypes_h,
-    glib_types_h,
-    glib_deprecated_sources,
-    glib_sources,
-  ],
-  namespace: 'GLib',
-  nsversion: '2.0',
-  identifier_prefix: gi_identifier_prefix,
-  symbol_prefix: gi_symbol_prefix,
-  export_packages: 'glib-2.0',
-  header: 'glib.h',
-  install: true,
-  dependencies: [
-    libgobject_dep,
-  ],
-  env: gi_gen_env_variables,
-  extra_args: gir_args + [
-    '-DGLIB_COMPILATION',
-    '-DGETTEXT_PACKAGE="dummy"',
-    '--symbol-prefix=glib',
-    '--library-path=' + meson.current_build_dir(),
-    '--library=gobject-2.0',
-  ],
-)
-
-# GObject
-gobject_gir = gnome.generate_gir(libgobject,
-  sources: [
-    gi_gen_shared_sources,
-    gobject_visibility_h,
-    gobject_install_headers,
-    gobject_sources,
-  ],
-  namespace: 'GObject',
-  nsversion: '2.0',
-  identifier_prefix: gi_identifier_prefix,
-  symbol_prefix: gi_symbol_prefix,
-  export_packages: 'gobject-2.0',
-  header: 'glib-object.h',
-  includes: [ glib_gir[0] ],
-  install: true,
-  env: gi_gen_env_variables,
-  extra_args: gir_args + [
-      '-DGOBJECT_COMPILATION',
-      '--symbol-prefix=gobject',
-  ],
-)
-
-# GModule
-gmodule_gir = gnome.generate_gir(libgmodule,
-  sources: [
-    gi_gen_shared_sources,
-    gmoduleconf_h,
-    gmodule_h,
-    gmodule_c,
-    gmodule_deprecated_c,
-    gmodule_visibility_h,
-  ],
-  namespace: 'GModule',
-  nsversion: '2.0',
-  identifier_prefix: gi_identifier_prefix,
-  symbol_prefix: gi_symbol_prefix,
-  export_packages: 'gmodule-2.0',
-  header: 'gmodule.h',
-  includes: [ glib_gir[0] ],
-  install: true,
-  dependencies: [
-    libglib_dep,
-  ],
-  env: gi_gen_env_variables,
-  extra_args: gir_args + [
-      '-DGMODULE_COMPILATION',
-      '-DGETTEXT_PACKAGE="dummy"',
-      '--symbol-prefix=gmodule',
-  ],
-)
-
-# Gio
-gio_gir_sources = [
-  gi_gen_shared_sources,
-  gio_visibility_h,
-  gioenumtypes_h,
-  gnetworking_h,
-  gio_headers,
-  gio_base_sources,
-  application_sources,
-  gdbus_sources,
-  appinfo_sources,
-  contenttype_sources,
-  unix_sources,
-  win32_sources,
-  settings_sources,
-]
-gio_gir_packages = [ 'gio-2.0' ]
-gio_gir_args = [
-  '-DGIO_COMPILATION',
-  '-DG_SETTINGS_ENABLE_BACKEND',
-  '--symbol-prefix=gio',
-]
-if host_system == 'windows'
-  gio_gir_sources += gio_win32_include_headers
-  foreach h: gio_win32_include_headers
-    gio_gir_args += '--c-include=@0@'.format(h)
-  endforeach
-  gio_gir_packages += 'gio-win32-2.0'
-  gio_gir_args += '--pkg=gio-win32-2.0'
-else
-  gio_gir_sources += gio_unix_include_headers
-  foreach h: gio_unix_include_headers
-    gio_gir_args += '--c-include=@0@'.format(h)
-  endforeach
-  gio_gir_packages += 'gio-unix-2.0'
-  gio_gir_args += '--pkg=gio-unix-2.0'
-endif
-
-gio_gir = gnome.generate_gir(libgio,
-  sources: gio_gir_sources,
-  namespace: 'Gio',
-  nsversion: '2.0',
-  identifier_prefix: gi_identifier_prefix,
-  symbol_prefix: gi_symbol_prefix,
-  export_packages: gio_gir_packages,
-  header: 'gio/gio.h',
-  includes: [ glib_gir[0], gmodule_gir[0], gobject_gir[0] ],
-  install: true,
-  dependencies: [
-    libglib_dep,
-    libgobject_dep,
-    libgmodule_dep,
-  ],
-  env: gi_gen_env_variables,
-  extra_args: gir_args + gio_gir_args,
-)
index 9e647fc..cee1cab 100644 (file)
@@ -1,5 +1,5 @@
 project('glib', 'c',
-  version : '2.79.1',
+  version : '2.79.2',
   # NOTE: See the policy in docs/meson-version.md before changing the Meson dependency
   meson_version : '>= 1.2.0',
   default_options : [
@@ -171,6 +171,10 @@ common_test_env = [
   'MALLOC_CHECK_=2',
 ]
 
+if get_option('werror')
+  common_test_env += 'LINT_WARNINGS_ARE_ERRORS=1'
+endif
+
 # Note: this may cause the tests output not to be printed when running in
 # verbose mode, see https://github.com/mesonbuild/meson/issues/11185
 # Can be changed it to 'exitcode' if required during development.
@@ -193,6 +197,13 @@ add_test_setup('unstable_tests',
   #suites: ['flaky', 'unstable']
 )
 
+add_test_setup('thorough',
+  exclude_suites: ['flaky', 'failing', 'performance'],
+  env: common_test_env,
+  timeout_multiplier: 20,
+  exe_wrapper: [find_program('./.gitlab-ci/thorough-test-wrapper.sh', required: true)],
+)
+
 # Allow the tests to be easily run under valgrind using --setup=valgrind
 valgrind = find_program('valgrind', required: false)
 valgrind_suppression_file = files('tools' / 'glib.supp')[0]
@@ -500,6 +511,7 @@ struct_members = [
                            #include <dirent.h>''' ],
   [ 'statvfs', 'f_basetype', '#include <sys/statvfs.h>' ],
   [ 'statvfs', 'f_fstypename', '#include <sys/statvfs.h>' ],
+  [ 'statvfs', 'f_type', '#include <sys/statvfs.h>' ],
   [ 'tm', 'tm_gmtoff', '#include <time.h>' ],
   [ 'tm', '__tm_gmtoff', '#include <time.h>' ],
 ]
@@ -511,7 +523,16 @@ foreach m : struct_members
   else
     header_check_prefix = header_check_prefix + '#include <sys/stat.h>'
   endif
-  if cc.has_member('struct ' + m[0], m[1], prefix : header_check_prefix)
+  # Reimplement cc.has_member() to workaround compiler warning
+  # FIXME: https://github.com/mesonbuild/meson/pull/12818
+  code = header_check_prefix + '''
+    void bar(void) {
+        struct ''' + m[0] + ''' foo;
+        (void) ( foo.''' + m[1] + ''' );
+        (void) foo;
+    }
+    '''
+  if cc.compiles(code, name : 'type "struct ' + m[0] + '" has member "' + m[1] + '"')
     define = 'HAVE_STRUCT_@0@_@1@'.format(m[0].to_upper(), m[1].underscorify().to_upper())
     glib_conf.set(define, 1)
     glib_conf_prefix = glib_conf_prefix + '#define @0@ 1\n'.format(define)
@@ -1006,6 +1027,7 @@ static __uint128_t v1 = 100;
 static __uint128_t v2 = 10;
 static __uint128_t u;
 u = v1 / v2;
+(void) u;
 }'''
 if cc.compiles(uint128_t_src, name : '__uint128_t available')
   glib_conf.set('HAVE_UINT128_T', 1)
@@ -1101,7 +1123,7 @@ if cc.compiles('''#include <fcntl.h>
                   #include <sys/types.h>
                   #include <sys/stat.h>
                   void some_func (void) {
-                    open(0, O_DIRECTORY, 0);
+                    open(".", O_DIRECTORY, 0);
                   }''', name : 'open() option O_DIRECTORY')
   glib_conf.set('HAVE_OPEN_O_DIRECTORY', 1)
 endif
@@ -1206,7 +1228,6 @@ int
 doit()
 {
   char buffer[32];
-  va_list args;
   int r;
 
   r = snprintf(buffer, 5, "1234567");
@@ -1293,6 +1314,7 @@ endif
 if cc.links('''#include <langinfo.h>
                int main (int argc, char ** argv) {
                  char *codeset = nl_langinfo (CODESET);
+                 (void) codeset;
                  return 0;
                }''', name : 'nl_langinfo and CODESET')
   glib_conf.set('HAVE_LANGINFO_CODESET', 1)
@@ -1313,6 +1335,7 @@ if cc.links('''#include <langinfo.h>
                  str = nl_langinfo (ABMON_12);
                  str = nl_langinfo (DAY_1);
                  str = nl_langinfo (ABDAY_7);
+                 (void) str;
                  return 0;
                }''', name : 'nl_langinfo (PM_STR)')
   have_langinfo_time = true
@@ -1328,6 +1351,7 @@ if cc.links('''#include <langinfo.h>
                  str = nl_langinfo (ERA_D_FMT);
                  str = nl_langinfo (ERA_T_FMT);
                  str = nl_langinfo (_NL_TIME_ERA_NUM_ENTRIES);
+                 (void) str;
                  return 0;
                }''', name : 'nl_langinfo (ERA)')
   glib_conf.set('HAVE_LANGINFO_ERA', 1)
@@ -1349,6 +1373,7 @@ if cc.links('''#include <langinfo.h>
                  str = nl_langinfo (_NL_CTYPE_OUTDIGIT7_MB);
                  str = nl_langinfo (_NL_CTYPE_OUTDIGIT8_MB);
                  str = nl_langinfo (_NL_CTYPE_OUTDIGIT9_MB);
+                 (void) str;
                  return 0;
                }''', name : 'nl_langinfo (_NL_CTYPE_OUTDIGITn_MB)')
   glib_conf.set('HAVE_LANGINFO_OUTDIGIT', 1)
@@ -1376,6 +1401,7 @@ if cc.links('''#ifndef _GNU_SOURCE
                  str = nl_langinfo (ALTMON_10);
                  str = nl_langinfo (ALTMON_11);
                  str = nl_langinfo (ALTMON_12);
+                 (void) str;
                  return 0;
                }''', name : 'nl_langinfo (ALTMON_n)')
   glib_conf.set('HAVE_LANGINFO_ALTMON', 1)
@@ -1403,6 +1429,7 @@ if cc.links('''#ifndef _GNU_SOURCE
                  str = nl_langinfo (_NL_ABALTMON_10);
                  str = nl_langinfo (_NL_ABALTMON_11);
                  str = nl_langinfo (_NL_ABALTMON_12);
+                 (void) str;
                  return 0;
                }''', name : 'nl_langinfo (_NL_ABALTMON_n)')
   glib_conf.set('HAVE_LANGINFO_ABALTMON', 1)
@@ -1415,6 +1442,7 @@ endif
 if cc.links('''#include <langinfo.h>
                int main (int argc, char ** argv) {
                  char *codeset = nl_langinfo (_NL_TIME_CODESET);
+                 (void) codeset;
                  return 0;
                }''', name : 'nl_langinfo and _NL_TIME_CODESET')
   glib_conf.set('HAVE_LANGINFO_TIME_CODESET', 1)
@@ -1485,6 +1513,7 @@ if cc.compiles('''#include <sys/types.h>
                   #include <inttypes.h>
                   void some_func (void) {
                     uintmax_t i = (uintmax_t) -1;
+                    (void) i;
                   }''', name : 'uintmax_t in inttypes.h')
   glib_conf.set('HAVE_INTTYPES_H_WITH_UINTMAX', 1)
   found_uintmax_t = true
@@ -1497,6 +1526,7 @@ if cc.compiles('''#include <sys/types.h>
                   #include <stdint.h>
                   void some_func (void) {
                     uintmax_t i = (uintmax_t) -1;
+                    (void) i;
                   }''', name : 'uintmax_t in stdint.h')
   glib_conf.set('HAVE_STDINT_H_WITH_UINTMAX', 1)
   found_uintmax_t = true
@@ -1546,6 +1576,7 @@ if long_long_size == long_size
                     int main () {
                       int64_t i1 = 1;
                       long *i2 = &i1;
+                      (void) i2;
                       return 1;
                     }''', name : 'int64_t is long')
     int64_t_typedef = 'long'
@@ -1558,9 +1589,12 @@ if long_long_size == long_size
                       int main () {
                         int64_t i1 = 1;
                         long long *i2 = &i1;
+                        (void) i2;
                         return 1;
                       }''', name : 'int64_t is long long')
     int64_t_typedef = 'long long'
+  else
+    error('Cannot detect int64_t typedef')
   endif
 endif
 
@@ -2245,6 +2279,10 @@ libmount_dep = []
 if host_system == 'linux'
   libmount_dep = dependency('mount', version : '>=2.23', required : get_option('libmount'))
   glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found())
+
+  if libmount_dep.found() and cc.has_function('mnt_monitor_veil_kernel', dependencies: libmount_dep)
+    glib_conf.set('HAVE_MNT_MONITOR_VEIL_KERNEL', 1)
+  endif
 endif
 
 if host_system == 'windows'
@@ -2532,11 +2570,9 @@ subdir('gobject')
 subdir('gthread')
 subdir('gmodule')
 subdir('gio')
-if enable_gir
-  subdir('introspection')
-endif
 subdir('girepository')
 subdir('fuzzing')
+subdir('tests')
 
 # xgettext is optional (on Windows for instance)
 if find_program('xgettext', required : get_option('nls')).found()
index a77fac7..a803951 100644 (file)
--- a/po/ka.po
+++ b/po/ka.po
@@ -6,9 +6,9 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: glib\n"
-"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/glib/issues\n"
-"POT-Creation-Date: 2023-12-20 00:43+0000\n"
-"PO-Revision-Date: 2024-01-08 05:28+0100\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/glib/issues/new\n"
+"POT-Creation-Date: 2024-02-09 10:04+0000\n"
+"PO-Revision-Date: 2024-02-12 14:03+0100\n"
 "Last-Translator: Ekaterine Papava <papava.e@gtu.ge>\n"
 "Language-Team: Georgian <(nothing)>\n"
 "Language: ka\n"
@@ -39,34 +39,38 @@ msgstr ""
 msgid "Failed to find default application for URI Scheme ‘%s’"
 msgstr "ჩავარდა ნაგულისხმევი აპლიკაციის პოვნის მცდელობა URI-ის სქემისთვის '%s'"
 
-#: gio/gapplication.c:503
+#: gio/gapplication.c:507
 msgid "GApplication Options:"
 msgstr "GApplication-ის მორგება:"
 
-#: gio/gapplication.c:503
+#: gio/gapplication.c:507
 msgid "Show GApplication options"
 msgstr "GApplication პარამეტრების ჩვენება"
 
-#: gio/gapplication.c:548
+#: gio/gapplication.c:552
 msgid "Enter GApplication service mode (use from D-Bus service files)"
 msgstr ""
 "Gapplication-ის სერვისის რეჟიმში შესვლა (გამოიყენება D-Bus-ის სერვისის "
 "ფაილებიდან)"
 
-#: gio/gapplication.c:560
+#: gio/gapplication.c:564
 msgid "Override the application’s ID"
 msgstr "აპლიკაციის ID-ის გადაფარვა"
 
-#: gio/gapplication.c:572
+#: gio/gapplication.c:575
+msgid "Print the application version"
+msgstr "აპლიკაციის ვერსიის გამოტანა"
+
+#: gio/gapplication.c:587
 msgid "Replace the running instance"
 msgstr "გაშვებული ინტერფეისის შეცვლა"
 
 #: gio/gapplication-tool.c:47 gio/gapplication-tool.c:48 gio/gio-tool.c:230
-#: gio/gresource-tool.c:496 gio/gsettings-tool.c:586
+#: gio/gresource-tool.c:497 gio/gsettings-tool.c:586
 msgid "Print help"
 msgstr "დახმარების ჩვენება"
 
-#: gio/gapplication-tool.c:49 gio/gresource-tool.c:497 gio/gresource-tool.c:565
+#: gio/gapplication-tool.c:49 gio/gresource-tool.c:498 gio/gresource-tool.c:566
 msgid "[COMMAND]"
 msgstr "[ბრძანება]"
 
@@ -137,7 +141,7 @@ msgstr "აპლიკაციის იდენტიფიკატორ
 
 #: gio/gapplication-tool.c:76 gio/glib-compile-resources.c:822
 #: gio/glib-compile-resources.c:828 gio/glib-compile-resources.c:858
-#: gio/gresource-tool.c:503 gio/gresource-tool.c:569
+#: gio/gresource-tool.c:504 gio/gresource-tool.c:570
 msgid "FILE"
 msgstr "PROFILEID"
 
@@ -163,7 +167,7 @@ msgstr "პარამეტრი"
 msgid "Optional parameter to the action invocation, in GVariant format"
 msgstr "ქმედების ჩაწოდების არასავალდებული პარამეტრი ფორმატში GVariant"
 
-#: gio/gapplication-tool.c:100 gio/gresource-tool.c:534
+#: gio/gapplication-tool.c:100 gio/gresource-tool.c:535
 #: gio/gsettings-tool.c:678
 #, c-format
 msgid ""
@@ -177,7 +181,7 @@ msgstr ""
 msgid "Usage:\n"
 msgstr "გამოყენება:\n"
 
-#: gio/gapplication-tool.c:118 gio/gresource-tool.c:559
+#: gio/gapplication-tool.c:118 gio/gresource-tool.c:560
 #: gio/gsettings-tool.c:713
 msgid "Arguments:\n"
 msgstr "არგუმენტები:\n"
@@ -300,7 +304,7 @@ msgstr "ნაკადი უკვე დახურულია"
 msgid "Truncate not supported on base stream"
 msgstr "საბაზისო ნაკადის წაკვეთა შეუძლებელია"
 
-#: gio/gcancellable.c:326 gio/gdbusconnection.c:1844 gio/gdbusprivate.c:1432
+#: gio/gcancellable.c:326 gio/gdbusconnection.c:1844 gio/gdbusprivate.c:1434
 #: gio/gsimpleasyncresult.c:871 gio/gsimpleasyncresult.c:897
 #, c-format
 msgid "Operation was cancelled"
@@ -319,13 +323,13 @@ msgid "Not enough space in destination"
 msgstr "დანიშნულების წერტილში საკმარისი ადგილი არაა"
 
 #: gio/gcharsetconverter.c:354 gio/gdatainputstream.c:842
-#: gio/gdatainputstream.c:1260 glib/gconvert.c:351 glib/gconvert.c:783
+#: gio/gdatainputstream.c:1260 glib/gconvert.c:360 glib/gconvert.c:792
 #: glib/giochannel.c:1565 glib/giochannel.c:1607 glib/giochannel.c:2467
 #: glib/gutf8.c:958 glib/gutf8.c:1412
 msgid "Invalid byte sequence in conversion input"
 msgstr "შეტანილ ტექსტში ბაიტების მიმდევრობა მცდარია"
 
-#: gio/gcharsetconverter.c:359 glib/gconvert.c:359 glib/gconvert.c:697
+#: gio/gcharsetconverter.c:359 glib/gconvert.c:368 glib/gconvert.c:706
 #: glib/giochannel.c:1572 glib/giochannel.c:2482
 #, c-format
 msgid "Error during conversion: %s"
@@ -335,12 +339,12 @@ msgstr "გარდაქმნის შეცდომა: %s"
 msgid "Cancellable initialization not supported"
 msgstr "გაუქმებადი ინიციალიზაცია მხარდაჭერილი არაა"
 
-#: gio/gcharsetconverter.c:468 glib/gconvert.c:224 glib/giochannel.c:1393
+#: gio/gcharsetconverter.c:468 glib/gconvert.c:233 glib/giochannel.c:1393
 #, c-format
 msgid "Conversion from character set “%s” to “%s” is not supported"
 msgstr "კოდური გვერდის \"%s\" გარდაქმნა \"%s\" კოდირებაში მხარდაუჭერელია"
 
-#: gio/gcharsetconverter.c:472 glib/gconvert.c:228
+#: gio/gcharsetconverter.c:472 glib/gconvert.c:237
 #, c-format
 msgid "Could not open converter from “%s” to “%s”"
 msgstr "ვერ ხერხდება \"%s\" - \"%s\" გარდამქმნელის გახსნა"
@@ -770,63 +774,74 @@ msgstr "%s-სთვის ქვეხე უკვე გატანილი
 msgid "Object does not exist at path “%s”"
 msgstr "ობიექტი არ არსებობს ბილიკზე \"%s\""
 
-#: gio/gdbusmessage.c:1303
-msgid "type is INVALID"
-msgstr "ტიპი არასწორია"
-
-#: gio/gdbusmessage.c:1321
-msgid "METHOD_CALL message: PATH or MEMBER header field is missing or invalid"
+#: gio/gdbusmessage.c:1351
+#, c-format
+msgid "%s message: %s header field is invalid; expected a value of type ‘%s’"
 msgstr ""
-"METHOD_CALL შეტყობინება: PATH ან MEMBER თავსართის ველი აკლია ან არასწორია"
 
-#: gio/gdbusmessage.c:1337
-msgid "METHOD_RETURN message: REPLY_SERIAL header field is missing or invalid"
-msgstr ""
-"METHOD_RETURN შეტყობინება: REPLY_SERIAL თავსართის ველი აკლია ან არასწორია"
+#: gio/gdbusmessage.c:1374
+#, c-format
+msgid "%s message: %s header field is missing or invalid"
+msgstr "%s შეტყობინება: %s თავსართის ველი აკლია ან არასწორია"
 
-#: gio/gdbusmessage.c:1357
-msgid ""
-"ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing or invalid"
+#: gio/gdbusmessage.c:1413
+#, c-format
+msgid "%s message: INVALID header field supplied"
 msgstr ""
-"ERROR შეტყობინება: REPLY_SERIAL ან ERROR_NAME თავსართის ველი აკლია ან "
-"არასწორია"
 
-#: gio/gdbusmessage.c:1381
+#: gio/gdbusmessage.c:1424
+#, c-format
 msgid ""
-"SIGNAL message: PATH, INTERFACE or MEMBER header field is missing or invalid"
+"%s message: PATH header field is using the reserved value /org/freedesktop/"
+"DBus/Local"
 msgstr ""
-"SIGNAL შეტყობინება: PATH, INTERFACE ან MEMBER თავსართის ველი აკლია ან "
-"არასწორია"
+"%s შეტყობინება: PATH თავსართის ველი იყენებს დაცულ მნიშვნელობას /org/"
+"freedesktop/DBus/Local"
 
-#: gio/gdbusmessage.c:1389
+#: gio/gdbusmessage.c:1437
+#, c-format
 msgid ""
-"SIGNAL message: The PATH header field is using the reserved value /org/"
-"freedesktop/DBus/Local"
+"%s message: INTERFACE header field does not contain a valid interface name"
 msgstr ""
-"SIGNAL შეტყობინება: PATH თავსართის ველი იყენებს დაცულ მნიშვნელობას /org/"
-"freedesktop/DBus/Local"
+"%s შეტყობინება: INTERFACE თავსართის ველი სწორ ინტერფეისის სახელს არ შეიცავს"
 
-#: gio/gdbusmessage.c:1397
+#: gio/gdbusmessage.c:1446
+#, c-format
 msgid ""
-"SIGNAL message: The INTERFACE header field is using the reserved value org."
+"%s message: INTERFACE header field is using the reserved value org."
 "freedesktop.DBus.Local"
 msgstr ""
-"SIGNAL შეტყობინება: INTERFACE თავსართის ველი იყენებს დაცულ მნიშვნელობას org."
+"%s შეტყობინება: INTERFACE თავსართის ველი იყენებს დაცულ მნიშვნელობას org."
 "freedesktop.DBus.Local"
 
-#: gio/gdbusmessage.c:1446 gio/gdbusmessage.c:1506
+#: gio/gdbusmessage.c:1459
+#, c-format
+msgid "%s message: MEMBER header field does not contain a valid member name"
+msgstr "%s შეტყობინება: MEMBER თავსართის ველი სწორი წევრის სახელს არ შეიცავს"
+
+#: gio/gdbusmessage.c:1472
+#, c-format
+msgid "%s message: ERROR_NAME header field does not contain a valid error name"
+msgstr ""
+"%s შეტყობინება: ERROR_NAME თავსართის ველი სწორ შეცდომის სახელს არ შეიცავს"
+
+#: gio/gdbusmessage.c:1511
+msgid "type is INVALID"
+msgstr "ტიპი არასწორია"
+
+#: gio/gdbusmessage.c:1581 gio/gdbusmessage.c:1641
 #, c-format
 msgid "Wanted to read %lu byte but only got %lu"
 msgid_plural "Wanted to read %lu bytes but only got %lu"
 msgstr[0] "მინდოდა წამეკითხა %lu ბაიტი, მაგრამ მივიღე მხოლოდ %lu"
 
-#: gio/gdbusmessage.c:1460
+#: gio/gdbusmessage.c:1595
 #, c-format
 msgid "Expected NUL byte after the string “%s” but found byte %d"
 msgstr ""
 "სტრიქონის \"%s\" შემდეგ მოველოდი ბაიტს NUL, მაგრამ აღმოჩენილია ბაიტი %d"
 
-#: gio/gdbusmessage.c:1479
+#: gio/gdbusmessage.c:1614
 #, c-format
 msgid ""
 "Expected valid UTF-8 string but found invalid bytes at byte offset %d "
@@ -835,23 +850,23 @@ msgstr ""
 "მოველოდი სწორ UTF-8 სტრიქონს, მაგრამ ბაიტის წანაცვლებაზე %d (სტრიქონის "
 "სიგრძეა %d) აღმოჩენილია არასწორი ბაიტები. სწორი UTF-8 ამ დრომდე იყო \"%s\""
 
-#: gio/gdbusmessage.c:1543 gio/gdbusmessage.c:1819 gio/gdbusmessage.c:2030
+#: gio/gdbusmessage.c:1678 gio/gdbusmessage.c:1954 gio/gdbusmessage.c:2165
 msgid "Value nested too deeply"
 msgstr "მნიშვნელობა მეტისმეტად ღრმადაა ჩადგმული"
 
-#: gio/gdbusmessage.c:1711
+#: gio/gdbusmessage.c:1846
 #, c-format
 msgid "Parsed value “%s” is not a valid D-Bus object path"
 msgstr ""
 "დამუშავებული მნიშვნელობა \"%s\" სწორ D-Bus-ის ობიექტის ბილიკს არ წარმოადგენს"
 
-#: gio/gdbusmessage.c:1735
+#: gio/gdbusmessage.c:1870
 #, c-format
 msgid "Parsed value “%s” is not a valid D-Bus signature"
 msgstr ""
 "დამუშავებული მნიშვნელობა \"%s\" სწორ D-Bus-ის ხელმოწერას არ წარმოადგენს"
 
-#: gio/gdbusmessage.c:1786
+#: gio/gdbusmessage.c:1921
 #, c-format
 msgid ""
 "Encountered array of length %u byte. Maximum length is 2<<26 bytes (64 MiB)."
@@ -860,7 +875,7 @@ msgid_plural ""
 msgstr[0] ""
 "შემხვდა მასივი სიგრძით %u ბაიტი. მაქსიმალური სიგრძეა 2<<26 ბაიტი (64 მიბ)."
 
-#: gio/gdbusmessage.c:1806
+#: gio/gdbusmessage.c:1941
 #, c-format
 msgid ""
 "Encountered array of type “a%c”, expected to have a length a multiple of %u "
@@ -869,18 +884,18 @@ msgstr ""
 "შემხვდა მასივი ტიპით \"a%c\". მოველოდი, რომ %u ბაიტის ნამრავლის სიგრძე "
 "ექნებოდა, მაგრამ ის %u ბაიტის სიგრძისაა"
 
-#: gio/gdbusmessage.c:1960 gio/gdbusmessage.c:2687
+#: gio/gdbusmessage.c:2095 gio/gdbusmessage.c:2822
 msgid "Empty structures (tuples) are not allowed in D-Bus"
 msgstr "D-Bus-ში ცარიელი სტრუქტურები (კორტეჟები) მხარდაჭერილი არაა"
 
-#: gio/gdbusmessage.c:2014
+#: gio/gdbusmessage.c:2149
 #, c-format
 msgid "Parsed value “%s” for variant is not a valid D-Bus signature"
 msgstr ""
 "ვარიანტისთვის დამუშავებული მნიშვნელობა \"%s\" სწორ D-Bus-ის ხელმოწერას არ "
 "წარმოადგენს"
 
-#: gio/gdbusmessage.c:2055
+#: gio/gdbusmessage.c:2190
 #, c-format
 msgid ""
 "Error deserializing GVariant with type string “%s” from the D-Bus wire format"
@@ -888,7 +903,7 @@ msgstr ""
 "D-Bus-ის მავთულის ფორმატიდან GVariant-ის სტრიქონის ტიპით \"%s\" "
 "დესერიალიზაციის შეცდომა"
 
-#: gio/gdbusmessage.c:2240
+#: gio/gdbusmessage.c:2375
 #, c-format
 msgid ""
 "Invalid endianness value. Expected 0x6c (“l”) or 0x42 (“B”) but found value "
@@ -897,30 +912,30 @@ msgstr ""
 "არასწორი ბოლოიანობის მნიშვნელობა. მოველოდი 0x6c (“l”) ან 0x42 (“B”) მაგრამ "
 "მიღებულია მნიშვნელობა: 0x%02x"
 
-#: gio/gdbusmessage.c:2259
+#: gio/gdbusmessage.c:2394
 #, c-format
 msgid "Invalid major protocol version. Expected 1 but found %d"
 msgstr "არასწორი პროტოკოლის ძირითადი ვერსია. მოველოდი 1, აღმოჩენილია %d"
 
-#: gio/gdbusmessage.c:2317 gio/gdbusmessage.c:2923
+#: gio/gdbusmessage.c:2452 gio/gdbusmessage.c:3058
 msgid "Signature header found but is not of type signature"
 msgstr "აღმოჩენილია ხელმოწერის თავსართი, მაგრამ ის ხელმოწერის ტიპი არაა"
 
-#: gio/gdbusmessage.c:2329
+#: gio/gdbusmessage.c:2464
 #, c-format
 msgid "Signature header with signature “%s” found but message body is empty"
 msgstr ""
 "აღმოჩენილია ხელმოწერის თავსართი ხელმოწერით \"%s\", მაგრამ შეტყობინების "
 "სხეული ცარიელია"
 
-#: gio/gdbusmessage.c:2344
+#: gio/gdbusmessage.c:2479
 #, c-format
 msgid "Parsed value “%s” is not a valid D-Bus signature (for body)"
 msgstr ""
 "დამუშავებული მნიშვნელობა \"%s\" სწორ D-Bus-ის ხელმოწერას არ წარმოადგენს "
 "(სხეულისთვის)"
 
-#: gio/gdbusmessage.c:2384
+#: gio/gdbusmessage.c:2519
 #, c-format
 msgid "No signature header in message but the message body is %u byte"
 msgid_plural "No signature header in message but the message body is %u bytes"
@@ -928,11 +943,11 @@ msgstr[0] ""
 "შეტყობინებაში ხელმოწერის თავსართი აღმოჩენილი არაა, მაგრამ შეტყობინების "
 "სხეული %u ბაიტია"
 
-#: gio/gdbusmessage.c:2394
+#: gio/gdbusmessage.c:2529
 msgid "Cannot deserialize message: "
 msgstr "დესერიალიზაცია შეუძლებელია შეტყობინებისთვის: "
 
-#: gio/gdbusmessage.c:2740
+#: gio/gdbusmessage.c:2875
 #, c-format
 msgid ""
 "Error serializing GVariant with type string “%s” to the D-Bus wire format"
@@ -940,7 +955,7 @@ msgstr ""
 "D-Bus-ის მავთულის ფორმატიდან GVariant-ის სტრიქონის ტიპით \"%s\" "
 "სერიალიზაციის შეცდომა"
 
-#: gio/gdbusmessage.c:2877
+#: gio/gdbusmessage.c:3012
 #, c-format
 msgid ""
 "Number of file descriptors in message (%d) differs from header field (%d)"
@@ -948,18 +963,18 @@ msgstr ""
 "დესკრიპტორების რაოდენობა შეტყობინებაში (%d) განსხვავდება თავსართის ველისგან "
 "(%d)"
 
-#: gio/gdbusmessage.c:2885
+#: gio/gdbusmessage.c:3020
 msgid "Cannot serialize message: "
 msgstr "სერიალიზაცია შეუძლებელია შეტყობინებისთვის: "
 
-#: gio/gdbusmessage.c:2938
+#: gio/gdbusmessage.c:3073
 #, c-format
 msgid "Message body has signature “%s” but there is no signature header"
 msgstr ""
 "შეტყობინების სხეულს გააჩნია ხელმოწერა \"%s\", მაგრამ ხელმოწერის თავსართი "
 "აღმოჩენილი არაა"
 
-#: gio/gdbusmessage.c:2948
+#: gio/gdbusmessage.c:3083
 #, c-format
 msgid ""
 "Message body has type signature “%s” but signature in the header field is "
@@ -968,39 +983,39 @@ msgstr ""
 "შეტყობინების სხეულს გააჩნია ხელმოწერა \"%s\", მაგრამ ხელმოწერა, რომელიც "
 "თავსართის ველშია, \"%s\"-ია"
 
-#: gio/gdbusmessage.c:2964
+#: gio/gdbusmessage.c:3099
 #, c-format
 msgid "Message body is empty but signature in the header field is “(%s)”"
 msgstr ""
 "შეტყობინების სხეული ცარელია, მაგრამ ხელმოწერა თავსართის ველში \"(%s)\"-ის "
 "ტოლია"
 
-#: gio/gdbusmessage.c:3538
+#: gio/gdbusmessage.c:3673
 #, c-format
 msgid "Error return with body of type “%s”"
 msgstr "\"%s\" ტიპის სხეულით დაბრუნების შეცდომა"
 
-#: gio/gdbusmessage.c:3546
+#: gio/gdbusmessage.c:3681
 msgid "Error return with empty body"
 msgstr "შეცდომა ცარიელი სხეულით დაბრუნდა"
 
-#: gio/gdbusprivate.c:2199
+#: gio/gdbusprivate.c:2201
 #, c-format
 msgid "(Type any character to close this window)\n"
 msgstr "(ფანჯრის დასახურად დააჭირეთ ნებისმიერ კლავიშას)\n"
 
-#: gio/gdbusprivate.c:2385
+#: gio/gdbusprivate.c:2387
 #, c-format
 msgid "Session dbus not running, and autolaunch failed"
 msgstr "სესიის dbus გაშვებული არაა და ავტომატური გაშვება ჩავარდა"
 
-#: gio/gdbusprivate.c:2408
+#: gio/gdbusprivate.c:2410
 #, c-format
 msgid "Unable to get Hardware profile: %s"
 msgstr "აპარატურის პროფილის მიღების შეცდომა: %s"
 
 #. Translators: Both placeholders are file paths
-#: gio/gdbusprivate.c:2464
+#: gio/gdbusprivate.c:2466
 #, c-format
 msgid "Unable to load %s or %s: "
 msgstr "%s-ის ან %s-ის ჩატვირთვის შეცდომა: "
@@ -1447,7 +1462,7 @@ msgstr "GEmblemedIcon-სთვის GEmblem-ს ველოდებოდი
 msgid "Containing mount does not exist"
 msgstr "შეცველი მიმაგრება არ არსებობს"
 
-#: gio/gfile.c:2650 gio/glocalfile.c:2518
+#: gio/gfile.c:2650 gio/glocalfile.c:2520
 msgid "Can’t copy over directory"
 msgstr "საქაღალდეზე ზემოდან კოპირება შეუძლებელია"
 
@@ -1565,7 +1580,7 @@ msgid "Truncate not supported on stream"
 msgstr "ნაკადის მოკვეთა მხარდაუჭერელია"
 
 #: gio/ghttpproxy.c:93 gio/gresolver.c:529 gio/gresolver.c:682
-#: glib/gconvert.c:1743
+#: glib/gconvert.c:1752
 msgid "Invalid hostname"
 msgstr "ჰოსტის არასწორი სახელი"
 
@@ -1598,37 +1613,37 @@ msgstr "HTTP პროქსის პასუხი მეტისმეტ
 msgid "HTTP proxy server closed connection unexpectedly."
 msgstr "HTTP პროქსისთან მიერთება მოულოდნელად დაიხურა."
 
-#: gio/gicon.c:297
+#: gio/gicon.c:298
 #, c-format
 msgid "Wrong number of tokens (%d)"
 msgstr "კოდების არასწორი რაოდენობა (%d)"
 
-#: gio/gicon.c:317
+#: gio/gicon.c:318
 #, c-format
 msgid "No type for class name %s"
 msgstr "ტიპი კლასის სახელისთვის %s მითითებული არაა"
 
-#: gio/gicon.c:327
+#: gio/gicon.c:328
 #, c-format
 msgid "Type %s does not implement the GIcon interface"
 msgstr "ტიპი %s GIcon ინტერფეისის განხორციელებას არ ახდენს"
 
-#: gio/gicon.c:338
+#: gio/gicon.c:339
 #, c-format
 msgid "Type %s is not classed"
 msgstr "ტიპი %s klasSi araa"
 
-#: gio/gicon.c:352
+#: gio/gicon.c:353
 #, c-format
 msgid "Malformed version number: %s"
 msgstr "არასწორი ვერსიის ნომერი: %s"
 
-#: gio/gicon.c:366
+#: gio/gicon.c:367
 #, c-format
 msgid "Type %s does not implement from_tokens() on the GIcon interface"
 msgstr "ტიპი %s GIcon ინტერფეისზე from_tokens()-ის განხორციელებას არ ახდენს"
 
-#: gio/gicon.c:468
+#: gio/gicon.c:469
 msgid "Can’t handle the supplied version of the icon encoding"
 msgstr "ხატულის კოდირების მითითებული ვერსიის დამუშავება შეუძლებელია"
 
@@ -2991,126 +3006,126 @@ msgstr "'%s'-სთვის ფაილური სისტემის ი
 #. * the enclosing (user visible) mount of a file, but none
 #. * exists.
 #.
-#: gio/glocalfile.c:1148
+#: gio/glocalfile.c:1150
 #, c-format
 msgid "Containing mount for file %s not found"
 msgstr "შემცველი მიმაგრების წერტილი ფაილისთვის '%s' აღმოჩენილი არაა"
 
-#: gio/glocalfile.c:1171
+#: gio/glocalfile.c:1173
 msgid "Can’t rename root directory"
 msgstr "საქაღალდის '/' სახელის გადარქმევა შეუძლებელია"
 
-#: gio/glocalfile.c:1189 gio/glocalfile.c:1212
+#: gio/glocalfile.c:1191 gio/glocalfile.c:1214
 #, c-format
 msgid "Error renaming file %s: %s"
 msgstr "შეცდომა სახელის გადარქმევისას ფაილისთვის %s: %s"
 
-#: gio/glocalfile.c:1196
+#: gio/glocalfile.c:1198
 msgid "Can’t rename file, filename already exists"
 msgstr ""
 "ფაილის სახელის გადარქმევა შეუძლებელია, რადგან ფაილის სახელი უკვე არსებობს"
 
-#: gio/glocalfile.c:1209 gio/glocalfile.c:2412 gio/glocalfile.c:2440
-#: gio/glocalfile.c:2579 gio/glocalfileoutputstream.c:658
+#: gio/glocalfile.c:1211 gio/glocalfile.c:2414 gio/glocalfile.c:2442
+#: gio/glocalfile.c:2581 gio/glocalfileoutputstream.c:658
 msgid "Invalid filename"
 msgstr "ფაილის არასწორი სახელი"
 
-#: gio/glocalfile.c:1377 gio/glocalfile.c:1388
+#: gio/glocalfile.c:1379 gio/glocalfile.c:1390
 #, c-format
 msgid "Error opening file %s: %s"
 msgstr "ფაილის (%s) გახსნის შეცდომა: %s"
 
-#: gio/glocalfile.c:1513
+#: gio/glocalfile.c:1515
 #, c-format
 msgid "Error removing file %s: %s"
 msgstr "ფაილის (%s) წაშლის შეცდომა: %s"
 
-#: gio/glocalfile.c:2007 gio/glocalfile.c:2018 gio/glocalfile.c:2045
+#: gio/glocalfile.c:2009 gio/glocalfile.c:2020 gio/glocalfile.c:2047
 #, c-format
 msgid "Error trashing file %s: %s"
 msgstr "ფაილის (%s) ნაგვის ყუთში გადატანის შეცდომა: %s"
 
-#: gio/glocalfile.c:2065
+#: gio/glocalfile.c:2067
 #, c-format
 msgid "Unable to create trash directory %s: %s"
 msgstr "სანაგვის საქაღალდის (%s) შექმნის შეცდომა: %s"
 
-#: gio/glocalfile.c:2086
+#: gio/glocalfile.c:2088
 #, c-format
 msgid "Unable to find toplevel directory to trash %s"
 msgstr "%s-ის წასაშლელად ზედა საქაღალდის პოვნა შეუძლებელი იყო"
 
-#: gio/glocalfile.c:2094
+#: gio/glocalfile.c:2096
 #, c-format
 msgid "Trashing on system internal mounts is not supported"
 msgstr ""
 "სისტემურ შიდა მიმაგრების წერტილებზე ნაგვის ყუთის შექმნა მხარდაჭერილი არაა"
 
-#: gio/glocalfile.c:2180 gio/glocalfile.c:2208
+#: gio/glocalfile.c:2182 gio/glocalfile.c:2210
 #, c-format
 msgid "Unable to find or create trash directory %s to trash %s"
 msgstr "სანაგვე საქაღალდის (%s) შექმნა შეუძლებელია %s-ის წასაშლელად"
 
-#: gio/glocalfile.c:2252
+#: gio/glocalfile.c:2254
 #, c-format
 msgid "Unable to create trashing info file for %s: %s"
 msgstr "წაშლის ინფორმაციის ფაილის (%s) შექმნის შეცდომა: %s"
 
-#: gio/glocalfile.c:2323
+#: gio/glocalfile.c:2325
 #, c-format
 msgid "Unable to trash file %s across filesystem boundaries"
 msgstr "ფაილის (%s) წაშლა ფაილური სისტემის საზღვრებს მიღმა შეუძლებელია"
 
-#: gio/glocalfile.c:2327 gio/glocalfile.c:2383
+#: gio/glocalfile.c:2329 gio/glocalfile.c:2385
 #, c-format
 msgid "Unable to trash file %s: %s"
 msgstr "ფაილის (%s) წაშლა შეუძლებელია: %s"
 
-#: gio/glocalfile.c:2389
+#: gio/glocalfile.c:2391
 #, c-format
 msgid "Unable to trash file %s"
 msgstr "ფაილის (%s) წაშლა შეუძლებელია"
 
-#: gio/glocalfile.c:2415
+#: gio/glocalfile.c:2417
 #, c-format
 msgid "Error creating directory %s: %s"
 msgstr "საქაღალდის (%s) შექმნის შეცდომა: %s"
 
-#: gio/glocalfile.c:2444
+#: gio/glocalfile.c:2446
 #, c-format
 msgid "Filesystem does not support symbolic links"
 msgstr "ფაილურ სისტემას სიმბმულების მხარდაჭერა არ გააჩნია"
 
-#: gio/glocalfile.c:2447
+#: gio/glocalfile.c:2449
 #, c-format
 msgid "Error making symbolic link %s: %s"
 msgstr "სიმბოლური ბმულის ('%s') შექმნის შეცდომა:%s"
 
-#: gio/glocalfile.c:2490 gio/glocalfile.c:2525 gio/glocalfile.c:2582
+#: gio/glocalfile.c:2492 gio/glocalfile.c:2527 gio/glocalfile.c:2584
 #, c-format
 msgid "Error moving file %s: %s"
 msgstr "ფაილის (%s) გადატანის შეცდომა: %s"
 
-#: gio/glocalfile.c:2513
+#: gio/glocalfile.c:2515
 msgid "Can’t move directory over directory"
 msgstr "საქაღალდის საქაღალდეზე გადატანა შეუძლებელია"
 
-#: gio/glocalfile.c:2539 gio/glocalfileoutputstream.c:1110
+#: gio/glocalfile.c:2541 gio/glocalfileoutputstream.c:1110
 #: gio/glocalfileoutputstream.c:1124 gio/glocalfileoutputstream.c:1139
 #: gio/glocalfileoutputstream.c:1156 gio/glocalfileoutputstream.c:1170
 msgid "Backup file creation failed"
 msgstr "მარქაფის ფაილის შექმნის შეცდომა"
 
-#: gio/glocalfile.c:2558
+#: gio/glocalfile.c:2560
 #, c-format
 msgid "Error removing target file: %s"
 msgstr "სამიზნე ფაილის წაშლა შეუძლებელია: %s"
 
-#: gio/glocalfile.c:2572
+#: gio/glocalfile.c:2574
 msgid "Move between mounts not supported"
 msgstr "მიმაგრების წერტილებს შორის გადატანა მხარდაჭერილი არაა"
 
-#: gio/glocalfile.c:2748
+#: gio/glocalfile.c:2750
 #, c-format
 msgid "Could not determine the disk usage of %s: %s"
 msgstr "დისკზე %s-ის მიერ დაკავებული ადგილის გამოთვლის შეცდომა: %s"
@@ -3411,11 +3426,11 @@ msgstr ""
 msgid "Hostname “%s” contains “[” but not “]”"
 msgstr "ჰოსტის სახელი “%s” შეიცავს “[”-ს, მაგრამ არა “]”-ს"
 
-#: gio/gnetworkmonitorbase.c:221 gio/gnetworkmonitorbase.c:325
+#: gio/gnetworkmonitorbase.c:221 gio/gnetworkmonitorbase.c:326
 msgid "Network unreachable"
 msgstr "ქსელი მიუწვდომელია"
 
-#: gio/gnetworkmonitorbase.c:259 gio/gnetworkmonitorbase.c:289
+#: gio/gnetworkmonitorbase.c:259 gio/gnetworkmonitorbase.c:290
 msgid "Host unreachable"
 msgstr "ჰოსტი მიუწვდომელია"
 
@@ -3456,7 +3471,7 @@ msgstr "%s-სთვის გადაცემული ვექტორე
 msgid "Source stream is already closed"
 msgstr "შეყვანის ნაკადი უკვე დახურულია"
 
-#: gio/gproxyaddressenumerator.c:328 gio/gproxyaddressenumerator.c:346
+#: gio/gproxyaddressenumerator.c:328 gio/gproxyaddressenumerator.c:348
 msgid "Unspecified proxy lookup failure"
 msgstr "მიუთითებელი პროქსის მოძებნის შეცდომა"
 
@@ -3479,15 +3494,15 @@ msgstr "%s განხორციელებული არაა"
 msgid "Invalid domain"
 msgstr "დომენის არასწორი სახელი"
 
-#: gio/gresource.c:704 gio/gresource.c:966 gio/gresource.c:1006
-#: gio/gresource.c:1130 gio/gresource.c:1202 gio/gresource.c:1276
-#: gio/gresource.c:1357 gio/gresourcefile.c:482 gio/gresourcefile.c:606
+#: gio/gresource.c:706 gio/gresource.c:968 gio/gresource.c:1008
+#: gio/gresource.c:1132 gio/gresource.c:1204 gio/gresource.c:1278
+#: gio/gresource.c:1359 gio/gresourcefile.c:482 gio/gresourcefile.c:606
 #: gio/gresourcefile.c:757
 #, c-format
 msgid "The resource at “%s” does not exist"
 msgstr "რესურსი მისამართზე \"%s\" არ არსებობს"
 
-#: gio/gresource.c:871
+#: gio/gresource.c:873
 #, c-format
 msgid "The resource at “%s” failed to decompress"
 msgstr "რესურის, მისამართზე \"%s\", გაშლა ჩავარდა"
@@ -3505,26 +3520,26 @@ msgstr "რესურსი მისამართზე '%s' საქა
 msgid "Input stream doesn’t implement seek"
 msgstr "შეყვანის ნაკადს გადახვევის ფუნქცია განხორციელებული არ აქვს"
 
-#: gio/gresource-tool.c:502
+#: gio/gresource-tool.c:503
 msgid "List sections containing resources in an elf FILE"
 msgstr ""
 
-#: gio/gresource-tool.c:508
+#: gio/gresource-tool.c:509
 msgid ""
 "List resources\n"
 "If SECTION is given, only list resources in this section\n"
 "If PATH is given, only list matching resources"
 msgstr ""
 
-#: gio/gresource-tool.c:511 gio/gresource-tool.c:521
+#: gio/gresource-tool.c:512 gio/gresource-tool.c:522
 msgid "FILE [PATH]"
 msgstr "ფაილი [ბილიკი]"
 
-#: gio/gresource-tool.c:512 gio/gresource-tool.c:522 gio/gresource-tool.c:529
+#: gio/gresource-tool.c:513 gio/gresource-tool.c:523 gio/gresource-tool.c:530
 msgid "SECTION"
 msgstr "სექცია"
 
-#: gio/gresource-tool.c:517
+#: gio/gresource-tool.c:518
 msgid ""
 "List resources with details\n"
 "If SECTION is given, only list resources in this section\n"
@@ -3532,15 +3547,15 @@ msgid ""
 "Details include the section, size and compression"
 msgstr ""
 
-#: gio/gresource-tool.c:527
+#: gio/gresource-tool.c:528
 msgid "Extract a resource file to stdout"
 msgstr "რესურსის ფაილის stdout-ზე გაშლა"
 
-#: gio/gresource-tool.c:528
+#: gio/gresource-tool.c:529
 msgid "FILE PATH"
 msgstr "ფაილის ბილიკი"
 
-#: gio/gresource-tool.c:542
+#: gio/gresource-tool.c:543
 msgid ""
 "Usage:\n"
 "  gresource [--section SECTION] COMMAND [ARGS…]\n"
@@ -3556,7 +3571,7 @@ msgid ""
 "\n"
 msgstr ""
 
-#: gio/gresource-tool.c:556
+#: gio/gresource-tool.c:557
 #, c-format
 msgid ""
 "Usage:\n"
@@ -3571,19 +3586,19 @@ msgstr ""
 "%s\n"
 "\n"
 
-#: gio/gresource-tool.c:563
+#: gio/gresource-tool.c:564
 msgid "  SECTION   An (optional) elf section name\n"
 msgstr "  სექცია   (არასავალდებულო) elf სექციის სახელი\n"
 
-#: gio/gresource-tool.c:567 gio/gsettings-tool.c:720
+#: gio/gresource-tool.c:568 gio/gsettings-tool.c:720
 msgid "  COMMAND   The (optional) command to explain\n"
 msgstr "  ბრძანება   (არასავალდებულო) ასახსნელი ბრძანება\n"
 
-#: gio/gresource-tool.c:573
+#: gio/gresource-tool.c:574
 msgid "  FILE      An elf file (a binary or a shared library)\n"
 msgstr "  ფაილი      elf ფაილი (გამშვები ან გაზიარებული ბიბლიოთეკა)\n"
 
-#: gio/gresource-tool.c:576
+#: gio/gresource-tool.c:577
 msgid ""
 "  FILE      An elf file (a binary or a shared library)\n"
 "            or a compiled resource file\n"
@@ -3591,19 +3606,19 @@ msgstr ""
 "  ფაილი      elf ფაილი (გამშვები ან გაზიარებული ბიბლიოთეკა)\n"
 "               ან აგებული რესურსის ფაილი\n"
 
-#: gio/gresource-tool.c:580
+#: gio/gresource-tool.c:581
 msgid "[PATH]"
 msgstr "[ბილიკი]"
 
-#: gio/gresource-tool.c:582
+#: gio/gresource-tool.c:583
 msgid "  PATH      An (optional) resource path (may be partial)\n"
 msgstr ""
 
-#: gio/gresource-tool.c:583
+#: gio/gresource-tool.c:584
 msgid "PATH"
 msgstr "ბილიკი"
 
-#: gio/gresource-tool.c:585
+#: gio/gresource-tool.c:586
 msgid "  PATH      A resource path\n"
 msgstr "  ბილიკი      რესურსის ბილიკი\n"
 
@@ -3814,7 +3829,7 @@ msgid "Socket is already closed"
 msgstr "სოკეტი უკვე დახურულია"
 
 #: gio/gsocket.c:465 gio/gsocket.c:3291 gio/gsocket.c:4664 gio/gsocket.c:4722
-#: gio/gthreadedresolver.c:1452
+#: gio/gthreadedresolver.c:1453
 msgid "Socket I/O timed out"
 msgstr "სოკეტის I/O ვადა გავიდა"
 
@@ -4271,7 +4286,7 @@ msgstr "შეცდომა ფაილის დესკრიპტორ
 msgid "Error closing file descriptor: %s"
 msgstr "შეცდომა ფაილის დესკრიპტორის დახურვისას: %s"
 
-#: gio/gunixmounts.c:2814 gio/gunixmounts.c:2867
+#: gio/gunixmounts.c:2890 gio/gunixmounts.c:2943
 msgid "Filesystem root"
 msgstr "ფაილური სისტემის საწყისი საქაღალდე"
 
@@ -4439,50 +4454,50 @@ msgstr "პროგრამას სახელით \"%s\" სანიშ
 msgid "Failed to expand exec line “%s” with URI “%s”"
 msgstr ""
 
-#: glib/gconvert.c:370
+#: glib/gconvert.c:379
 msgid "Unrepresentable character in conversion input"
 msgstr ""
 
-#: glib/gconvert.c:397 glib/gutf8.c:954 glib/gutf8.c:1167 glib/gutf8.c:1304
+#: glib/gconvert.c:406 glib/gutf8.c:954 glib/gutf8.c:1167 glib/gutf8.c:1304
 #: glib/gutf8.c:1408
 msgid "Partial character sequence at end of input"
 msgstr "არასრული სიმბოლო შეტანის ტექსტის ბოლოს"
 
-#: glib/gconvert.c:668
+#: glib/gconvert.c:677
 #, c-format
 msgid "Cannot convert fallback “%s” to codeset “%s”"
 msgstr "ვერ ხერხდება \"%s\" სიმბოლოს გარდაქმნა კოდირებაში \"%s\""
 
-#: glib/gconvert.c:840
+#: glib/gconvert.c:849
 msgid "Embedded NUL byte in conversion input"
 msgstr "გადაყვანისას შეყვანაში ჩაშენებული NUL ბაიტი"
 
-#: glib/gconvert.c:861
+#: glib/gconvert.c:870
 msgid "Embedded NUL byte in conversion output"
 msgstr "გადაყვანისას გამოტანაში ჩაშენებული NUL ბაიტი"
 
-#: glib/gconvert.c:1599
+#: glib/gconvert.c:1608
 #, c-format
 msgid "The URI “%s” is not an absolute URI using the “file” scheme"
 msgstr ""
 "URI \"%s\" არ გახლავთ აბსოლუტური იდენტიფიკატორი \"file\" სქემის გამოყენებისას"
 
-#: glib/gconvert.c:1629
+#: glib/gconvert.c:1638
 #, c-format
 msgid "The URI “%s” is invalid"
 msgstr "URI იდენტიფიკატორი \"%s\" მცდარია"
 
-#: glib/gconvert.c:1642
+#: glib/gconvert.c:1651
 #, c-format
 msgid "The hostname of the URI “%s” is invalid"
 msgstr "URI იდენტიფიკატორის \"%s\" ჰოსტის სახელი მცდარია"
 
-#: glib/gconvert.c:1659
+#: glib/gconvert.c:1668
 #, c-format
 msgid "The URI “%s” contains invalidly escaped characters"
 msgstr "URI იდენტიფიკატორი \"%s\" შეიცავ მცდარ საკონტროლო სიმბოლოებს"
 
-#: glib/gconvert.c:1733
+#: glib/gconvert.c:1742
 #, c-format
 msgid "The pathname “%s” is not an absolute path"
 msgstr "ბილიკი \"%s\" აბსოლუტური არ გახლავთ"
@@ -4889,13 +4904,13 @@ msgid "Dec"
 msgstr "დეკ"
 
 #. Translators: 'before midday' indicator
-#: glib/gdatetime.c:589
+#: glib/gdatetime.c:592
 msgctxt "GDateTime"
 msgid "AM"
 msgstr "AM"
 
 #. Translators: 'after midday' indicator
-#: glib/gdatetime.c:592
+#: glib/gdatetime.c:595
 msgctxt "GDateTime"
 msgid "PM"
 msgstr "PM"
@@ -5853,21 +5868,21 @@ msgstr ""
 "მოულოდნელი შეცდომა ფუნქციაში g_io_channel_win32_poll() ქვეპროცესიდან "
 "მონაცემთა წაკითხვისას"
 
-#: glib/gstrfuncs.c:3334 glib/gstrfuncs.c:3436
+#: glib/gstrfuncs.c:3339 glib/gstrfuncs.c:3441
 msgid "Empty string is not a number"
 msgstr "ცარიელი სტრიქონი რიცხვი არაა"
 
-#: glib/gstrfuncs.c:3358
+#: glib/gstrfuncs.c:3363
 #, c-format
 msgid "“%s” is not a signed number"
 msgstr "\"%s\" ნიშნიანი რიცხვი არაა"
 
-#: glib/gstrfuncs.c:3368 glib/gstrfuncs.c:3472
+#: glib/gstrfuncs.c:3373 glib/gstrfuncs.c:3477
 #, c-format
 msgid "Number “%s” is out of bounds [%s, %s]"
 msgstr "რიცხვი \"%s\" დიაპაზონს გარეთაა [%s, %s]"
 
-#: glib/gstrfuncs.c:3462
+#: glib/gstrfuncs.c:3467
 #, c-format
 msgid "“%s” is not an unsigned number"
 msgstr "\"%s\" უნიშნო რიცხვი არაა"
@@ -6163,6 +6178,11 @@ msgstr "%.1f პბ"
 msgid "%.1f EB"
 msgstr "%.1f ებ"
 
+#~ msgid ""
+#~ "METHOD_RETURN message: REPLY_SERIAL header field is missing or invalid"
+#~ msgstr ""
+#~ "METHOD_RETURN შეტყობინება: REPLY_SERIAL თავსართის ველი აკლია ან არასწორია"
+
 #, c-format
 #~ msgid "Could not allocate %lu byte to read file “%s”"
 #~ msgid_plural "Could not allocate %lu bytes to read file “%s”"
index a6830b1..61fea05 100644 (file)
--- a/po/ru.po
+++ b/po/ru.po
@@ -16,8 +16,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ru\n"
 "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/glib/issues\n"
-"POT-Creation-Date: 2023-12-12 20:32+0000\n"
-"PO-Revision-Date: 2023-12-16 14:24+0300\n"
+"POT-Creation-Date: 2024-01-22 10:45+0000\n"
+"PO-Revision-Date: 2024-02-02 14:57+0300\n"
 "Last-Translator: Artur So <arturios2005@mail.ru>\n"
 "Language-Team: Русский <gnome-cyr@gnome.org>\n"
 "Language: ru\n"
@@ -26,7 +26,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
 "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 3.4.1\n"
+"X-Generator: Poedit 3.4.2\n"
 
 #: gio/gappinfo.c:339
 msgid "Setting default applications not supported yet"
@@ -73,7 +73,7 @@ msgstr "Заменить запущенный экземпляр"
 #: gio/gapplication-tool.c:47 gio/gapplication-tool.c:48 gio/gio-tool.c:230
 #: gio/gresource-tool.c:496 gio/gsettings-tool.c:586
 msgid "Print help"
-msgstr "Ð\9dапеÑ\87аÑ\82аÑ\82Ñ\8c справку"
+msgstr "Ð\92Ñ\8bвеÑ\81Ñ\82и справку"
 
 #: gio/gapplication-tool.c:49 gio/gresource-tool.c:497 gio/gresource-tool.c:565
 msgid "[COMMAND]"
@@ -89,13 +89,12 @@ msgstr "Вывести информацию о версии и выйти"
 
 #: gio/gapplication-tool.c:55
 msgid "List applications"
-msgstr "Ð\92Ñ\8bвеÑ\81Ñ\82и Ñ\81писок приложений"
+msgstr "Список приложений"
 
 #: gio/gapplication-tool.c:56
 msgid "List the installed D-Bus activatable applications (by .desktop files)"
 msgstr ""
-"Вывести список установленных активируемых по D-Bus приложений (по desktop-"
-"файлам)"
+"Список установленных активируемых по D-Bus приложений (по desktop-файлам)"
 
 #: gio/gapplication-tool.c:59
 msgid "Launch an application"
@@ -312,7 +311,7 @@ msgstr "Поток уже закрыт"
 msgid "Truncate not supported on base stream"
 msgstr "Усечение не поддерживается в базовом потоке"
 
-#: gio/gcancellable.c:326 gio/gdbusconnection.c:1844 gio/gdbusprivate.c:1432
+#: gio/gcancellable.c:326 gio/gdbusconnection.c:1844 gio/gdbusprivate.c:1434
 #: gio/gsimpleasyncresult.c:871 gio/gsimpleasyncresult.c:897
 #, c-format
 msgid "Operation was cancelled"
@@ -778,53 +777,67 @@ msgstr "Поддерево уже экспортировано для %s"
 msgid "Object does not exist at path “%s”"
 msgstr "Объект по пути «%s» не существует"
 
-#: gio/gdbusmessage.c:1303
-msgid "type is INVALID"
-msgstr "тип является НЕДОПУСТИМЫМ"
-
-#: gio/gdbusmessage.c:1321
-msgid "METHOD_CALL message: PATH or MEMBER header field is missing or invalid"
+#: gio/gdbusmessage.c:1351
+#, c-format
+msgid "%s message: %s header field is invalid; expected a value of type ‘%s’"
 msgstr ""
-"Сообщение METHOD_CALL: отсутствует или недопустимо поле заголовка PATH или "
-"MEMBER"
+"%s сообщение: поле заголовка %s недействительно; ожидаемое значение типа '%s'"
 
-#: gio/gdbusmessage.c:1337
-msgid "METHOD_RETURN message: REPLY_SERIAL header field is missing or invalid"
-msgstr ""
-"Сообщение METHOD_RETURN: отсутствует или недопустимо поле заголовка "
-"REPLY_SERIAL"
+#: gio/gdbusmessage.c:1374
+#, c-format
+msgid "%s message: %s header field is missing or invalid"
+msgstr "%s cообщение: отсутствует или недопустимо поле заголовка %s"
 
-#: gio/gdbusmessage.c:1357
-msgid ""
-"ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing or invalid"
-msgstr ""
-"Сообщение ERROR: отсутствует или недопустимо поле заголовка REPLY_SERIAL или "
-"ERROR_NAME"
+#: gio/gdbusmessage.c:1413
+#, c-format
+msgid "%s message: INVALID header field supplied"
+msgstr "%s сообщение: поле заголовка INVALID предоставлено"
 
-#: gio/gdbusmessage.c:1381
+#: gio/gdbusmessage.c:1424
+#, c-format
 msgid ""
-"SIGNAL message: PATH, INTERFACE or MEMBER header field is missing or invalid"
+"%s message: PATH header field is using the reserved value /org/freedesktop/"
+"DBus/Local"
 msgstr ""
-"Сообщение SIGNAL: отсутствует или недопустимо поле заголовка PATH, INTERFACE "
-"или MEMBER"
+"%s сообщение: поле заголовка PATH использует зарезервированное значение /org/"
+"freedesktop/DBus/Local"
 
-#: gio/gdbusmessage.c:1389
+#: gio/gdbusmessage.c:1437
+#, c-format
 msgid ""
-"SIGNAL message: The PATH header field is using the reserved value /org/"
-"freedesktop/DBus/Local"
+"%s message: INTERFACE header field does not contain a valid interface name"
 msgstr ""
-"Сообщение SIGNAL: поле заголовка PATH использует зарезервированное значение /"
-"org/freedesktop/DBus/Local"
+"%s сообщение: поле заголовка INTERFACE не содержит действительного имени "
+"интерфейса"
 
-#: gio/gdbusmessage.c:1397
+#: gio/gdbusmessage.c:1446
+#, c-format
 msgid ""
-"SIGNAL message: The INTERFACE header field is using the reserved value org."
+"%s message: INTERFACE header field is using the reserved value org."
 "freedesktop.DBus.Local"
 msgstr ""
-"Сообщение SIGNAL: поле заголовка INTERFACE использует зарезервированное "
-"значение org.freedesktop.DBus.Local"
+"%s сообщение: поле заголовка INTERFACE использует зарезервированное значение "
+"org.freedesktop.DBus.Local"
 
-#: gio/gdbusmessage.c:1446 gio/gdbusmessage.c:1506
+#: gio/gdbusmessage.c:1459
+#, c-format
+msgid "%s message: MEMBER header field does not contain a valid member name"
+msgstr ""
+"%s сообщение: поле заголовка MEMBER не содержит действительного имени "
+"участника"
+
+#: gio/gdbusmessage.c:1472
+#, c-format
+msgid "%s message: ERROR_NAME header field does not contain a valid error name"
+msgstr ""
+"%s сообщение: поле заголовка ERROR_NAME не содержит действительного имени "
+"ошибки"
+
+#: gio/gdbusmessage.c:1511
+msgid "type is INVALID"
+msgstr "тип является НЕДОПУСТИМЫМ"
+
+#: gio/gdbusmessage.c:1581 gio/gdbusmessage.c:1641
 #, c-format
 msgid "Wanted to read %lu byte but only got %lu"
 msgid_plural "Wanted to read %lu bytes but only got %lu"
@@ -832,12 +845,12 @@ msgstr[0] "Требовалось прочитать %lu байт, но проч
 msgstr[1] "Требовалось прочитать %lu байта, но прочитано только %lu"
 msgstr[2] "Требовалось прочитать %lu байт, но прочитано только %lu"
 
-#: gio/gdbusmessage.c:1460
+#: gio/gdbusmessage.c:1595
 #, c-format
 msgid "Expected NUL byte after the string “%s” but found byte %d"
 msgstr "Ожидался байт NUL после строки «%s», но найден байт %d"
 
-#: gio/gdbusmessage.c:1479
+#: gio/gdbusmessage.c:1614
 #, c-format
 msgid ""
 "Expected valid UTF-8 string but found invalid bytes at byte offset %d "
@@ -847,21 +860,21 @@ msgstr ""
 "(смещение %d, длина строки %d). Корректная строка UTF-8 вплоть до тех байт: "
 "«%s»"
 
-#: gio/gdbusmessage.c:1543 gio/gdbusmessage.c:1819 gio/gdbusmessage.c:2030
+#: gio/gdbusmessage.c:1678 gio/gdbusmessage.c:1954 gio/gdbusmessage.c:2165
 msgid "Value nested too deeply"
 msgstr "Слишком глубокая иерархия"
 
-#: gio/gdbusmessage.c:1711
+#: gio/gdbusmessage.c:1846
 #, c-format
 msgid "Parsed value “%s” is not a valid D-Bus object path"
 msgstr "Разобранное значение «%s» не является допустимым путём объекта D-Bus"
 
-#: gio/gdbusmessage.c:1735
+#: gio/gdbusmessage.c:1870
 #, c-format
 msgid "Parsed value “%s” is not a valid D-Bus signature"
 msgstr "Разобранное значение «%s» не является допустимой подписью D-Bus"
 
-#: gio/gdbusmessage.c:1786
+#: gio/gdbusmessage.c:1921
 #, c-format
 msgid ""
 "Encountered array of length %u byte. Maximum length is 2<<26 bytes (64 MiB)."
@@ -877,7 +890,7 @@ msgstr[2] ""
 "Обнаружен массив длинной %u байт. Максимальная длина равна 2<<26 байт (64 "
 "МиБ)."
 
-#: gio/gdbusmessage.c:1806
+#: gio/gdbusmessage.c:1941
 #, c-format
 msgid ""
 "Encountered array of type “a%c”, expected to have a length a multiple of %u "
@@ -886,24 +899,24 @@ msgstr ""
 "Получен массив типа «a%c», который должен иметь размер кратный %u (байт), но "
 "массив имеет длину %u (байт)"
 
-#: gio/gdbusmessage.c:1960 gio/gdbusmessage.c:2687
+#: gio/gdbusmessage.c:2095 gio/gdbusmessage.c:2822
 msgid "Empty structures (tuples) are not allowed in D-Bus"
 msgstr "Пустые структуры (записи) не допускаются в D-Bus"
 
-#: gio/gdbusmessage.c:2014
+#: gio/gdbusmessage.c:2149
 #, c-format
 msgid "Parsed value “%s” for variant is not a valid D-Bus signature"
 msgstr ""
 "Разобранное значение «%s» для варианта не является допустимой подписью D-Bus"
 
-#: gio/gdbusmessage.c:2055
+#: gio/gdbusmessage.c:2190
 #, c-format
 msgid ""
 "Error deserializing GVariant with type string “%s” from the D-Bus wire format"
 msgstr ""
 "Ошибка десериализации GVariant с типом строки «%s» из формата D-Bus wire"
 
-#: gio/gdbusmessage.c:2240
+#: gio/gdbusmessage.c:2375
 #, c-format
 msgid ""
 "Invalid endianness value. Expected 0x6c (“l”) or 0x42 (“B”) but found value "
@@ -912,27 +925,27 @@ msgstr ""
 "Неправильный порядок байтов в значении. Ожидался 0x6c ('l') или 0x42 ('B'), "
 "но найдено значение 0x%02x"
 
-#: gio/gdbusmessage.c:2259
+#: gio/gdbusmessage.c:2394
 #, c-format
 msgid "Invalid major protocol version. Expected 1 but found %d"
 msgstr "Неправильный старший номер версии протокола. Ожидался 1, но найден %d"
 
-#: gio/gdbusmessage.c:2317 gio/gdbusmessage.c:2923
+#: gio/gdbusmessage.c:2452 gio/gdbusmessage.c:3058
 msgid "Signature header found but is not of type signature"
 msgstr "Заголовок подписи найден, но его тип отличается от подписи"
 
-#: gio/gdbusmessage.c:2329
+#: gio/gdbusmessage.c:2464
 #, c-format
 msgid "Signature header with signature “%s” found but message body is empty"
 msgstr "Найден заголовок подписи с подписью «%s», но тело сообщения пусто"
 
-#: gio/gdbusmessage.c:2344
+#: gio/gdbusmessage.c:2479
 #, c-format
 msgid "Parsed value “%s” is not a valid D-Bus signature (for body)"
 msgstr ""
 "Разобранное значение «%s» не является допустимой подписью D-Bus (для тела)"
 
-#: gio/gdbusmessage.c:2384
+#: gio/gdbusmessage.c:2519
 #, c-format
 msgid "No signature header in message but the message body is %u byte"
 msgid_plural "No signature header in message but the message body is %u bytes"
@@ -944,17 +957,17 @@ msgstr[1] ""
 msgstr[2] ""
 "Отсутствует заголовок подписи в сообщении, но тело сообщения занимает %u байт"
 
-#: gio/gdbusmessage.c:2394
+#: gio/gdbusmessage.c:2529
 msgid "Cannot deserialize message: "
 msgstr "Не удалось выполнить извлечение сообщения: "
 
-#: gio/gdbusmessage.c:2740
+#: gio/gdbusmessage.c:2875
 #, c-format
 msgid ""
 "Error serializing GVariant with type string “%s” to the D-Bus wire format"
 msgstr "Ошибка сериализации GVariant с типом строки «%s» в формат D-Bus wire"
 
-#: gio/gdbusmessage.c:2877
+#: gio/gdbusmessage.c:3012
 #, c-format
 msgid ""
 "Number of file descriptors in message (%d) differs from header field (%d)"
@@ -962,16 +975,16 @@ msgstr ""
 "Количество дескрипторов файлов в сообщении (%d) отличается от указанного в "
 "заголовке (%d)"
 
-#: gio/gdbusmessage.c:2885
+#: gio/gdbusmessage.c:3020
 msgid "Cannot serialize message: "
 msgstr "Не удалось сериализовать сообщение: "
 
-#: gio/gdbusmessage.c:2938
+#: gio/gdbusmessage.c:3073
 #, c-format
 msgid "Message body has signature “%s” but there is no signature header"
 msgstr "Тело сообщения имеет подпись «%s», но нет заголовка подписи"
 
-#: gio/gdbusmessage.c:2948
+#: gio/gdbusmessage.c:3083
 #, c-format
 msgid ""
 "Message body has type signature “%s” but signature in the header field is "
@@ -980,38 +993,38 @@ msgstr ""
 "Тело сообщения имеет тип подписи «%s», но значение подписи в поле заголовка "
 "равно «%s»"
 
-#: gio/gdbusmessage.c:2964
+#: gio/gdbusmessage.c:3099
 #, c-format
 msgid "Message body is empty but signature in the header field is “(%s)”"
 msgstr ""
 "Тело сообщения пусто, но значение подписи в поле заголовка равно «(%s)»"
 
-#: gio/gdbusmessage.c:3538
+#: gio/gdbusmessage.c:3673
 #, c-format
 msgid "Error return with body of type “%s”"
 msgstr "Возвращена ошибка с телом типа «%s»"
 
-#: gio/gdbusmessage.c:3546
+#: gio/gdbusmessage.c:3681
 msgid "Error return with empty body"
 msgstr "Возвращена ошибка с пустым телом"
 
-#: gio/gdbusprivate.c:2199
+#: gio/gdbusprivate.c:2201
 #, c-format
 msgid "(Type any character to close this window)\n"
 msgstr "(Чтобы закрыть это окно, введите любой символ)\n"
 
-#: gio/gdbusprivate.c:2385
+#: gio/gdbusprivate.c:2387
 #, c-format
 msgid "Session dbus not running, and autolaunch failed"
 msgstr "Сеанс dbus не запущен, и автозапуск не выполнился"
 
-#: gio/gdbusprivate.c:2408
+#: gio/gdbusprivate.c:2410
 #, c-format
 msgid "Unable to get Hardware profile: %s"
 msgstr "Не удалось получить профиль аппаратуры: %s"
 
 #. Translators: Both placeholders are file paths
-#: gio/gdbusprivate.c:2464
+#: gio/gdbusprivate.c:2466
 #, c-format
 msgid "Unable to load %s or %s: "
 msgstr "Не удалось загрузить %s или %s: "
@@ -1607,37 +1620,37 @@ msgstr "Ответ HTTP с прокси слишком большой"
 msgid "HTTP proxy server closed connection unexpectedly."
 msgstr "Cервер прокси HTTP неожиданно закрыл соединение."
 
-#: gio/gicon.c:297
+#: gio/gicon.c:298
 #, c-format
 msgid "Wrong number of tokens (%d)"
 msgstr "Неверное число лексем (%d)"
 
-#: gio/gicon.c:317
+#: gio/gicon.c:318
 #, c-format
 msgid "No type for class name %s"
 msgstr "Нет типа для класса с именем %s"
 
-#: gio/gicon.c:327
+#: gio/gicon.c:328
 #, c-format
 msgid "Type %s does not implement the GIcon interface"
 msgstr "Тип %s не реализует интерфейс GIcon"
 
-#: gio/gicon.c:338
+#: gio/gicon.c:339
 #, c-format
 msgid "Type %s is not classed"
 msgstr "Тип %s не является классифицируемым"
 
-#: gio/gicon.c:352
+#: gio/gicon.c:353
 #, c-format
 msgid "Malformed version number: %s"
 msgstr "Некорректный номер версии: %s"
 
-#: gio/gicon.c:366
+#: gio/gicon.c:367
 #, c-format
 msgid "Type %s does not implement from_tokens() on the GIcon interface"
 msgstr "Тип %s не реализует from_tokens() интерфейса GIcon"
 
-#: gio/gicon.c:468
+#: gio/gicon.c:469
 msgid "Can’t handle the supplied version of the icon encoding"
 msgstr "Не удалось обработать данную версию текстового представления значка"
 
@@ -3516,11 +3529,11 @@ msgstr ""
 msgid "Hostname “%s” contains “[” but not “]”"
 msgstr "Имя узла «%s» содержит «[», но не «]»"
 
-#: gio/gnetworkmonitorbase.c:221 gio/gnetworkmonitorbase.c:325
+#: gio/gnetworkmonitorbase.c:221 gio/gnetworkmonitorbase.c:326
 msgid "Network unreachable"
 msgstr "Сеть недоступна"
 
-#: gio/gnetworkmonitorbase.c:259 gio/gnetworkmonitorbase.c:289
+#: gio/gnetworkmonitorbase.c:259 gio/gnetworkmonitorbase.c:290
 msgid "Host unreachable"
 msgstr "Узел недоступен"
 
@@ -3561,7 +3574,7 @@ msgstr "Сумма массивов, переданных в «%s» слишко
 msgid "Source stream is already closed"
 msgstr "Исходный поток уже закрыт"
 
-#: gio/gproxyaddressenumerator.c:328 gio/gproxyaddressenumerator.c:346
+#: gio/gproxyaddressenumerator.c:328 gio/gproxyaddressenumerator.c:348
 msgid "Unspecified proxy lookup failure"
 msgstr "Неуказанный сбой поиска прокси-сервера"
 
@@ -3584,15 +3597,15 @@ msgstr "«%s» не реализовано"
 msgid "Invalid domain"
 msgstr "Недопустимый домен"
 
-#: gio/gresource.c:704 gio/gresource.c:966 gio/gresource.c:1006
-#: gio/gresource.c:1130 gio/gresource.c:1202 gio/gresource.c:1276
-#: gio/gresource.c:1357 gio/gresourcefile.c:482 gio/gresourcefile.c:606
+#: gio/gresource.c:706 gio/gresource.c:968 gio/gresource.c:1008
+#: gio/gresource.c:1132 gio/gresource.c:1204 gio/gresource.c:1278
+#: gio/gresource.c:1359 gio/gresourcefile.c:482 gio/gresourcefile.c:606
 #: gio/gresourcefile.c:757
 #, c-format
 msgid "The resource at “%s” does not exist"
 msgstr "Ресурс из «%s» не существует"
 
-#: gio/gresource.c:871
+#: gio/gresource.c:873
 #, c-format
 msgid "The resource at “%s” failed to decompress"
 msgstr "Не удалось распаковать ресурс из «%s»"
@@ -3973,8 +3986,8 @@ msgstr "Недопустимый сокет, инициализация не у
 msgid "Socket is already closed"
 msgstr "Сокет уже закрыт"
 
-#: gio/gsocket.c:465 gio/gsocket.c:3291 gio/gsocket.c:4522 gio/gsocket.c:4580
-#: gio/gthreadedresolver.c:1452
+#: gio/gsocket.c:465 gio/gsocket.c:3291 gio/gsocket.c:4664 gio/gsocket.c:4722
+#: gio/gthreadedresolver.c:1453
 msgid "Socket I/O timed out"
 msgstr "Превышено время ожидания ввода-вывода сокета"
 
@@ -4089,56 +4102,56 @@ msgstr "Не удалось получить ошибку ожидания: "
 msgid "Error receiving data: %s"
 msgstr "Ошибка при получении данных: %s"
 
-#: gio/gsocket.c:3553
+#: gio/gsocket.c:3695
 #, c-format
 msgid "Error sending data: %s"
 msgstr "Ошибка при отправлении данных: %s"
 
-#: gio/gsocket.c:3740
+#: gio/gsocket.c:3882
 #, c-format
 msgid "Unable to shutdown socket: %s"
 msgstr "Не удалось выключить сокет: %s"
 
-#: gio/gsocket.c:3821
+#: gio/gsocket.c:3963
 #, c-format
 msgid "Error closing socket: %s"
 msgstr "Произошла ошибка при закрытии сокета: %s"
 
-#: gio/gsocket.c:4515
+#: gio/gsocket.c:4657
 #, c-format
 msgid "Waiting for socket condition: %s"
 msgstr "Ожидание состояния сокета: %s"
 
-#: gio/gsocket.c:4905 gio/gsocket.c:4921 gio/gsocket.c:4934
+#: gio/gsocket.c:5047 gio/gsocket.c:5063 gio/gsocket.c:5076
 #, c-format
 msgid "Unable to send message: %s"
 msgstr "Не удалось отправить сообщение: %s"
 
-#: gio/gsocket.c:4906 gio/gsocket.c:4922 gio/gsocket.c:4935
+#: gio/gsocket.c:5048 gio/gsocket.c:5064 gio/gsocket.c:5077
 msgid "Message vectors too large"
 msgstr "Слишком большие массивы сообщения"
 
-#: gio/gsocket.c:4951 gio/gsocket.c:4953 gio/gsocket.c:5100 gio/gsocket.c:5185
-#: gio/gsocket.c:5363 gio/gsocket.c:5403 gio/gsocket.c:5405
+#: gio/gsocket.c:5093 gio/gsocket.c:5095 gio/gsocket.c:5242 gio/gsocket.c:5327
+#: gio/gsocket.c:5505 gio/gsocket.c:5545 gio/gsocket.c:5547
 #, c-format
 msgid "Error sending message: %s"
 msgstr "Произошла ошибка при отправлении сообщения: %s"
 
-#: gio/gsocket.c:5127
+#: gio/gsocket.c:5269
 msgid "GSocketControlMessage not supported on Windows"
 msgstr "GSocketControlMessage не поддерживается в Windows"
 
-#: gio/gsocket.c:5600 gio/gsocket.c:5676 gio/gsocket.c:5902
+#: gio/gsocket.c:5742 gio/gsocket.c:5818 gio/gsocket.c:6044
 #, c-format
 msgid "Error receiving message: %s"
 msgstr "Произошла ошибка при получении сообщения: %s"
 
-#: gio/gsocket.c:6187 gio/gsocket.c:6198 gio/gsocket.c:6261
+#: gio/gsocket.c:6329 gio/gsocket.c:6340 gio/gsocket.c:6403
 #, c-format
 msgid "Unable to read socket credentials: %s"
 msgstr "Не удалось прочитать полномочия сокета: %s"
 
-#: gio/gsocket.c:6270
+#: gio/gsocket.c:6412
 msgid "g_socket_get_credentials not implemented for this OS"
 msgstr "g_socket_get_credentials не реализован для данной ОС"
 
@@ -5067,13 +5080,13 @@ msgid "Dec"
 msgstr "Дек"
 
 #. Translators: 'before midday' indicator
-#: glib/gdatetime.c:589
+#: glib/gdatetime.c:592
 msgctxt "GDateTime"
 msgid "AM"
 msgstr "ДП (AM)"
 
 #. Translators: 'after midday' indicator
-#: glib/gdatetime.c:592
+#: glib/gdatetime.c:595
 msgctxt "GDateTime"
 msgid "PM"
 msgstr "ПП (PM)"
@@ -5590,184 +5603,184 @@ msgstr "Отсутствует аргумент для %s"
 msgid "Unknown option %s"
 msgstr "Неизвестный параметр %s"
 
-#: glib/gregex.c:479
+#: glib/gregex.c:486
 msgid "corrupted object"
 msgstr "повреждённый объект"
 
-#: glib/gregex.c:481
+#: glib/gregex.c:488
 msgid "out of memory"
 msgstr "закончилась память"
 
-#: glib/gregex.c:496
+#: glib/gregex.c:503
 msgid "internal error"
 msgstr "внутренняя ошибка"
 
-#: glib/gregex.c:498
+#: glib/gregex.c:505
 msgid "the pattern contains items not supported for partial matching"
 msgstr ""
 "шаблон содержит элементы, которые не поддерживаются при поиске частичного "
 "совпадения"
 
-#: glib/gregex.c:500
+#: glib/gregex.c:507
 msgid "back references as conditions are not supported for partial matching"
 msgstr ""
 "условия в виде обратных ссылок при поиске частичного совпадения не "
 "поддерживаются"
 
-#: glib/gregex.c:506
+#: glib/gregex.c:513
 msgid "recursion limit reached"
 msgstr "достигнут предел рекурсии"
 
-#: glib/gregex.c:508
+#: glib/gregex.c:515
 msgid "bad offset"
 msgstr "неправильное смещение"
 
-#: glib/gregex.c:510
+#: glib/gregex.c:517
 msgid "recursion loop"
 msgstr "зацикливание рекурсии"
 
 #. should not happen in GRegex since we check modes before each match
-#: glib/gregex.c:513
+#: glib/gregex.c:520
 msgid "matching mode is requested that was not compiled for JIT"
 msgstr "запрашивается режим согласования, который не был скомпилирован для JIT"
 
-#: glib/gregex.c:534 glib/gregex.c:1850
+#: glib/gregex.c:541 glib/gregex.c:1869
 msgid "unknown error"
 msgstr "неизвестная ошибка"
 
-#: glib/gregex.c:555
+#: glib/gregex.c:562
 msgid "\\ at end of pattern"
 msgstr "\\ в конце шаблона"
 
-#: glib/gregex.c:559
+#: glib/gregex.c:566
 msgid "\\c at end of pattern"
 msgstr "\\c в конце шаблона"
 
-#: glib/gregex.c:564
+#: glib/gregex.c:571
 msgid "unrecognized character following \\"
 msgstr "неопознанный символ следует за \\"
 
-#: glib/gregex.c:568
+#: glib/gregex.c:575
 msgid "numbers out of order in {} quantifier"
 msgstr "числа в квантификаторе {} в неправильном порядке"
 
-#: glib/gregex.c:572
+#: glib/gregex.c:579
 msgid "number too big in {} quantifier"
 msgstr "слишком большое число в квантификаторе {}"
 
-#: glib/gregex.c:576
+#: glib/gregex.c:583
 msgid "missing terminating ] for character class"
 msgstr "отсутствует завершающая ] для класса символов"
 
-#: glib/gregex.c:580
+#: glib/gregex.c:587
 msgid "invalid escape sequence in character class"
 msgstr "неверное экранирование в классе символов"
 
-#: glib/gregex.c:584
+#: glib/gregex.c:591
 msgid "range out of order in character class"
 msgstr "диапазон в классе символов в неправильном порядке"
 
-#: glib/gregex.c:589
+#: glib/gregex.c:596
 msgid "nothing to repeat"
 msgstr "нечего повторять"
 
-#: glib/gregex.c:593
+#: glib/gregex.c:600
 msgid "unrecognized character after (? or (?-"
 msgstr "неопознанный символ после (? или (?-"
 
-#: glib/gregex.c:597
+#: glib/gregex.c:604
 msgid "POSIX named classes are supported only within a class"
 msgstr "Именованные классы POSIX поддерживаются только внутри класса"
 
-#: glib/gregex.c:601
+#: glib/gregex.c:608
 msgid "POSIX collating elements are not supported"
 msgstr "Сортировочные элементы POSIX не поддерживаются"
 
-#: glib/gregex.c:607
+#: glib/gregex.c:614
 msgid "missing terminating )"
 msgstr "отсутствует завершающая )"
 
-#: glib/gregex.c:611
+#: glib/gregex.c:618
 msgid "reference to non-existent subpattern"
 msgstr "ссылка на несуществующий подшаблон"
 
-#: glib/gregex.c:615
+#: glib/gregex.c:622
 msgid "missing ) after comment"
 msgstr "отсутствует ) после комментария"
 
-#: glib/gregex.c:619
+#: glib/gregex.c:626
 msgid "regular expression is too large"
 msgstr "слишком длинное регулярное выражение"
 
-#: glib/gregex.c:623
+#: glib/gregex.c:630
 msgid "malformed number or name after (?("
 msgstr "ошибочное число или имя после (?("
 
-#: glib/gregex.c:627
+#: glib/gregex.c:634
 msgid "lookbehind assertion is not fixed length"
 msgstr "lookbehind-утверждение не имеет фиксированную длину"
 
-#: glib/gregex.c:631
+#: glib/gregex.c:638
 msgid "conditional group contains more than two branches"
 msgstr "условная группа содержит более двух ветвей"
 
-#: glib/gregex.c:635
+#: glib/gregex.c:642
 msgid "assertion expected after (?("
 msgstr "ожидалось утверждение после (?("
 
-#: glib/gregex.c:639
+#: glib/gregex.c:646
 msgid "a numbered reference must not be zero"
 msgstr "номерная ссылка не может быть нулём"
 
-#: glib/gregex.c:643
+#: glib/gregex.c:650
 msgid "unknown POSIX class name"
 msgstr "неизвестное имя класса POSIX"
 
-#: glib/gregex.c:648
+#: glib/gregex.c:655
 msgid "character value in \\x{...} sequence is too large"
 msgstr "значение символа в последовательности \\x{...} слишком велико"
 
-#: glib/gregex.c:652
+#: glib/gregex.c:659
 msgid "\\C not allowed in lookbehind assertion"
 msgstr "\\C запрещено в lookbehind-утверждениях"
 
-#: glib/gregex.c:656
+#: glib/gregex.c:663
 msgid "missing terminator in subpattern name"
 msgstr "отсутствует завершающий символ в имени подшаблона"
 
-#: glib/gregex.c:660
+#: glib/gregex.c:667
 msgid "two named subpatterns have the same name"
 msgstr "два именованных подшаблона имеют одинаковое имя"
 
-#: glib/gregex.c:664
+#: glib/gregex.c:671
 msgid "malformed \\P or \\p sequence"
 msgstr "ошибочная последовательность \\P или \\p"
 
-#: glib/gregex.c:668
+#: glib/gregex.c:675
 msgid "unknown property name after \\P or \\p"
 msgstr "неизвестное имя свойства после \\P или \\p"
 
-#: glib/gregex.c:672
+#: glib/gregex.c:679
 msgid "subpattern name is too long (maximum 32 characters)"
 msgstr "имя подшаблона слишком длинное (не должно превышать 32 символа)"
 
-#: glib/gregex.c:676
+#: glib/gregex.c:683
 msgid "too many named subpatterns (maximum 10,000)"
 msgstr "слишком много именованных подшаблонов (не должно быть больше 10 000)"
 
-#: glib/gregex.c:680
+#: glib/gregex.c:687
 msgid "octal value is greater than \\377"
 msgstr "восьмеричное значение превышает \\377"
 
-#: glib/gregex.c:684
+#: glib/gregex.c:691
 msgid "DEFINE group contains more than one branch"
 msgstr "Группа DEFINE содержит более одной ветви"
 
-#: glib/gregex.c:688
+#: glib/gregex.c:695
 msgid "inconsistent NEWLINE options"
 msgstr "противоречивые параметры NEWLINE"
 
-#: glib/gregex.c:692
+#: glib/gregex.c:699
 msgid ""
 "\\g is not followed by a braced, angle-bracketed, or quoted name or number, "
 "or by a plain number"
@@ -5775,122 +5788,122 @@ msgstr ""
 "за \\g не следует имя или число в скобках, угловых скобках или кавычках, или "
 "просто число"
 
-#: glib/gregex.c:697
+#: glib/gregex.c:704
 msgid "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)"
 msgstr "нельзя указать параметр для (*ACCEPT), (*FAIL) или (*COMMIT)"
 
-#: glib/gregex.c:701
+#: glib/gregex.c:708
 msgid "(*VERB) not recognized"
 msgstr "значение (*VERB) не распознано"
 
-#: glib/gregex.c:705
+#: glib/gregex.c:712
 msgid "number is too big"
 msgstr "слишком большое число"
 
-#: glib/gregex.c:709
+#: glib/gregex.c:716
 msgid "missing subpattern name after (?&"
 msgstr "отсутствует имя подшаблона после (?&"
 
-#: glib/gregex.c:713
+#: glib/gregex.c:720
 msgid "different names for subpatterns of the same number are not allowed"
 msgstr ""
 "не допускаются использовать различные имена для подшаблонов с одинаковым "
 "номером"
 
-#: glib/gregex.c:717
+#: glib/gregex.c:724
 msgid "(*MARK) must have an argument"
 msgstr "для (*MARK) требуется параметр"
 
-#: glib/gregex.c:721
+#: glib/gregex.c:728
 msgid "\\c must be followed by an ASCII character"
 msgstr "за \\c должен быть символ ASCII"
 
-#: glib/gregex.c:725
+#: glib/gregex.c:732
 msgid "\\k is not followed by a braced, angle-bracketed, or quoted name"
 msgstr "за \\k не следует имя в скобках, угловых скобках или кавычках"
 
-#: glib/gregex.c:729
+#: glib/gregex.c:736
 msgid "\\N is not supported in a class"
 msgstr "\\N в классе не поддерживается"
 
-#: glib/gregex.c:733
+#: glib/gregex.c:740
 msgid "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)"
 msgstr "слишком длинное имя в (*MARK), (*PRUNE), (*SKIP) или (*THEN)"
 
-#: glib/gregex.c:737 glib/gregex.c:873
+#: glib/gregex.c:744 glib/gregex.c:880
 msgid "code overflow"
 msgstr "переполнение кода"
 
-#: glib/gregex.c:741
+#: glib/gregex.c:748
 msgid "unrecognized character after (?P"
 msgstr "неопознанный символ после (?P"
 
-#: glib/gregex.c:745
+#: glib/gregex.c:752
 msgid "overran compiling workspace"
 msgstr "переполнение рабочего пространства компиляции"
 
-#: glib/gregex.c:749
+#: glib/gregex.c:756
 msgid "previously-checked referenced subpattern not found"
 msgstr "не найден ранее проверенный подшаблон со ссылкой"
 
-#: glib/gregex.c:872 glib/gregex.c:1134 glib/gregex.c:2456
+#: glib/gregex.c:879 glib/gregex.c:1153 glib/gregex.c:2475
 #, c-format
 msgid "Error while matching regular expression %s: %s"
 msgstr ""
 "Во время поиска совпадений с регулярным выражением %s возникла ошибка: %s"
 
-#: glib/gregex.c:1734
+#: glib/gregex.c:1753
 msgid "PCRE library is compiled without UTF8 support"
 msgstr "Библиотека PCRE собрана без поддержки UTF-8"
 
-#: glib/gregex.c:1742
+#: glib/gregex.c:1761
 msgid "PCRE library is compiled with incompatible options"
 msgstr "Библиотека PCRE собрана с несовместимыми параметрами"
 
-#: glib/gregex.c:1859
+#: glib/gregex.c:1878
 #, c-format
 msgid "Error while compiling regular expression ‘%s’ at char %s: %s"
 msgstr ""
 "Произошла ошибка при компиляции регулярного выражения '%s' у символа с "
 "номером %s: %s"
 
-#: glib/gregex.c:2899
+#: glib/gregex.c:2918
 msgid "hexadecimal digit or “}” expected"
 msgstr "ожидалась шестнадцатеричная цифра или символ «}»"
 
-#: glib/gregex.c:2915
+#: glib/gregex.c:2934
 msgid "hexadecimal digit expected"
 msgstr "ожидалась шестнадцатеричная цифра"
 
-#: glib/gregex.c:2955
+#: glib/gregex.c:2974
 msgid "missing “<” in symbolic reference"
 msgstr "в символьной ссылке отсутствует «<»"
 
-#: glib/gregex.c:2964
+#: glib/gregex.c:2983
 msgid "unfinished symbolic reference"
 msgstr "незаконченная символьная ссылка"
 
-#: glib/gregex.c:2971
+#: glib/gregex.c:2990
 msgid "zero-length symbolic reference"
 msgstr "символьная ссылка нулевой длины"
 
-#: glib/gregex.c:2982
+#: glib/gregex.c:3001
 msgid "digit expected"
 msgstr "ожидалась цифра"
 
-#: glib/gregex.c:3000
+#: glib/gregex.c:3019
 msgid "illegal symbolic reference"
 msgstr "недопустимая символьная ссылка"
 
-#: glib/gregex.c:3063
+#: glib/gregex.c:3082
 msgid "stray final “\\”"
 msgstr "лишний «\\» в конце"
 
-#: glib/gregex.c:3067
+#: glib/gregex.c:3086
 msgid "unknown escape sequence"
 msgstr "неизвестная экранирующая последовательность"
 
-#: glib/gregex.c:3077
+#: glib/gregex.c:3096
 #, c-format
 msgid "Error while parsing replacement text “%s” at char %lu: %s"
 msgstr ""
@@ -6389,6 +6402,12 @@ msgstr "%.1f ПБ"
 msgid "%.1f EB"
 msgstr "%.1f ЭБ"
 
+#~ msgid ""
+#~ "METHOD_RETURN message: REPLY_SERIAL header field is missing or invalid"
+#~ msgstr ""
+#~ "Сообщение METHOD_RETURN: отсутствует или недопустимо поле заголовка "
+#~ "REPLY_SERIAL"
+
 #, c-format
 #~ msgid "Could not allocate %lu byte to read file “%s”"
 #~ msgid_plural "Could not allocate %lu bytes to read file “%s”"
diff --git a/tests/black.sh b/tests/black.sh
new file mode 100755 (executable)
index 0000000..37473be
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+# Copyright 2020 Frederic Martinsons
+# Copyright 2020 Endless OS Foundation, LLC
+# Copyright 2024 Collabora Ltd.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eu
+
+if [ -z "${G_TEST_SRCDIR-}" ]; then
+    me="$(readlink -f "$0")"
+    G_TEST_SRCDIR="${me%/*}"
+fi
+
+export TEST_NAME=black
+export TEST_REQUIRES_TOOLS="black git"
+
+run_lint () {
+    # shellcheck disable=SC2046
+    black --diff --check $(git ls-files '*.py')
+}
+
+# shellcheck source=tests/lint-common.sh
+. "$G_TEST_SRCDIR/lint-common.sh"
similarity index 52%
rename from .gitlab-ci/check-missing-install-tag.py
rename to tests/check-missing-install-tag.py
index ab23804..71e96eb 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-# Copyright © 2022 Collabora, Ltd.
+# Copyright © 2022-2024 Collabora, Ltd.
 #
 # SPDX-License-Identifier: LGPL-2.1-or-later
 #
@@ -18,19 +18,33 @@ from pathlib import Path
 
 def main():
     parser = argparse.ArgumentParser()
-    parser.add_argument("builddir", type=Path)
+    parser.add_argument("builddir", type=Path, nargs="?", default=".")
     args = parser.parse_args()
 
-    success = True
+    print("# TAP version 13")
+
+    count = 0
+    bad = 0
     path = args.builddir / "meson-info" / "intro-install_plan.json"
     with path.open(encoding="utf-8") as f:
         install_plan = json.load(f)
         for target in install_plan.values():
             for info in target.values():
+                count += 1
+
                 if not info["tag"]:
-                    print("Missing install_tag for", info["destination"])
-                    success = False
-    return 0 if success else 1
+                    bad += 1
+                    dest = info["destination"]
+                    print(f"not ok {bad} - Missing install_tag for {dest}")
+
+    if bad == 0:
+        print(f"ok 1 - All {count} installed files have install_tag")
+        print("1..1")
+        return 0
+    else:
+        print(f"# {bad}/{count} installed files do not have install_tag")
+        print(f"1..{bad}")
+        return 1
 
 
 if __name__ == "__main__":
diff --git a/tests/flake8.sh b/tests/flake8.sh
new file mode 100755 (executable)
index 0000000..10a0a6f
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+# Copyright 2020 Frederic Martinsons
+# Copyright 2020 Endless OS Foundation, LLC
+# Copyright 2024 Collabora Ltd.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eu
+
+if [ -z "${G_TEST_SRCDIR-}" ]; then
+    me="$(readlink -f "$0")"
+    G_TEST_SRCDIR="${me%/*}"
+fi
+
+export TEST_NAME=flake8
+export TEST_REQUIRES_TOOLS="flake8 git"
+
+run_lint () {
+    # Disable formatting warnings in flake8, as we use `black` to handle that.
+    local formatting_warnings=E101,E111,E114,E115,E116,E117,E12,E13,E2,E3,E401,E5,E70,W1,W2,W3,W5
+
+    # shellcheck disable=SC2046
+    flake8 \
+        --max-line-length=88 --ignore="$formatting_warnings" \
+        $(git ls-files '*.py')
+}
+
+# shellcheck source=tests/lint-common.sh
+. "$G_TEST_SRCDIR/lint-common.sh"
diff --git a/tests/lint-common.sh b/tests/lint-common.sh
new file mode 100644 (file)
index 0000000..1d1c3d4
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+# Copyright 2016-2018 Simon McVittie
+# Copyright 2018-2024 Collabora Ltd.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eu
+
+skip_all () {
+    echo "1..0 # SKIP $*"
+    exit 0
+}
+
+main () {
+    local need_git=
+    local tool
+
+    cd "$G_TEST_SRCDIR/.."
+    echo "TAP version 13"
+
+    # shellcheck disable=SC2046
+    for tool in ${TEST_REQUIRES_TOOLS-}; do
+        command -v "$tool" >/dev/null || skip_all "$tool not found"
+        if [ "$tool" = git ]; then
+            need_git=1
+        fi
+    done
+
+    if [ -n "${need_git-}" ] && ! test -e .git; then
+        skip_all "not a git checkout"
+    fi
+
+    echo "1..1"
+
+    if run_lint >&2; then
+        echo "ok 1"
+        exit 0
+    elif [ -n "${LINT_WARNINGS_ARE_ERRORS-}" ]; then
+        echo "not ok 1 - warnings from ${TEST_NAME-"lint tool"}"
+        exit 1
+    else
+        echo "not ok 1 # TO""DO warnings from ${TEST_NAME-"lint tool"}"
+        exit 0
+    fi
+}
+
+main
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644 (file)
index 0000000..a2822d4
--- /dev/null
@@ -0,0 +1,29 @@
+# Copyright 2024 Collabora Ltd.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+lint_scripts = [
+  'black.sh',
+  'flake8.sh',
+  'reuse.sh',
+  'shellcheck.sh',
+]
+
+if have_bash
+  foreach test_name : lint_scripts
+    test(
+      test_name, files(test_name),
+      env : common_test_env,
+      suite : 'lint',
+      protocol : 'tap',
+    )
+  endforeach
+endif
+
+test(
+  'check-missing-install-tag.py',
+  python,
+  args : ['-B', files('check-missing-install-tag.py')],
+  env : common_test_env,
+  suite : 'lint',
+  protocol : 'tap',
+)
\ No newline at end of file
similarity index 62%
rename from .gitlab-ci/run-reuse.sh
rename to tests/reuse.sh
index 6d0a44c..e9d9e06 100755 (executable)
@@ -1,23 +1,43 @@
-#!/bin/bash
+#!/usr/bin/env bash
 #
 # Copyright 2022 Endless OS Foundation, LLC
+# Copyright 2024 Collabora Ltd.
 #
 # SPDX-License-Identifier: LGPL-2.1-or-later
 #
 # Original author: Philip Withnall
 
-set -e
+set -eu
+
+if [ -z "${G_TEST_SRCDIR-}" ]; then
+    me="$(readlink -f "$0")"
+    G_TEST_SRCDIR="${me%/*}"
+fi
+
+skip_all () {
+    echo "1..0 # SKIP $*"
+    exit 0
+}
+
+cd "$G_TEST_SRCDIR/.."
+echo "TAP version 13"
+
+command -v git >/dev/null || skip_all "git not found"
+command -v reuse >/dev/null || skip_all "reuse not found"
+test -e .git || skip_all "not a git checkout"
+
+echo "1..1"
 
 # We need to make sure the submodules are up to date, or `reuse lint` will fail
 # when it tries to run `git status` internally
-git submodule update --init
+git submodule update --init >&2
 
 # Run `reuse lint` on the code base and see if the number of files without
 # suitable copyright/licensing information has increased from a baseline
 # FIXME: Eventually this script can check whether *any* files are missing
 # information. But for now, let’s slowly improve the baseline.
-files_without_copyright_information_max=407
-files_without_license_information_max=559
+files_without_copyright_information_max=343
+files_without_license_information_max=414
 
 # The || true is because `reuse lint` will exit with status 1 if the project is not compliant
 # FIXME: Once https://github.com/fsfe/reuse-tool/issues/512 or
@@ -26,9 +46,9 @@ files_without_license_information_max=559
 # output rather than the current human-readable output.
 lint_output="$(reuse lint || true)"
 
-files_with_copyright_information="$(echo "${lint_output}" | awk '/^\* Files with copyright information: / { print $6 }')"
-files_with_license_information="$(echo "${lint_output}" | awk '/^\* Files with license information: / { print $6 }')"
-total_files="$(echo "${lint_output}" | awk '/^\* Files with copyright information: / { print $8 }')"
+files_with_copyright_information="$(echo "${lint_output}" | awk '/^\* [fF]iles with copyright information: / { print $6 }')"
+files_with_license_information="$(echo "${lint_output}" | awk '/^\* [fF]iles with license information: / { print $6 }')"
+total_files="$(echo "${lint_output}" | awk '/^\* [fF]iles with copyright information: / { print $8 }')"
 error=0
 
 files_without_copyright_information="$(( total_files - files_with_copyright_information ))"
@@ -36,27 +56,36 @@ files_without_license_information="$(( total_files - files_with_license_informat
 
 if [ "${files_without_copyright_information}" -gt "${files_without_copyright_information_max}" ] || \
    [ "${files_without_license_information}" -gt "${files_without_license_information_max}" ]; then
-  echo "${lint_output}"
+  echo "${lint_output}" >&2
 fi
 
 if [ "${files_without_copyright_information}" -gt "${files_without_copyright_information_max}" ]; then
-  echo ""
-  echo "Error: New files added without REUSE-compliant copyright information"
-  echo "Please make sure that all files added in this branch/merge request have correct copyright information"
+  echo "" >&2
+  echo "Error: New files added without REUSE-compliant copyright information" >&2
+  echo "Please make sure that all files added in this branch/merge request have correct copyright information" >&2
   error=1
 fi
 
 if [ "${files_without_license_information}" -gt "${files_without_license_information_max}" ]; then
-  echo ""
-  echo "Error: New files added without REUSE-compliant licensing information"
-  echo "Please make sure that all files added in this branch/merge request have correct license information"
+  echo "" >&2
+  echo "Error: New files added without REUSE-compliant licensing information" >&2
+  echo "Please make sure that all files added in this branch/merge request have correct license information" >&2
   error=1
 fi
 
 if [ "${error}" -eq "1" ]; then
-  echo ""
-  echo "See https://reuse.software/tutorial/#step-2 for information on how to add REUSE information"
-  echo "Also see https://gitlab.gnome.org/GNOME/glib/-/issues/1415"
+  echo "" >&2
+  echo "See https://reuse.software/tutorial/#step-2 for information on how to add REUSE information" >&2
+  echo "Also see https://gitlab.gnome.org/GNOME/glib/-/issues/1415" >&2
 fi
 
-exit "${error}"
\ No newline at end of file
+if [ "${error}" -eq 0 ]; then
+    echo "ok 1"
+    exit 0
+elif [ -n "${LINT_WARNINGS_ARE_ERRORS-}" ]; then
+    echo "not ok 1 - warnings from reuse"
+    exit "${error}"
+else
+    echo "not ok 1 # TO""DO warnings from reuse"
+    exit 0
+fi
diff --git a/tests/shellcheck.sh b/tests/shellcheck.sh
new file mode 100755 (executable)
index 0000000..4f5ce62
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+# Copyright 2020 Frederic Martinsons
+# Copyright 2024 Collabora Ltd.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eu
+
+if [ -z "${G_TEST_SRCDIR-}" ]; then
+    me="$(readlink -f "$0")"
+    G_TEST_SRCDIR="${me%/*}"
+fi
+
+export TEST_NAME=shellcheck
+export TEST_REQUIRES_TOOLS="git shellcheck"
+
+run_lint () {
+    # Ignoring third-party directories that we don't want to parse
+    # shellcheck disable=SC2046
+    shellcheck $(git ls-files '*.sh' | grep -Ev "glib/libcharset|glib/dirent")
+}
+
+# shellcheck source=tests/lint-common.sh
+. "$G_TEST_SRCDIR/lint-common.sh"