summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2022-07-06 10:15:51 +0200
committerThomas Haller <thaller@redhat.com>2022-07-06 10:15:51 +0200
commit9d1772bd73ba53c8afd938617548b124eec42d07 (patch)
tree10be14d0ba8f2e5d3c5d07d299c50453a11ce2fa
parent1c260f5a961d6185aca74d6bde24b3a0ef1b82d3 (diff)
downloadNetworkManager-9d1772bd73ba53c8afd938617548b124eec42d07.tar.gz
Squashed 'src/c-stdaux/' changes from 99fe83cd5698..1407a1fb2754
1407a1fb2754 ci: add clang to RTD scripts 8404c4ca6d06 ci: build documentation 044c65c2e134 docs: add sphinx-based API documentation ad8449068d96 ci: use ci-c-util for macos runs 179987035687 build: adjust for v1.1.0 release 55d787178c84 build: prepare v1.1.0 release 29ca943e636a c-stdaux: avoid NULL arithmetic even in dead-code c6358e956c29 c-stdaux: avoid NULL-dereference in constant expressions 7fab258bdf6a c-stdaux: improve kerneldoc comments 434b75a796c0 build: export 'version-scripts' configuration 33d56a6aecb9 c-stdaux: encapsulate C_EXPR_ASSERT() aca7ee0ece60 ci: add macos run 0aa338b1f0de test: prefer pipe() over eventfd() for portability adda5ff3e9d9 build: export cflags via pkg-config 7a8493bebc59 api: add c_memcpy() a01615aefe48 build: prepare v1.0.0 1685fc39db3d api: provide c_memzero() 1257244f886a api: add c_memset() git-subtree-dir: src/c-stdaux git-subtree-split: 1407a1fb275494f9efc1abbef2fd19856fb1f43d
-rw-r--r--.github/workflows/ci.yml17
-rw-r--r--.readthedocs.yaml20
-rw-r--r--AUTHORS4
-rw-r--r--NEWS.md37
-rw-r--r--meson.build26
-rw-r--r--meson_options.txt7
-rw-r--r--src/c-stdaux.h583
-rw-r--r--src/docs/api.rst5
-rw-r--r--src/docs/conf.py43
-rw-r--r--src/docs/index.rst14
-rw-r--r--src/docs/requirements.txt3
-rw-r--r--src/libcstdaux.sym6
-rw-r--r--src/meson.build8
-rw-r--r--src/test-api.c3
-rw-r--r--src/test-basic.c94
15 files changed, 690 insertions, 180 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d54b120011..0b905f5683 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,11 +7,24 @@ on:
- cron: '0 0 * * *'
jobs:
- ci:
- name: CI with Default Configuration
+ ci-linux:
+ name: Linux CI
uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
with:
cabuild_ref: "v1"
+ linux: true
m32: true
matrixmode: true
valgrind: true
+ ci-macos:
+ name: MacOS CI
+ uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+ with:
+ cabuild_ref: "v1"
+ linux: false
+ macos: true
+ ci-docs:
+ name: Documentation CI
+ uses: bus1/cabuild/.github/workflows/ci-sphinx.yml@main
+ with:
+ source: "./src/docs"
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000000..5bbe6ac1fd
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,20 @@
+# Read the Docs configuration file
+
+version: 2
+
+build:
+ apt_packages:
+ - "clang"
+ os: "ubuntu-22.04"
+ tools:
+ python: "3"
+
+formats: "all"
+
+python:
+ install:
+ - requirements: "src/docs/requirements.txt"
+ system_packages: true
+
+sphinx:
+ configuration: "src/docs/conf.py"
diff --git a/AUTHORS b/AUTHORS
index 555ecb5581..4b108f1901 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -34,5 +34,9 @@ COPYRIGHT: (ordered alphabetically)
AUTHORS: (ordered alphabetically)
David Rheinsberg <david.rheinsberg@gmail.com>
+ Evgeny Vereshchagin <evvers@ya.ru>
+ Lorenzo Arena <lorenzo.arena@powersoft.com>
+ Michele Dionisio <michele.dionisio@gmail.com>
Thomas Haller <thaller@redhat.com>
Tom Gundersen <teg@jklm.no>
+ Yuri Chornoivan <yurchor@ukr.net>
diff --git a/NEWS.md b/NEWS.md
index d51af4cf60..4954a47876 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,11 +1,38 @@
# c-stdaux - Auxiliary macros and functions for the C standard library
-## CHANGES WITH 1:
+## CHANGES WITH 1.1.0:
- * Initial release of c-stdaux.
+ * Add c_memcpy() as a safe wrapper around memcpy(3) that supports
+ empty arenas as NULL pointers.
+
+ * Support building on MacOS-X.
+
+ * Rework the apidoc comments and properly document the entire API.
+
+ * Export 'version-scripts' configuration variable alongside the
+ existing 'cflags' variable. It defines whether c-stdaux was built
+ with GNU-linker version-scripts, or not. Dependent projects can
+ use this to decide whether to use version-scripts or not.
+ Additionally, the new 'version-scripts' meson-option allows
+ specifying whether to use version-scripts, auto-detect whether to
+ enable it, or disable it.
+
+ * Fix the export of `cflags` to also be exported in pkg-config, not
+ just meson subprojects.
- * TBD
+ * Avoid NULL-pointers in compile-time macros. This silences possible
+ false-positives from code sanitizers that otherwise trip over the
+ NULL pointer dereferences.
+
+ Contributions from: David Rheinsberg, Evgeny Vereshchagin
+
+ - Brno, 2022-06-22
+
+## CHANGES WITH 1.0.0:
+
+ * Initial release of c-stdaux.
- Contributions from: TBD
+ Contributions from: David Rheinsberg, Lorenzo Arena, Michele Dionisio,
+ Yuri Chornoivan
- - TBD, YYYY-MM-DD
+ - Dußlingen, 2022-05-12
diff --git a/meson.build b/meson.build
index 3054e25073..fb3217e61f 100644
--- a/meson.build
+++ b/meson.build
@@ -1,3 +1,7 @@
+#
+# Global Project Setup
+#
+
project(
'c-stdaux',
'c',
@@ -6,7 +10,7 @@ project(
],
license: 'Apache',
meson_version: '>=0.60.0',
- version: '1.0.0',
+ version: '1.1.0',
)
major = meson.project_version().split('.')[0]
project_description = 'Auxiliary macros and functions for the C standard library'
@@ -23,6 +27,7 @@ mod_pkgconfig = import('pkgconfig')
# well. Since these exports are limited to strings, we need to be careful that
# the individual entries do not contain spaces (see the assertion below).
#
+
cflags = meson.get_compiler('c').get_supported_arguments(
# Enable GNU features of our dependencies. See feature_test_macros(7).
'-D_GNU_SOURCE',
@@ -81,6 +86,25 @@ cflags = meson.get_compiler('c').get_supported_arguments(
assert(not ''.join(cflags).contains(' '), 'Malformed compiler flags.')
add_project_arguments(cflags, language: 'c')
+#
+# Version Scripts
+#
+
+use_version_scripts = get_option('version-scripts')
+if use_version_scripts == 'auto'
+ use_version_scripts = meson.get_compiler('c').has_link_argument(
+ '-Wl,--version-script=' + (meson.current_source_dir() / 'src/libcstdaux.sym')
+ ) ? 'yes' : 'no'
+endif
+
+#
+# Subdir Delegation
+#
+
subdir('src')
+#
+# Meson Subproject Configuration
+#
+
meson.override_dependency('libcstdaux-'+major, libcstdaux_dep, static: true)
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000000..82fdaf7814
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,7 @@
+option(
+ 'version-scripts',
+ choices: ['yes', 'no', 'auto'],
+ description: 'Enable GNU-version-scripts for linking',
+ type: 'combo',
+ value: 'auto',
+)
diff --git a/src/c-stdaux.h b/src/c-stdaux.h
index 6d43027c03..6eb0a22d03 100644
--- a/src/c-stdaux.h
+++ b/src/c-stdaux.h
@@ -1,12 +1,23 @@
#pragma once
/*
- * Auxiliary macros and functions for the C standard library
+ * c-stdaux: Auxiliary macros and functions for the C standard library
*
- * The `c-stdaux.h` header contains a collection of auxiliary macros and helper
- * functions around the functionality provided by the different C standard
- * library implementations, as well as other specifications implemented by
- * them.
+ * Main public header of the c-stdaux library. All includes of this header are
+ * part of the API!
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * DOC:
+ *
+ * The ``c-stdaux.h`` header contains a collection of auxiliary macros and
+ * helper functions around the functionality provided by the different C
+ * standard library implementations, as well as other specifications
+ * implemented by them.
*
* Most of the helpers provided here provide aliases for common library and
* compiler features. Furthermore, several helpers simply provide other calling
@@ -16,17 +27,27 @@
*
* The namespace used by this project is:
*
- * * `c_*` for all common C symbols or definitions that behave like proper C
+ * - ``c_*`` for all common C symbols or definitions that behave like proper C
* entities (e.g., macros that protect against double-evaluation would use
- * lower-case names)
+ * lower-case names).
*
- * * `C_*` for all constants, as well as macros that may not be safe against
+ * - ``C_*`` for all constants, as well as macros that may not be safe against
* double evaluation.
+ *
+ * - ``c_internal_*`` and ``C_INTERNAL_*`` for all internal symbols that
+ * should not be invoked by the caller and are not part of the API
+ * guarantees.
*/
+/**/
-#ifdef __cplusplus
-extern "C" {
-#endif
+/**
+ * DOC: Guaranteed Includes
+ *
+ * The ``c-stdaux.h`` header includes a set of C Standard Library headers as
+ * well as UNIX headers. All those includes are guaranteed and part of the API.
+ * See the actual header for a comprehensive list.
+ */
+/**/
#include <assert.h>
#include <dirent.h>
@@ -48,44 +69,152 @@ extern "C" {
#include <time.h>
#include <unistd.h>
-/*
- * Shortcuts for gcc attributes. See GCC manual for details. They're 1-to-1
- * mappings to the GCC equivalents. No additional magic here. They are
- * supported by other compilers as well.
+/**
+ * DOC: Compiler Attributes
+ *
+ * The GCC compiler uses the ``__attribute__((__xyz__()))`` syntax to annotate
+ * language entities with special attributes. Aliases are provided by this
+ * header which map one-to-one to the respective compiler attributes.
+ *
+ * These attributes are not supported by all compilers, but are always provided
+ * by this header. They are pre-processor macros and do not affect the
+ * compilation, unless used. Note that most compilers support these, not just
+ * GCC.
+ */
+/**/
+
+/**
+ * _c_cleanup_() - Cleanup attribute
+ * @_x: Cleanup function to use
+ *
+ * Alias for ``__attribute__((__cleanup__(_x)))``.
*/
#define _c_cleanup_(_x) __attribute__((__cleanup__(_x)))
+
+/**
+ * _c_const_() - Const attribute
+ *
+ * Alias for ``__attribute__((__const__))``.
+ */
#define _c_const_ __attribute__((__const__))
+
+/**
+ * _c_deprecated_() - Deprecated attribute
+ *
+ * Alias for ``__attribute__((__deprecated__))``.
+ */
#define _c_deprecated_ __attribute__((__deprecated__))
+
+/**
+ * _c_hidden_() - Hidden attribute
+ *
+ * Alias for ``__attribute__((__visibility__("hidden")))``.
+ */
#define _c_hidden_ __attribute__((__visibility__("hidden")))
-#define _c_likely_(_x) (__builtin_expect(!!(_x), 1))
+
+/**
+ * _c_packed_() - Packed attribute
+ *
+ * Alias for ``__attribute__((__packed__))``.
+ */
#define _c_packed_ __attribute__((__packed__))
+
+/**
+ * _c_printf_() - Printf attribute
+ * @_a: Format expression argument index
+ * @_b: First format-parameter argument index
+ *
+ * Alias for ``__attribute__((__format__(printf, _a, _b)))``.
+ */
#define _c_printf_(_a, _b) __attribute__((__format__(printf, _a, _b)))
+
+/**
+ * _c_public_() - Public attribute
+ *
+ * Alias for ``__attribute__((__visibility__("default")))``.
+ */
#define _c_public_ __attribute__((__visibility__("default")))
+
+/**
+ * _c_pure_() - Pure attribute
+ *
+ * Alias for ``__attribute__((__pure__))``.
+ */
#define _c_pure_ __attribute__((__pure__))
+
+/**
+ * _c_sentinel_() - Sentinel attribute
+ *
+ * Alias for ``__attribute__((__sentinel__))``.
+ */
#define _c_sentinel_ __attribute__((__sentinel__))
-#define _c_unlikely_(_x) (__builtin_expect(!!(_x), 0))
+
+/**
+ * _c_unused_() - Unused attribute
+ *
+ * Alias for ``__attribute__((__unused__))``.
+ */
#define _c_unused_ __attribute__((__unused__))
/**
- * C_EXPR_ASSERT() - create expression with assertion
- * @_expr: expression to evaluate to
- * @_assertion: arbitrary assertion
- * @_message: message associated with the assertion
+ * DOC: Compiler Intrinsics
*
- * This macro simply evaluates to @_expr. That is, it can be used in any
- * context that expects an expression like @_expr. Additionally, it takes an
- * assertion as @_assertion and evaluates it through _Static_assert(), using
- * @_message as debug message.
+ * Aliases for common compiler extensions and intrinsics are provided similar
+ * to the compiler attributes. They are pure preprocessor aliases and do not
+ * affect compilation unless used.
+ */
+/**/
+
+/**
+ * _c_likely_() - Likely attribute
+ * @_x: Expression to evaluate
+ *
+ * Alias for ``__builtin_expect(!!(_x), 1)``.
+ *
+ * Return: The expression ``_x`` is evaluated and returned.
+ */
+#define _c_likely_(_x) (__builtin_expect(!!(_x), 1))
+
+/**
+ * _c_unlikely_() - Unlikely attribute
+ * @_x: Expression to evaluate
*
- * The _Static_assert() builtin of C11 is defined as statement and thus cannot
- * be used in expressions. This macro circumvents this restriction.
+ * Alias for ``__builtin_expect(!!(_x), 0)``.
*
- * Return: Evaluates to @_expr.
+ * Return: The expression ``_x`` is evaluated and returned.
*/
+#define _c_unlikely_(_x) (__builtin_expect(!!(_x), 0))
+
+/**
+ * DOC: Utility Macros
+ *
+ * A set of utility macros is provided which aids in creating safe macros
+ * suitable for use in other pre-processor statements as well as in C
+ * expressions.
+ */
+/**/
+
+/**
+ * C_EXPR_ASSERT() - Create expression with assertion
+ * @_expr: Expression to evaluate to
+ * @_assertion: Arbitrary assertion
+ * @_message: Message associated with the assertion
+ *
+ * This macro simply evaluates to ``_expr``. That is, it can be used in any
+ * context that expects an expression like ``_expr``. Additionally, it takes an
+ * assertion as ``_assertion`` and evaluates it through ``_Static_assert()``,
+ * using ``_message`` as debug message.
+ *
+ * The ``_Static_assert()`` builtin of C11 is defined as statement and thus
+ * cannot be used in expressions. This macro circumvents this restriction.
+ *
+ * Return: Evaluates to ``_expr``.
+ */
+#define C_EXPR_ASSERT(_expr, _assertion, _message) C_INTERNAL_EXPR_ASSERT((_expr), (_assertion), _message)
#if defined(__COVERITY__) // Coverity cannot const-fold __builtin_choose_expr()
-# define C_EXPR_ASSERT(_expr, _assertion, _message) (_expr)
+# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) (_expr)
#else
-# define C_EXPR_ASSERT(_expr, _assertion, _message) \
+# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) \
/* indentation and line-split to get better diagnostics */ \
(__builtin_choose_expr( \
!!(1 + 0 * sizeof( \
@@ -99,8 +228,8 @@ _Static_assert(_assertion, _message); \
#endif
/**
- * C_STRINGIFY() - stringify a token, but evaluate it first
- * @_x: token to evaluate and stringify
+ * C_STRINGIFY() - Stringify a token, but evaluate it first
+ * @_x: Token to evaluate and stringify
*
* Return: Evaluates to a constant string literal
*/
@@ -108,9 +237,9 @@ _Static_assert(_assertion, _message); \
#define C_INTERNAL_STRINGIFY(_x) #_x
/**
- * C_CONCATENATE() - concatenate two tokens, but evaluate them first
- * @_x: first token
- * @_y: second token
+ * C_CONCATENATE() - Concatenate two tokens, but evaluate them first
+ * @_x: First token
+ * @_y: Second token
*
* Return: Evaluates to a constant identifier
*/
@@ -118,8 +247,8 @@ _Static_assert(_assertion, _message); \
#define C_INTERNAL_CONCATENATE(_x, _y) _x ## _y
/**
- * C_EXPAND() - expand a tuple to a series of its values
- * @_x: tuple to expand
+ * C_EXPAND() - Expand a tuple to a series of its values
+ * @_x: Tuple to expand
*
* Return: Evaluates to the expanded tuple
*/
@@ -127,29 +256,32 @@ _Static_assert(_assertion, _message); \
#define C_INTERNAL_EXPAND(...) __VA_ARGS__
/**
- * C_VAR() - generate unique variable name
- * @_x: name of variable, optional
- * @_uniq: unique prefix, usually provided by __COUNTER__, optional
+ * C_VAR() - Generate unique variable name
+ * @_x: Name of variable, optional
+ * @_uniq: Unique prefix, usually provided by ``__COUNTER__``, optional
*
* This macro shall be used to generate unique variable names, that will not be
* shadowed by recursive macro invocations. It is effectively a
- * C_CONCATENATE of both arguments, but also provides a globally separated
- * prefix and makes the code better readable.
+ * :c:macro:`C_CONCATENATE` of both arguments, but also provides a globally
+ * separated prefix and makes the code better readable.
*
- * The second argument is optional. If not given, __LINE__ is implied, and as
- * such the macro will generate the same identifier if used multiple times on
- * the same code-line (or within a macro). This should be used if recursive
+ * The second argument is optional. If not given, ``__LINE__`` is implied, and
+ * as such the macro will generate the same identifier if used multiple times
+ * on the same code-line (or within a macro). This should be used if recursive
* calls into the macro are not expected. In fact, no argument is necessary in
- * this case, as a mere `C_VAR` will evaluate to a valid variable name.
+ * this case, as a mere ``C_VAR`` will evaluate to a valid variable name.
*
* This helper may be used by macro implementations that might reasonable well
* be called in a stacked fasion, like:
*
+ * .. code-block:: c
+ *
* c_max(foo, c_max(bar, baz))
*
- * Such a stacked call of c_max() might cause compiler warnings of shadowed
- * variables in the definition of c_max(). By using C_VAR(), such warnings
- * can be silenced as each evaluation of c_max() uses unique variable names.
+ * Such a stacked call of :c:macro:`c_max()` might cause compiler warnings of
+ * shadowed variables in the definition of :c:macro:`c_max()`. By using
+ * ``C_VAR()``, such warnings can be silenced as each evaluation of
+ * :c:macro:`c_max()` uses unique variable names.
*
* Return: This evaluates to a constant identifier.
*/
@@ -159,28 +291,28 @@ _Static_assert(_assertion, _message); \
#define C_VAR2(_x, _uniq) C_CONCATENATE(c_internal_var_unique_, C_CONCATENATE(_uniq, _x))
/**
- * C_CC_MACRO1() - provide safe environment to a macro
- * @_call: macro to call
- * @_x1: first argument
- * @...: further arguments to forward unmodified to @_call
+ * C_CC_MACRO1() - Provide safe environment to a macro
+ * @_call: Macro to call
+ * @_x1: First argument
+ * @...: Further arguments to forward unmodified to ``_call``
*
* This function simplifies the implementation of macros. Whenever you
- * implement a macro, provide the internal macro name as @_call and its
- * argument as @_x1. Inside of your internal macro, you...
+ * implement a macro, provide the internal macro name as ``_call`` and its
+ * argument as ``_x1``. Inside of your internal macro, you...
*
- * - ...are safe against multiple evaluation errors, since C_CC_MACRO1 will
- * store the initial parameters in temporary variables.
+ * - are safe against multiple evaluation errors, since ``C_CC_MACRO1``
+ * will store the initial parameters in temporary variables.
*
- * - ...support constant folding, as C_CC_MACRO1 takes care to invoke your
- * macro with the original values, if they are compile-time constant.
+ * - support constant folding, as ``C_CC_MACRO1`` takes care to invoke your
+ * macro with the original values, if they are compile-time constant.
*
- * - ...have unique variable names for recursive callers and will not run into
- * variable-shadowing-warnings accidentally.
+ * - have unique variable names for recursive callers and will not run into
+ * variable-shadowing-warnings accidentally.
*
- * - ...have properly typed arguments as C_CC_MACRO1 stores the original
- * arguments in an `__auto_type` temporary variable.
+ * - have properly typed arguments as ``C_CC_MACRO1`` stores the original
+ * arguments in an ``__auto_type`` temporary variable.
*
- * Return: Result of @_call is returned.
+ * Return: Result of ``_call`` is returned.
*/
#define C_CC_MACRO1(_call, _x1, ...) C_INTERNAL_CC_MACRO1(_call, __COUNTER__, (_x1), ## __VA_ARGS__)
#define C_INTERNAL_CC_MACRO1(_call, _x1q, _x1, ...) \
@@ -193,15 +325,15 @@ _Static_assert(_assertion, _message); \
}))
/**
- * C_CC_MACRO2() - provide safe environment to a macro
- * @_call: macro to call
- * @_x1: first argument
- * @_x2: second argument
- * @...: further arguments to forward unmodified to @_call
+ * C_CC_MACRO2() - Provide safe environment to a macro
+ * @_call: Macro to call
+ * @_x1: First argument
+ * @_x2: Second argument
+ * @...: Further arguments to forward unmodified to ``_call``
*
- * This is the 2-argument equivalent of C_CC_MACRO1().
+ * This is the 2-argument equivalent of :c:macro:`C_CC_MACRO1()`.
*
- * Return: Result of @_call is returned.
+ * Return: Result of ``_call`` is returned.
*/
#define C_CC_MACRO2(_call, _x1, _x2, ...) C_INTERNAL_CC_MACRO2(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), ## __VA_ARGS__)
#define C_INTERNAL_CC_MACRO2(_call, _x1q, _x1, _x2q, _x2, ...) \
@@ -215,16 +347,16 @@ _Static_assert(_assertion, _message); \
}))
/**
- * C_CC_MACRO3() - provide safe environment to a macro
- * @_call: macro to call
- * @_x1: first argument
- * @_x2: second argument
- * @_x3: third argument
- * @...: further arguments to forward unmodified to @_call
+ * C_CC_MACRO3() - Provide safe environment to a macro
+ * @_call: Macro to call
+ * @_x1: First argument
+ * @_x2: Second argument
+ * @_x3: Third argument
+ * @...: Further arguments to forward unmodified to ``_call``
*
- * This is the 3-argument equivalent of C_CC_MACRO1().
+ * This is the 3-argument equivalent of :c:macro:`C_CC_MACRO1()`.
*
- * Return: Result of @_call is returned.
+ * Return: Result of ``_call`` is returned.
*/
#define C_CC_MACRO3(_call, _x1, _x2, _x3, ...) C_INTERNAL_CC_MACRO3(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), __COUNTER__, (_x3), ## __VA_ARGS__)
#define C_INTERNAL_CC_MACRO3(_call, _x1q, _x1, _x2q, _x2, _x3q, _x3, ...) \
@@ -239,8 +371,17 @@ _Static_assert(_assertion, _message); \
}))
/**
- * C_ARRAY_SIZE() - calculate number of array elements at compile time
- * @_x: array to calculate size of
+ * DOC: Standard Library Utilities
+ *
+ * The C Standard Library lacks some crucial and basic support functions. This
+ * section describes the set of helpers provided as extension to the standard
+ * library.
+ */
+/**/
+
+/**
+ * C_ARRAY_SIZE() - Calculate number of array elements at compile time
+ * @_x: Array to calculate size of
*
* Return: Evaluates to a constant integer expression.
*/
@@ -258,9 +399,8 @@ _Static_assert(_assertion, _message); \
)
/**
- * C_DECIMAL_MAX() - calculate maximum length of the decimal
- * representation of an integer
- * @_type: integer variable/type
+ * C_DECIMAL_MAX() - Calculate maximum length of a decimal representation
+ * @_type: Integer variable/type
*
* This calculates the bytes required for the decimal representation of an
* integer of the given type. It accounts for a possible +/- prefix, but it
@@ -292,28 +432,41 @@ _Static_assert(_assertion, _message); \
)
/**
- * c_container_of() - cast a member of a structure out to the containing structure
- * @_ptr: pointer to the member or NULL
- * @_type: type of the container struct this is embedded in
- * @_member: name of the member within the struct
+ * c_container_of() - Cast a member of a structure out to the containing type
+ * @_ptr: Pointer to the member or NULL
+ * @_type: Type of the container struct this is embedded in
+ * @_member: Name of the member within the struct
*
- * This uses `offsetof(3)` to turn a pointer to a structure-member into a
+ * This uses ``offsetof(3)`` to turn a pointer to a structure-member into a
* pointer to the surrounding structure.
*
* Return: Pointer to the surrounding object.
*/
#define c_container_of(_ptr, _type, _member) C_CC_MACRO1(C_CONTAINER_OF, (_ptr), _type, _member)
#define C_CONTAINER_OF(_ptr, _type, _member) \
- __extension__ ({ \
- /* trigger warning if types do not match */ \
- (void)(&((_type *)0)->_member == (_ptr)); \
- _ptr ? (_type*)( (char*)_ptr - offsetof(_type, _member) ) : NULL; \
- })
+ C_EXPR_ASSERT( \
+ (_ptr ? (_type*)c_internal_container_of((void *)_ptr, offsetof(_type, _member)) : NULL), \
+ __builtin_types_compatible_p( \
+ __typeof__(*(_ptr)), \
+ __typeof__(((_type){})._member) \
+ ) || __builtin_types_compatible_p( \
+ __typeof__(_ptr), \
+ __typeof__(NULL) \
+ ), \
+ "Invalid use of C_CONTAINER_OF()" \
+ )
+static inline void *c_internal_container_of(void *ptr, size_t offset) {
+ /*
+ * Arithmetic on NULL is UB, even if in dead-code. Hide it in a proper
+ * C function, so the macro never emits it as code.
+ */
+ return (char *)ptr - offset;
+}
/**
- * c_max() - compute maximum of two values
- * @_a: value A
- * @_b: value B
+ * c_max() - Compute maximum of two values
+ * @_a: Value A
+ * @_b: Value B
*
* Calculate the maximum of both passed values. Both arguments are evaluated
* exactly once, under all circumstances. Furthermore, if both values are
@@ -329,9 +482,9 @@ _Static_assert(_assertion, _message); \
#define C_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
/**
- * c_min() - compute minimum of two values
- * @_a: value A
- * @_b: value B
+ * c_min() - Compute minimum of two values
+ * @_a: Value A
+ * @_b: Value B
*
* Calculate the minimum of both passed values. Both arguments are evaluated
* exactly once, under all circumstances. Furthermore, if both values are
@@ -347,32 +500,32 @@ _Static_assert(_assertion, _message); \
#define C_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
/**
- * c_less_by() - calculate clamped difference of two values
- * @_a: minuend
- * @_b: subtrahend
+ * c_less_by() - Calculate clamped difference of two values
+ * @_a: Minuend
+ * @_b: Subtrahend
*
- * Calculate [_a - _b], but clamp the result to 0. Both arguments are evaluated
- * exactly once, under all circumstances. Furthermore, if both values are
- * constant expressions, the result will be constant as well.
+ * Calculate ``_a - _b``, but clamp the result to 0. Both arguments are
+ * evaluated exactly once, under all circumstances. Furthermore, if both values
+ * are constant expressions, the result will be constant as well.
*
* The comparison of their values is performed with the types given by the
* caller. It is the caller's responsibility to convert them to suitable types
* if necessary.
*
- * Return: This computes [_a - _b], if [_a > _b]. Otherwise, 0 is returned.
+ * Return: This computes ``_a - _b``, if ``_a > _b``. Otherwise, 0 is returned.
*/
#define c_less_by(_a, _b) C_CC_MACRO2(C_LESS_BY, (_a), (_b))
#define C_LESS_BY(_a, _b) ((_a) > (_b) ? (_a) - (_b) : 0)
/**
- * c_clamp() - clamp value to lower and upper boundary
- * @_x: value to clamp
- * @_low: lower boundary
- * @_high: higher boundary
+ * c_clamp() - Clamp value to lower and upper boundary
+ * @_x: Value to clamp
+ * @_low: Lower boundary
+ * @_high: Higher boundary
*
- * This clamps @_x to the lower and higher bounds given as @_low and @_high.
- * All arguments are evaluated exactly once, and yield a constant expression if
- * all arguments are constant as well.
+ * This clamps ``_x`` to the lower and higher bounds given as ``_low`` and
+ * ``_high``. All arguments are evaluated exactly once, and yield a constant
+ * expression if all arguments are constant as well.
*
* The comparison of their values is performed with the types given by the
* caller. It is the caller's responsibility to convert them to suitable types
@@ -384,18 +537,18 @@ _Static_assert(_assertion, _message); \
#define C_CLAMP(_x, _low, _high) ((_x) > (_high) ? (_high) : (_x) < (_low) ? (_low) : (_x))
/**
- * c_div_round_up() - calculate integer quotient but round up
- * @_x: dividend
- * @_y: divisor
+ * c_div_round_up() - Calculate integer quotient but round up
+ * @_x: Dividend
+ * @_y: Divisor
*
- * Calculates [x / y] but rounds up the result to the next integer. All
+ * Calculates ``x / y`` but rounds up the result to the next integer. All
* arguments are evaluated exactly once, and yield a constant expression if all
* arguments are constant.
*
- * Note:
- * [(x + y - 1) / y] suffers from an integer overflow, even though the
+ * **Note:**
+ * ``(x + y - 1) / y`` suffers from an integer overflow, even though the
* computation should be possible in the given type. Therefore, we use
- * [x / y + !!(x % y)]. Note that on most CPUs a division returns both the
+ * ``x / y + !!(x % y)``. Note that on most CPUs a division returns both the
* quotient and the remainder, so both should be equally fast. Furthermore, if
* the divisor is a power of two, the compiler will optimize it, anyway.
*
@@ -409,34 +562,35 @@ _Static_assert(_assertion, _message); \
#define C_DIV_ROUND_UP(_x, _y) ((_x) / (_y) + !!((_x) % (_y)))
/**
- * c_align_to() - align value to a multiple
- * @_val: value to align
- * @_to: align to multiple of this
+ * c_align_to() - Align value to a multiple
+ * @_val: Value to align
+ * @_to: Align to multiple of this
*
- * This aligns @_val to a multiple of @_to. If @_val is already a multiple of
- * @_to, @_val is returned unchanged. This function operates within the
- * boundaries of the type of @_val and @_to. Make sure to cast them if needed.
+ * This aligns ``_val`` to a multiple of ``_to``. If ``_val`` is already a
+ * multiple of ``_to``, ``_val`` is returned unchanged. This function operates
+ * within the boundaries of the type of ``_val`` and ``_to``. Make sure to cast
+ * them if needed.
*
* The arguments of this macro are evaluated exactly once. If both arguments
* are a constant expression, this also yields a constant return value.
*
- * Note that @_to must be a power of 2, otherwise the behavior will not match
- * expectations.
+ * Note that ``_to`` must be a power of 2, otherwise the behavior will not
+ * match expectations.
*
- * Return: @_val aligned to a multiple of @_to
+ * Return: ``_val`` aligned to a multiple of ``_to``.
*/
#define c_align_to(_val, _to) C_CC_MACRO2(C_ALIGN_TO, (_val), (_to))
#define C_ALIGN_TO(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1))
/**
- * c_assert() - runtime assertions
- * @expr_result: result of an expression
+ * c_assert() - Runtime assertions
+ * @expr_result: Result of an expression
*
- * This function behaves like the standard `assert(3)` macro. That is, if
- * `NDEBUG` is defined, it is a no-op. In all other cases it will assert that
+ * This function behaves like the standard ``assert(3)`` macro. That is, if
+ * ``NDEBUG`` is defined, it is a no-op. In all other cases it will assert that
* the result of the passed expression is true.
*
- * Unlike the standard `assert(3)` macro, this function always evaluates its
+ * Unlike the standard ``assert(3)`` macro, this function always evaluates its
* argument. This means side-effects will always be evaluated! However, if the
* macro is used with constant expressions, the compiler will be able to
* optimize it away.
@@ -447,13 +601,13 @@ _Static_assert(_assertion, _message); \
})
/**
- * c_errno() - return valid errno
+ * c_errno() - Return valid errno
*
- * This helper should be used to shut up gcc if you know 'errno' is valid (ie.,
- * errno is > 0). Instead of "return -errno;", use
- * "return -c_errno();" It will suppress bogus gcc warnings in case it assumes
- * 'errno' might be 0 (or <0) and thus the caller's error-handling might not be
- * triggered.
+ * This helper should be used to shut up gcc if you know ``errno`` is valid
+ * (ie., ``errno`` is greater than 0). Instead of ``return -errno;``, use
+ * ``return -c_errno();`` It will suppress bogus gcc warnings in case it
+ * assumes ``errno`` might be 0 (or smaller than 0) and thus the caller's
+ * error-handling might not be triggered.
*
* This helper should be avoided whenever possible. However, occasionally we
* really want to shut up gcc (especially with static/inline functions). In
@@ -470,62 +624,179 @@ static inline int c_errno(void) {
return _c_likely_(errno > 0) ? errno : ENOTRECOVERABLE;
}
-/*
- * Common Destructors
+/**
+ * c_memset() - Fill memory region with constant byte
+ * @p: Pointer to memory region, if non-empty
+ * @c: Value to fill with
+ * @n: Size of the memory region in bytes
*
- * Followingly, there're a bunch of common 'static inline' destructors, which
- * simply call the function that they're named after, but return "INVALID"
- * instead of "void". This allows direct assignment to any member-field and/or
- * variable they're defined in, like:
+ * This function works like ``memset(3)`` if ``n`` is non-zero. If ``n`` is
+ * zero, this function is a no-op. Therefore, unlike ``memset(3)`` it is safe
+ * to call this function with ``NULL`` as ``p`` if ``n`` is 0.
*
- * foo = c_free(foo);
+ * Return: ``p`` is returned.
+ */
+static inline void *c_memset(void *p, int c, size_t n) {
+ if (n > 0)
+ memset(p, c, n);
+ return p;
+}
+
+/**
+ * c_memzero() - Clear memory area
+ * @p: Pointer to memory region, if non-empty
+ * @n: Size of the memory region in bytes
*
- * or
+ * Clear a memory area to 0. If the memory area is empty, this is a no-op.
+ * Similar to ``c_memset()``, this function allows ``p`` to be ``NULL`` if the
+ * area is empty.
+ *
+ * Return: ``p`` is returned.
+ */
+static inline void *c_memzero(void *p, size_t n) {
+ return c_memset(p, 0, n);
+}
+
+/**
+ * c_memcpy() - Copy memory area
+ * @dst: Pointer to target area
+ * @src: Pointer to source area
+ * @n: Length of area to copy
*
- * foo->bar = c_close(foo->bar);
+ * Copy the memory of size ``n`` from ``src`` to ``dst``, just as ``memcpy(3)``
+ * does, except this function allows either to be ``NULL`` if ``n`` is zero. In
+ * the latter case, the operation is a no-op.
+ *
+ * Return: ``p`` is returned.
+ */
+static inline void *c_memcpy(void *dst, const void *src, size_t n) {
+ if (n > 0)
+ memcpy(dst, src, n);
+ return dst;
+}
+
+/**
+ * DOC: Common Destructors
+ *
+ * A set of destructors is provided which extends standard library destructors
+ * to adhere to some adjuvant rules. In particular, they return an invalid
+ * value of the particular object, rather than void. This allows direct
+ * assignment to any member-field and/or variable they are defined in, like:
+ *
+ * .. code-block:: c
+ *
+ * foo = c_free(foo);
+ * foo->bar = c_close(foo->bar);
*
* Furthermore, all those destructors can be safely called with the "INVALID"
* value as argument, and they will be a no-op.
*/
+/**/
+/**
+ * c_free() - Destructor-wrapper for free()
+ * @p: Value to pass to destructor, or NULL
+ *
+ * Wrapper around ``free()``, but always returns ``NULL``.
+ *
+ * Return: NULL is returned.
+ */
static inline void *c_free(void *p) {
free(p);
return NULL;
}
+/**
+ * c_close() - Destructor-wrapper for close()
+ * @fd: File-descriptor to pass to destructor, or negative value
+ *
+ * Wrapper around ``close()``, but a no-op if a negative value is provided.
+ * Always returns ``-1``.
+ *
+ * Return: -1 is returned.
+ */
static inline int c_close(int fd) {
if (fd >= 0)
close(fd);
return -1;
}
+/**
+ * c_fclose() - Destructor-wrapper for fclose()
+ * @f: File handle to pass to destructor, or NULL
+ *
+ * Wrapper around ``fclose()``, but a no-op if ``NULL`` is passed. Always
+ * returns ``NULL``.
+ *
+ * Return: NULL is returned.
+ */
static inline FILE *c_fclose(FILE *f) {
if (f)
fclose(f);
return NULL;
}
+/**
+ * c_closedir() - Destructor-wrapper for closedir)
+ * @d: Directory handle to pass to destructor, or NULL
+ *
+ * Wrapper around ``closedir()``, but a no-op if ``NULL`` is passed. Always
+ * returns ``NULL``.
+ *
+ * Return: NULL is returned.
+ */
static inline DIR *c_closedir(DIR *d) {
if (d)
closedir(d);
return NULL;
}
-/*
- * Common Cleanup Helpers
+/**
+ * DOC: Common Cleanup Helpers
+ *
+ * A set of helpers that aid in creating functions suitable for use with
+ * :c:macro:`_c_cleanup_()`. Furthermore, a collection of predefined cleanup
+ * functions of a set of standard library objects ready for use with
+ * :c:macro:`_c_cleanup_()`.
+ * Those cleanup helpers are always suffixed with a ``p``.
*
- * A bunch of _c_cleanup_(foobarp) helpers that are used all over the place.
- * Note that all of those have the "if (IS_INVALID(foobar))" check inline, so
- * compilers can optimize most of the cleanup-paths in a function. However, if
- * the function they call already does this _inline_, then it might be skipped.
+ * The helpers that are provided are:
+ *
+ * - ``c_freep()``: Wrapper around :c:func:`c_free()`.
+ * - ``c_closep()``: Wrapper around :c:func:`c_close()`.
+ * - ``c_fclosep()``: Wrapper around :c:func:`c_fclose()`.
+ * - ``c_closedirp()``: Wrapper around :c:func:`c_closedir()`.
*/
+/**/
+/**
+ * C_DEFINE_CLEANUP() - Define cleanup helper
+ * @_type: Type of object to cleanup
+ * @_func: Destructor of the respective type
+ *
+ * Define a C static inline function that takes a single argument of type
+ * `_type` and calls `_func` on it, if its dereferenced value of its argument
+ * evaluates to true. Otherwise, it is a no-op.
+ *
+ * This macro allows for very simple and fast creation of cleanup helpers for
+ * use with ``_c_cleanup_()``, based on any destructor and type you provide to
+ * it.
+ */
#define C_DEFINE_CLEANUP(_type, _func) \
static inline void _func ## p(_type *p) { \
if (*p) \
_func(*p); \
} struct c_internal_trailing_semicolon
+/**
+ * C_DEFINE_DIRECT_CLEANUP() - Define direct cleanup helper
+ * @_type: Type of object to cleanup
+ * @_func: Destructor of the respective type
+ *
+ * This works like :c:macro:`C_DEFINE_CLEANUP()` but does not check the
+ * dereferenced value of its argument. It always unconditionally invokes the
+ * destructor.
+ */
#define C_DEFINE_DIRECT_CLEANUP(_type, _func) \
static inline void _func ## p(_type *p) { \
_func(*p); \
diff --git a/src/docs/api.rst b/src/docs/api.rst
new file mode 100644
index 0000000000..6cb51827ae
--- /dev/null
+++ b/src/docs/api.rst
@@ -0,0 +1,5 @@
+API
+===
+
+.. c:autodoc:: c-*.h
+ :transform: kerneldoc
diff --git a/src/docs/conf.py b/src/docs/conf.py
new file mode 100644
index 0000000000..65ffb00e84
--- /dev/null
+++ b/src/docs/conf.py
@@ -0,0 +1,43 @@
+#
+# Sphinx Documentation Configuration
+#
+
+import re
+import os
+import sys
+
+import capidocs.kerneldoc
+import hawkmoth
+
+# Global Setup
+
+project = 'c-stdaux'
+
+author = 'C-Util Community'
+copyright = '2022, C-Util Community'
+
+# Hawkmoth C-Audodoc Setup
+
+capidocs.kerneldoc.hawkmoth_conf()
+
+# Extensions
+
+exclude_patterns = []
+
+extensions = [
+ 'hawkmoth',
+]
+
+# Hawkmoth Options
+
+cautodoc_clang = capidocs.kerneldoc.hawkmoth_include_args()
+
+cautodoc_root = os.path.abspath('..')
+
+cautodoc_transformations = {
+ 'kerneldoc': capidocs.kerneldoc.hawkmoth_converter,
+}
+
+# HTML Options
+
+html_theme = 'sphinx_rtd_theme'
diff --git a/src/docs/index.rst b/src/docs/index.rst
new file mode 100644
index 0000000000..5ad99374de
--- /dev/null
+++ b/src/docs/index.rst
@@ -0,0 +1,14 @@
+Introduction
+============
+
+The **c-stdaux** project contains support-macros and auxiliary functions around
+the functionality of common C standard libraries. This includes helpers for the
+ISO-C Standard Library, but also other common specifications like POSIX or
+common extended features of wide-spread compilers like gcc and clang.
+
+.. toctree::
+ :caption: Library Documentation
+ :hidden:
+
+ self
+ api
diff --git a/src/docs/requirements.txt b/src/docs/requirements.txt
new file mode 100644
index 0000000000..97872db187
--- /dev/null
+++ b/src/docs/requirements.txt
@@ -0,0 +1,3 @@
+c-apidocs>=0.0.2
+clang>=6
+hawkmoth>=0.7
diff --git a/src/libcstdaux.sym b/src/libcstdaux.sym
new file mode 100644
index 0000000000..2073665f1a
--- /dev/null
+++ b/src/libcstdaux.sym
@@ -0,0 +1,6 @@
+LIBCSTDAUX_1 {
+global:
+ c_internal_dummy;
+local:
+ *;
+};
diff --git a/src/meson.build b/src/meson.build
index a9322d4daa..9d78af9c98 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -4,9 +4,14 @@
# future, if we add more complex helpers.)
#
+libcstdaux_vars = {
+ 'cflags': ' '.join(cflags),
+ 'version-scripts': use_version_scripts,
+}
+
libcstdaux_dep = declare_dependency(
include_directories: include_directories('.'),
- variables: { 'cflags': ' '.join(cflags) },
+ variables: libcstdaux_vars,
version: meson.project_version(),
)
@@ -17,6 +22,7 @@ if not meson.is_subproject()
description: project_description,
filebase: 'libcstdaux-'+major,
name: 'libcstdaux',
+ unescaped_variables: libcstdaux_vars,
version: meson.project_version(),
)
endif
diff --git a/src/test-api.c b/src/test-api.c
index cddbb70dac..44be1395a9 100644
--- a/src/test-api.c
+++ b/src/test-api.c
@@ -189,6 +189,9 @@ static void test_api_macros(void) {
static void test_api_functions(void) {
void *fns[] = {
(void *)c_errno,
+ (void *)c_memset,
+ (void *)c_memzero,
+ (void *)c_memcpy,
(void *)c_free,
(void *)c_close,
(void *)c_fclose,
diff --git a/src/test-basic.c b/src/test-basic.c
index 15499353cb..e5e3f907ed 100644
--- a/src/test-basic.c
+++ b/src/test-basic.c
@@ -7,7 +7,6 @@
#undef NDEBUG
#include <stdlib.h>
-#include <sys/eventfd.h>
#include "c-stdaux.h"
/*
@@ -138,6 +137,8 @@ static void test_misc(int non_constant_expr) {
c_assert(&sub == c_container_of(&sub.a, struct foobar, a));
c_assert(&sub == c_container_of(&sub.b, struct foobar, b));
c_assert(&sub == c_container_of((const char *)&sub.b, struct foobar, b));
+
+ c_assert(!c_container_of(NULL, struct foobar, b));
}
/*
@@ -304,6 +305,58 @@ static void test_misc(int non_constant_expr) {
errno = 0;
c_assert(c_errno() != errno);
}
+
+ /*
+ * Test c_memset(). Simply verify its most basic behavior, as well as
+ * calling it on empty regions.
+ */
+ {
+ uint64_t v = (uint64_t)-1;
+ size_t n;
+ void *p;
+
+ /* try filling with 0 and 0xff */
+ c_assert(v == (uint64_t)-1);
+ c_memset(&v, 0, sizeof(v));
+ c_assert(v == (uint64_t)0);
+ c_memset(&v, 0xff, sizeof(v));
+ c_assert(v == (uint64_t)-1);
+
+ /*
+ * Try tricking the optimizer into thinking @p cannot be NULL,
+ * as normal `memset(3)` would allow.
+ */
+ p = NULL;
+ n = 0;
+ c_memset(p, 0, n);
+ if (p)
+ abort();
+ c_assert(p == NULL);
+ }
+
+ /*
+ * Test c_memzero(). Simply verify it can clear a trivial area to 0.
+ */
+ {
+ uint64_t v = (uint64_t)-1;
+
+ c_assert(v == (uint64_t)-1);
+ c_memzero(&v, sizeof(v));
+ c_assert(v == (uint64_t)0);
+ }
+
+ /*
+ * Test c_memcpy() with a simple 8-byte copy.
+ */
+ {
+ uint64_t v1 = (uint64_t)-1, v2 = (uint64_t)0;
+
+ c_assert(v1 == (uint64_t)-1);
+ c_memcpy(&v1, &v2, sizeof(v1));
+ c_assert(v1 == (uint64_t)0);
+
+ c_memcpy(NULL, NULL, 0);
+ }
}
/*
@@ -345,13 +398,16 @@ static void test_destructors(void) {
* helpers actually close the fd, and cope fine with negative numbers.
*/
{
- int fd;
+ int r, fd1, fd2, tmp[2];
- fd = eventfd(0, EFD_CLOEXEC);
- c_assert(fd >= 0);
+ r = pipe(tmp);
+ c_assert(r >= 0);
+ fd1 = tmp[0];
+ fd2 = tmp[1];
/* verify c_close() returns -1 */
- c_assert(c_close(fd) == -1);
+ c_assert(c_close(fd1) == -1);
+ c_assert(c_close(fd2) == -1);
/* verify c_close() deals fine with negative fds */
c_assert(c_close(-1) == -1);
@@ -370,11 +426,15 @@ static void test_destructors(void) {
* path works as well.
*/
for (i = 0; i < 2; ++i) {
- _c_cleanup_(c_closep) _c_unused_ int t = -1;
+ _c_cleanup_(c_closep) _c_unused_ int t1 = -1, t2 = -1;
- t = eventfd(0, EFD_CLOEXEC);
- c_assert(t >= 0);
- c_assert(t == fd);
+ r = pipe(tmp);
+ c_assert(r >= 0);
+ t1 = tmp[0];
+ t2 = tmp[1];
+
+ c_assert(t1 == fd1);
+ c_assert(t2 == fd2);
}
}
@@ -383,11 +443,13 @@ static void test_destructors(void) {
* tests for c_close() (i.e., sparse FD allocation).
*/
{
+ int r, fd, tmp[2];
FILE *f;
- int fd;
- fd = eventfd(0, EFD_CLOEXEC);
- c_assert(fd >= 0);
+ r = pipe(tmp);
+ c_assert(r >= 0);
+ fd = tmp[0];
+ c_close(tmp[1]);
f = fdopen(fd, "r");
c_assert(f);
@@ -415,10 +477,12 @@ static void test_destructors(void) {
_c_cleanup_(c_fclosep) _c_unused_ FILE *t = NULL;
int tfd;
- tfd = eventfd(0, EFD_CLOEXEC);
- c_assert(tfd >= 0);
- c_assert(tfd == fd); /* the same as before */
+ r = pipe(tmp);
+ c_assert(r >= 0);
+ tfd = tmp[0];
+ c_close(tmp[1]);
+ c_assert(tfd == fd); /* the same as before */
t = fdopen(tfd, "r");
c_assert(t);
}