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}"
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: []
# 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:
extends:
- .build-linux
- .only-default
+ - .with-git
image: $DEBIAN_IMAGE
stage: build
needs: []
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
muslc-alpine-x86_64:
extends:
- .build-linux
- - .only-schedules
+ - .only-schedules-or-manual
+ - .with-git
image: $ALPINE_IMAGE
stage: build
needs: []
installed-tests:
extends:
- .build-linux
- - .only-schedules
+ - .only-schedules-or-manual
image: $FEDORA_IMAGE
stage: build
needs: []
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
-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:
valgrind:
extends:
- .build-linux
- - .only-schedules
+ - .only-schedules-or-manual
+ - .with-git
image: $FEDORA_IMAGE
stage: analysis
needs: []
- _build/gobject/libgobject-2.0-0.dll
msys2-mingw32:
- extends: .only-default
+ extends: .only-default-and-merges
stage: build
tags:
- win32-ps
paths:
- _build/meson-logs
- _coverage/
+ - _reference/
msys2-clang64:
- extends: .only-schedules
+ extends: .only-schedules-or-manual
stage: build
tags:
- win32-ps
- _build/meson-logs
freebsd-13-x86_64:
- extends: .only-origin
+ extends: .only-origin-or-manual
stage: build
tags:
- freebsd-13
- "_build/meson-logs"
macos-x86_64:
- extends: .only-origin
+ extends: .only-origin-or-manual
stage: build
tags:
- macosintel
scan-build:
extends:
- .build-linux
- - .only-schedules
+ - .only-schedules-or-manual
image: $FEDORA_IMAGE
stage: analysis
needs: []
.coverity:
extends:
- .build-linux
- - .only-schedules
+ - .only-schedules-or-manual-in-default-branch
image: $COVERITY_IMAGE
stage: analysis
needs: []
- 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:
+++ /dev/null
-#!/bin/bash
-
-set -e
-
-# shellcheck disable=SC2046
-black --diff --check $(git ls-files '*.py')
+++ /dev/null
-#!/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')
+++ /dev/null
-#!/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")
set -ex
-./.gitlab-ci/check-missing-install-tag.py _build
-
meson test -v \
-C _build \
--timeout-multiplier "${MESON_TEST_TIMEOUT_MULTIPLIER}" \
:: 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
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 \
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
--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
--- /dev/null
+#!/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
# 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
+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
==============================================
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
SEE ALSO
--------
-`gdbus(1) <man:gdbus(1)>`_
\ No newline at end of file
+`gdbus(1) <man:gdbus(1)>`_
--- /dev/null
+# 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 = []
--- /dev/null
+# 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 = []
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",
"error.md",
"pollable-utils.md",
- "unix-mounts.md",
"dbus-error.md",
"dbus-introspection.md",
'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)
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
## 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) |
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
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]
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 = [
]
if get_option('documentation') and enable_gir
+ expand_content_files = [
+ 'migrating-gi.md',
+ ]
+
girepository_toml = configure_file(
input: 'girepository.toml.in',
output: 'girepository.toml',
'@INPUT1@',
],
build_by_default: true,
+ depend_files: expand_content_files,
+ install: true,
+ install_dir: docs_dir,
+ install_tag: 'doc',
)
endif
--- /dev/null
+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`.
+
+++ /dev/null
-// 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/' ],
-];
--- /dev/null
+# 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 = []
--- /dev/null
+# 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 = []
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",
"checked-math.md",
"threads.md",
"spawn.md",
- "unix.md",
- "windows.md",
"random.md",
"numerical.md",
"markup.md",
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.
'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',
]
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
## 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 -->
## 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]
+++ /dev/null
-// 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/' ],
-];
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
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",
+++ /dev/null
-// 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/' ],
-];
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",
+++ /dev/null
-// 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/' ],
-];
'--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'
// 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/' ],
];
#include "gio/gnetworking.h"
#include "../gio/gthreadedresolver.h"
+#include "../gio/gthreadedresolver-private.h"
static void
test_for_rrtype (const guint8 *data,
* - [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
{
GApplicationFlags flags;
gchar *id;
+ gchar *version;
gchar *resource_path;
GActionGroup *actions;
{
PROP_NONE,
PROP_APPLICATION_ID,
+ PROP_VERSION,
PROP_FLAGS,
PROP_RESOURCE_BASE_PATH,
PROP_IS_REGISTERED,
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;
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)
{
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;
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);
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)
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;
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));
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);
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.
}
/**
+ * 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
*
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
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"
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")
# ----------------------------------------------------------------------------------------------------
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"
)
import argparse
import os
import sys
+from contextlib import contextmanager
from . import config
from . import dbustypes
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:
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")
)
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
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,
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,
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,
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,
*
* 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"
*
* 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
**/
{
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);
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.
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;
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
if (elf_kind (elf) != ELF_K_ELF)
{
+ elf_end (elf);
g_close (*fd, NULL);
*fd = -1;
return NULL;
*
* 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
* 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.
*
--- /dev/null
+/* 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__ */
#include "glib/glib-private.h"
#include "gthreadedresolver.h"
+#include "gthreadedresolver-private.h"
#include "gnetworkingprivate.h"
#include "gcancellable.h"
}
g_clear_pointer (&addresses, g_resolver_free_addresses);
+ g_clear_error (&local_error);
}
break;
case LOOKUP_BY_ADDRESS:
}
g_clear_pointer (&name, g_free);
+ g_clear_error (&local_error);
}
break;
case LOOKUP_RECORDS:
}
g_clear_pointer (&records, free_records);
+ g_clear_error (&local_error);
}
break;
default:
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__ */
* 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].
*
* 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"
*
* 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 {
#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[])
{
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 ();
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)
*/
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,
{
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,
*
* 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 {
platform_deps = []
internal_deps = []
-appinfo_sources = []
contenttype_sources = []
portal_sources = []
unix_sources = []
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
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')
)
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'),
gio_sources += files ('../glib/gtrace.c')
endif
-gio_sources += appinfo_sources
gio_sources += contenttype_sources
gio_sources += gdbus_daemon_sources
gio_sources += unix_sources
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.
"#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)
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}
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}
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 = """
xml_file1.name,
xml_file2.name,
"--output",
- "/dev/stdout",
+ "-",
header_or_body,
)
self.assertEqual("", result1.err)
xml_file2.name,
xml_file1.name,
"--output",
- "/dev/stdout",
+ "-",
header_or_body,
)
self.assertEqual("", result2.err)
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."""
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."""
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."""
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",
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
result = self.runCodegenWithInterface(
"",
"--output",
- "/dev/stdout",
+ "-",
"--header",
"--glib-max-allowed",
"2.63",
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."""
self.runCodegenWithInterface(
"",
"--output",
- "/dev/stdout",
+ "-",
"--body",
"--glib-max-allowed",
"2.62",
"2.64",
)
- @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_dbus_types(self):
bad_types = [
"{vs}", # Bad dictionary key type
</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"
</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.
# 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)
result = self.runCodegenWithInterface(
interface_xml,
"--output",
- "/dev/stdout",
+ "-",
"--header",
"--glib-min-required",
"2.32",
result = self.runCodegenWithInterface(
interface_xml,
"--output",
- "/dev/stdout",
+ "-",
"--header",
"--glib-min-required",
"2.64",
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.
# 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)
result = self.runCodegenWithInterface(
interface_xml,
"--output",
- "/dev/stdout",
+ "-",
"--header",
"--glib-min-required",
"2.32",
result = self.runCodegenWithInterface(
interface_xml,
"--output",
- "/dev/stdout",
+ "-",
"--header",
"--glib-min-required",
"2.64",
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 = """
</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)
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"""
</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)
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"""
</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)
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 = """
</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)
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():
</node>"""
result = self.runCodegenWithInterface(
- interface_xml, "--output", "/dev/stdout", "--body"
+ interface_xml, "--output", "-", "--body"
)
stripped_out = result.out.strip()
self.assertFalse(result.err)
1,
)
- @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_generate_signals_marshallers_multiple_args(self):
"""Test that signals marshallers are generated"""
generated_args = [
</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)
)
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 = """
</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)
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():
</node>"""
result = self.runCodegenWithInterface(
- interface_xml, "--output", "/dev/stdout", "--body"
+ interface_xml, "--output", "-", "--body"
)
stripped_out = result.out.strip()
self.assertFalse(result.err)
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():
</node>"""
result = self.runCodegenWithInterface(
- interface_xml, "--output", "/dev/stdout", "--body"
+ interface_xml, "--output", "-", "--body"
)
stripped_out = result.out.strip()
self.assertFalse(result.err)
)
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 = [
</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)
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 = [
</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)
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 = """
</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)
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"""
* 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,
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);
GDBusConnection *c;
GObject *app;
gchar *id;
+ gchar *version;
GApplicationFlags flags;
gboolean registered;
guint timeout;
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", ®istered,
"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);
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
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);
import subprocess
import fcntl
import os
-import time
import taptestrunner
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)
import subprocess
import fcntl
import os
-import time
import taptestrunner
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
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)
import subprocess
import fcntl
import os
-import time
import taptestrunner
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()
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()
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)
import subprocess
import fcntl
import os
-import time
import taptestrunner
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()
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'''
#define GIO_COMPILATION
#include "gthreadedresolver.h"
+#include "gthreadedresolver-private.h"
#undef GIO_COMPILATION
#ifdef HAVE_DN_COMP
// 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)
-/* -*- 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;
gboolean show_version = FALSE;
static gboolean
-write_out_typelib (gchar *prefix,
- GITypelib *typelib)
+write_out_typelib (gchar *prefix,
+ GITypelib *typelib)
{
FILE *file;
gsize written;
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);
{
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;
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, "");
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)
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;
}
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;
}
--- /dev/null
+# 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
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+# 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,
+)
}
/**
- * 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
*
*
* 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
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
#include <glib.h>
#include <glib-object.h>
+#include "girepository-private.h"
#include "gitypes.h"
G_BEGIN_DECLS
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
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
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);
}
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)
{ 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 },
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
* 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);
}
*/
void
gi_info_init (GIRealInfo *info,
- GIInfoType type,
+ GType type,
GIRepository *repository,
GIBaseInfo *container,
GITypelib *typelib,
{
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;
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,
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);
{
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)
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);
}
* 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
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);
+ }
}
/**
*
* 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.
*
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:
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:
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);
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
+++ /dev/null
-/* -*- 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;
-}
+++ /dev/null
-/* -*- 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
*
* 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
*
* 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
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);
}
/**
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);
}
/**
*
* 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
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);
}
/**
* 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
*/
void
gi_type_tag_extract_ffi_return_value (GITypeTag return_tag,
- GIInfoType interface_type,
+ GType interface_type,
GIFFIReturnValue *ffi_value,
GIArgument *arg)
{
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;
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);
}
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);
}
/**
+ 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);
}
/**
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
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;
{
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:
{
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:
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;
}
if (blob->wraps_vfunc)
flags = flags | GI_FUNCTION_WRAPS_VFUNC;
- if (blob->throws)
- flags = flags | GI_FUNCTION_THROWS;
-
return flags;
}
+ (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);
}
/**
+ 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);
}
/**
+ 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);
}
/**
+ 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);
}
/**
+ 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);
}
/**
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);
}
/**
+ 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);
}
/**
+ 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);
}
/**
* 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.
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)
{
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);
}
/**
+ 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);
}
/**
+ 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);
}
/**
* 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.
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)
{
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);
}
/**
+ 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);
}
/**
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
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);
* 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
*/
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)
*
* 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);
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
#pragma once
-#include <ffi.h>
#include <glib.h>
#define __GIREPOSITORY_H_INSIDE__
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
{
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;
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);
gpointer class_data);
void gi_info_init (GIRealInfo *info,
- GIInfoType type,
+ GType type,
GIRepository *repository,
GIBaseInfo *container,
GITypelib *typelib,
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);
#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
*/
#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[];
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 */
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
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
{
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));
}
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};
}
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 *
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,
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)
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
{
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`.
* 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);
if (deps == NULL)
deps = g_strsplit ("", "|", 0);
+ if (n_dependencies_out != NULL)
+ *n_dependencies_out = g_strv_length (deps);
+
return deps;
}
/**
* 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_.
* 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 */
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);
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`
*
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);
/**
* 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
*
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
/**
* 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
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);
/**
* 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
*
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);
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);
}
/**
* 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].
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);
* 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,
* 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
*
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);
}
/**
* 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]
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)
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;
/**
* 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
{
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)
{
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);
/**
* 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;
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
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);
/**
* 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
* 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
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);
}
/* 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
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);
/**
* 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
{
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;
/**
* 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
*/
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)
{
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;
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)
{
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,
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);
"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)
"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;
/**
* 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
{
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`
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:
#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:
/* 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,
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,
#include <unistd.h>
#endif
#include "girffi.h"
+#include "gibaseinfo-private.h"
#include "girepository.h"
#include "girepository-private.h"
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:
default:
g_assert_not_reached ();
}
+
+ gi_base_info_clear (&arg_type);
+ gi_base_info_clear (&arg_info);
}
arg_types[n_invoke_args] = NULL;
* 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
*/
}
/**
- * gi_function_invoker_destroy:
+ * gi_function_invoker_clear:
* @invoker: (transfer none): A #GIFunctionInvoker
*
* Release all resources allocated for the internals of @invoker.
* Since: 2.80
*/
void
-gi_function_invoker_destroy (GIFunctionInvoker *invoker)
+gi_function_invoker_clear (GIFunctionInvoker *invoker)
{
g_free (invoker->cif.arg_types);
}
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);
GError **error);
GI_AVAILABLE_IN_ALL
-void gi_function_invoker_destroy (GIFunctionInvoker *invoker);
+void gi_function_invoker_clear (GIFunctionInvoker *invoker);
GI_AVAILABLE_IN_ALL
#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,
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;
gi_ir_module_build_typelib (GIIrModule *module)
{
GError *error = NULL;
+ GBytes *bytes = NULL;
GITypelib *typelib;
size_t length;
size_t i;
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",
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
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
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
int8_t closure;
int8_t destroy;
- GIIrNodeType *type;
+ GIIrNodeType *type; /* (owned) */
};
struct _GIIrNodeProperty
uint8_t deprecated : 1;
- char *name;
+ char *name; /* (owned) */
uint8_t readable : 1;
uint8_t writable : 1;
uint8_t construct : 1;
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
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
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
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
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
uint8_t deprecated : 1;
- GIIrNodeType *type;
+ GIIrNodeType *type; /* (owned) */
- char *value;
+ char *value; /* (owned) */
};
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
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
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
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) */
};
#include "config.h"
#include "girnode-private.h"
-
+#include "girepository-private.h"
#include "gitypelib-internal.h"
#include <stdio.h>
g_free (type->giinterface);
g_strfreev (type->errors);
-
+ g_free (type->unparsed);
}
break;
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);
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",
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 */
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
}
if (have_error)
- field->offset = -1;
+ {
+ field->offset = 0;
+ field->offset_state = GI_IR_OFFSETS_FAILED;
+ }
}
else if (member->type == GI_IR_NODE_CALLBACK)
{
GIIrNodeField *field;
ParseState target_state;
gboolean introspectable;
+ guint64 parsed_bits;
switch (ctx->state)
{
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)
{
}
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;
const char *throws;
GIIrNodeInterface *iface;
GIIrNodeVFunc *vfunc;
+ guint64 parsed_offset;
if (!(strcmp (element_name, "virtual-method") == 0 &&
(ctx->state == STATE_CLASS ||
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);
{
const char *type;
const char *offset;
+ guint64 parsed_offset;
+
if (!(strcmp (element_name, "discriminator") == 0 &&
ctx->state == STATE_UNION))
return FALSE;
((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;
}
#include "girwriter-private.h"
+#include "gibaseinfo-private.h"
#include "girepository.h"
+#include "girepository-private.h"
#include "gitypelib-internal.h"
#include <errno.h>
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'",
}
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);
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);
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");
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);
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");
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;
" 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++)
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);
}
xml_end_element (xml, "repository");
xml_free (xml);
+
+ g_clear_object (&repository);
}
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));
}
/**
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;
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);
}
/**
*
* 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.
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
{
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
#include <gmodule.h>
#include "girepository.h"
+#include "girepository-private.h"
G_BEGIN_DECLS
* Since: 2.80
*/
typedef enum {
+ /* The values here must be kept in sync with GIInfoType */
BLOB_TYPE_INVALID,
BLOB_TYPE_FUNCTION,
BLOB_TYPE_CALLBACK,
BLOB_TYPE_UNION
} GITypelibBlobType;
+GIInfoType gi_typelib_blob_type_to_info_type (GITypelibBlobType blob_type);
+
#if defined (G_CAN_INLINE) && defined (G_ALWAYS_INLINE)
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,
* 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;
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
* 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__
#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);
{
GModule *module;
- module = load_one_shared_library (shlibs[i]);
+ module = load_one_shared_library (typelib, shlibs[i]);
if (module == NULL)
{
}
/**
- * 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);
}
/**
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,
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);
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);
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 */
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
* @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.
*
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;
/**
* @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.
*
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
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);
}
/**
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);
}
/**
/**
* 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;
}
/**
*
* 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
*/
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);
}
+ 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;
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);
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;
}
if (blob->must_not_be_implemented)
flags = flags | GI_VFUNC_MUST_NOT_OVERRIDE;
- if (blob->throws)
- flags = flags | GI_VFUNC_THROWS;
-
return flags;
}
--- /dev/null
+/* 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;
+}
--- /dev/null
+# 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,
+)
--- /dev/null
+
+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,
+)
+
girepo_headers = files(
'giarginfo.h',
'gibaseinfo.h',
- 'giboxedinfo.h',
'gicallableinfo.h',
'gicallbackinfo.h',
'giconstantinfo.h',
'gdump.c',
'giarginfo.c',
'gibaseinfo.c',
- 'giboxedinfo.c',
'gicallableinfo.c',
'gicallbackinfo.c',
'giconstantinfo.c',
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',
)
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
--- /dev/null
+/*
+ * 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 ();
+}
-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
)
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', []),
--- /dev/null
+/*
+ * 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 ();
+}
--- /dev/null
+/*
+ * 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 ();
+}
#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);
g_assert_cmpstr (search_paths[1], ==, expected_path);
g_clear_pointer (&expected_path, g_free);
#endif
+
+ g_clear_object (&repository);
}
static 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);
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);
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
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 ();
}
/*
+ * 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);
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");
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");
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");
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 */
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 ();
}
--- /dev/null
+/*
+ * 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 ();
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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 ();
+}
--- /dev/null
+/*
+ * 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 ();
+}
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)
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)
{
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);
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
#include "gslice.h"
#include "gdatasetprivate.h"
+#include "gutilsprivate.h"
#include "ghash.h"
#include "gquark.h"
#include "gstrfuncs.h"
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;
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)
{
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 */
{
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
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);
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);
}
}
* 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
GQuark *keys,
gsize n_keys)
{
- g_return_if_fail (n_keys <= 16);
-
g_data_remove_internal (datalist, keys, n_keys);
}
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.
{
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);
}
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;
}
}
}
{
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;
}
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;
#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
#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,
g_uri_get_default_scheme_port,
g_set_prgname_once,
+
+ g_datalist_id_update_atomic,
};
return &table;
#include <glib.h>
#include "gwakeup.h"
#include "gstdioprivate.h"
+#include "gdatasetprivate.h"
/* gcc defines __SANITIZE_ADDRESS__, clang sets the address_sanitizer
* feature flag.
/* 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
#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__ */
/* 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));
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
+}
/**
* 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
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
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;
}
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
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. */
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
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;
{
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;
/* 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;
/**
* 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
**/
/**
* 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
**/
/**
* 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
**/
/**
* 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.
*
* 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,
/**
* 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
**/
/**
* 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
**/
/**
* 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
**/
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,
/**
* 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
**/
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
/* 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
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
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)
{
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)
{
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)
{
{
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);
}
/*
*
* 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] = {
* 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
*/
/**
* 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,
/**
* 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
/**
* 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,
* @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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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
/**
* 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,
/**
* 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
* 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,
/**
* 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,
/**
* 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,
/**
* 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
*/
/**
* 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.
* 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
*/
/**
* 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)
* @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)
/* 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.
*/
* @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
* @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,
/**
* 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,
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);
/**
* 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,
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);
* 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
*/
/**
* 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)
*
* 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)
* 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)
* 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)
* 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)
* 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)
/**
* 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)
*
* 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.
*
* 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,
* 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,
/**
* g_strcasecmp:
- * @s1: a string
- * @s2: a 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,
/**
* g_strncasecmp:
- * @s1: a string
- * @s2: a 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,
/**
* 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.
* 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
*/
*
* 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
*/
*
* 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)
* 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,
*
* 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)
*
* 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)
* 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,
/**
* 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
**/
* 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)
/**
* 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)
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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
*/
/**
* 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
*/
/**
* 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
*/
/**
* 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):
+ * a 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
* 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.
*
* 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
* 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
**/
/**
* 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
*/
/**
* 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
/**
* 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)
* 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
*/
* 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
*/
/**
* 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.
*
* @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
*/
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)
{
}
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);
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);
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);
}
#include <glib/gtypes.h>
-#if defined(G_PLATFORM_WIN32) || defined(__GI_SCANNER__)
+#ifdef G_PLATFORM_WIN32
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
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"
G_END_DECLS
-#endif /* G_PLATFORM_WIN32 || __GI_SCANNER__ */
+#endif /* G_PLATFORM_WIN32 */
#endif /* __G_WIN32_H__ */
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',
'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',
'gtimer.c',
'gtimezone.c',
'gtrace.c',
- 'gtrace-private.h',
'gtranslit.c',
'gtrashstack.c',
'gtree.c',
'gunicollate.c',
'gunidecomp.c',
'guri.c',
- 'guriprivate.h',
'gutils.c',
- 'gutilsprivate.h',
'guuid.c',
'gvariant.c',
'gvariant-core.c',
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
#include <glib.h>
#include <stdlib.h>
+#include "glib/glib-private.h"
+
static void
test_quark_basic (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++;
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[])
{
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 ();
}
/* Cleaning left over files */
g_remove ("maptest");
-
- g_test_message ("test_private: ok");
}
static void
/* Cleaning left over files */
g_remove ("mapchild");
g_remove ("maptest");
-
- g_test_message ("test_child_private: ok");
}
int
}
#endif
+ g_test_init (&argc, &argv, NULL);
local_argv = argv;
if (argc > 1)
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);
{
/* 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 ());
}
}
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");
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);
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);
#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;
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);
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);
{
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);
#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.
*
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;
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 *
#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
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");
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);
}
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;
}
/**
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. */
* 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)
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;
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,
* 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
*/
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);
+ }
}
/**
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;
}
/**
*/
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);
}
g_object_interface_install_property (iface, pspec);
g_test_assert_expected_messages ();
- g_param_spec_unref (pspec);
continue;
}
{
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++)
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':
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++)
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);
}
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
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);
}
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)
test_references (void)
{
GObject *object;
+ GWeakRef weak_ref;
/* Test basic weak reference operation */
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
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);
+++ /dev/null
-
-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,
-)
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 : [
'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.
#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]
#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>' ],
]
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)
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)
#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
doit()
{
char buffer[32];
- va_list args;
int r;
r = snprintf(buffer, 5, "1234567");
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)
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
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)
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)
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)
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)
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)
#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
#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
int main () {
int64_t i1 = 1;
long *i2 = &i1;
+ (void) i2;
return 1;
}''', name : 'int64_t is long')
int64_t_typedef = 'long'
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
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'
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()
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"
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 "[ბრძანება]"
#: 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"
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 ""
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"
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"
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"
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\" გარდამქმნელის გახსნა"
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 "
"მოველოდი სწორ 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)."
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 "
"შემხვდა მასივი ტიპით \"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"
"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 "
"არასწორი ბოლოიანობის მნიშვნელობა. მოველოდი 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"
"შეტყობინებაში ხელმოწერის თავსართი აღმოჩენილი არაა, მაგრამ შეტყობინების "
"სხეული %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"
"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)"
"დესკრიპტორების რაოდენობა შეტყობინებაში (%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 "
"შეტყობინების სხეულს გააჩნია ხელმოწერა \"%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-ის ჩატვირთვის შეცდომა: "
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 "საქაღალდეზე ზემოდან კოპირება შეუძლებელია"
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 "ჰოსტის არასწორი სახელი"
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 "ხატულის კოდირების მითითებული ვერსიის დამუშავება შეუძლებელია"
#. * 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"
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 "ჰოსტი მიუწვდომელია"
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 "მიუთითებელი პროქსის მოძებნის შეცდომა"
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\", გაშლა ჩავარდა"
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"
"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"
"\n"
msgstr ""
-#: gio/gresource-tool.c:556
+#: gio/gresource-tool.c:557
#, c-format
msgid ""
"Usage:\n"
"%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"
" ფაილი 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"
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 ვადა გავიდა"
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 "ფაილური სისტემის საწყისი საქაღალდე"
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\" აბსოლუტური არ გახლავთ"
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"
"მოულოდნელი შეცდომა ფუნქციაში 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\" უნიშნო რიცხვი არაა"
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”"
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"
"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"
#: 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]"
#: 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"
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"
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"
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 "
"(смещение %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)."
"Обнаружен массив длинной %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 "
"Получен массив типа «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 "
"Неправильный порядок байтов в значении. Ожидался 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"
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)"
"Количество дескрипторов файлов в сообщении (%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 "
"Тело сообщения имеет тип подписи «%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: "
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 "Не удалось обработать данную версию текстового представления значка"
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 "Узел недоступен"
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 "Неуказанный сбой поиска прокси-сервера"
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»"
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 "Превышено время ожидания ввода-вывода сокета"
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 не реализован для данной ОС"
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)"
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"
"за \\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 ""
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”"
--- /dev/null
+#!/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"
#!/usr/bin/env python3
#
-# Copyright © 2022 Collabora, Ltd.
+# Copyright © 2022-2024 Collabora, Ltd.
#
# SPDX-License-Identifier: LGPL-2.1-or-later
#
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__":
--- /dev/null
+#!/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"
--- /dev/null
+#!/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
--- /dev/null
+# 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
-#!/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
# 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 ))"
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
--- /dev/null
+#!/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"