Skip to content

Commit d873966

Browse files
committed
util: detect all possible qsort_r and qsort_s variants
As reported in https://bugs.freebsd.org/271234, recent versions of FreeBSD have adjusted the prototype for qsort_r() to match the POSIX interface. This causes libgit2's CMake configuration check to fail to detect qsort_r(), making it fall back to qsort_s(), which in libgit2 also has an incompatible interface. With recent versions of clang this results in a "incompatible function pointer types" compile error. Summarizing, there are four variations of 'qsort-with-context': * old style BSD qsort_r(), used in FreeBSD 13 and earlier, where the comparison function has the context parameter first * GNU or POSIX qsort_r(), also used in FreeBSD 14 and later, where the comparison function has the context parameter last * C11 qsort_s(), where the comparison function has the context parameter last * Microsoft qsort_s(), where the comparison function has the context parameter first Add explicit detections for all these variants, so they get detected as (in the same order as above): * `GIT_QSORT_R_BSD` * `GIT_QSORT_R_GNU` * `GIT_QSORT_S_C11` * `GIT_QSORT_S_MSC` An additional complication is that on FreeBSD 14 and later, <stdlib.h> uses the C11 _Generic() macro mechanism to automatically select the correct qsort_r() prototype, depending on the caller's comparison function argument. This breaks CMake's check_prototype_definition() functionality, since it tries to redefine the function, and _Generic macro is expanded inline causing a compile error. Work around that problem by putting the function names in parentheses, to prevent the preprocessor from using a macro to replace the function name. Also, in `git__qsort_r()`, change the `#if` order so the variants that do not have to use glue are preferred.
1 parent fc4c00b commit d873966

File tree

3 files changed

+29
-11
lines changed

3 files changed

+29
-11
lines changed

src/CMakeLists.txt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,29 @@ add_feature_info(futimens GIT_USE_FUTIMENS "futimens support")
5858

5959
# qsort
6060

61+
# old-style FreeBSD qsort_r() has the 'context' parameter as the first argument
62+
# of the comparison function:
6163
check_prototype_definition(qsort_r
62-
"void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))"
64+
"void (qsort_r)(void *base, size_t nmemb, size_t size, void *context, int (*compar)(void *, const void *, const void *))"
6365
"" "stdlib.h" GIT_QSORT_R_BSD)
6466

67+
# GNU or POSIX qsort_r() has the 'context' parameter as the last argument of the
68+
# comparison function:
6569
check_prototype_definition(qsort_r
66-
"void qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg)"
70+
"void (qsort_r)(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *context)"
6771
"" "stdlib.h" GIT_QSORT_R_GNU)
6872

69-
check_function_exists(qsort_s GIT_QSORT_S)
73+
# C11 qsort_s() has the 'context' parameter as the last argument of the
74+
# comparison function, and returns an error status:
75+
check_prototype_definition(qsort_s
76+
"errno_t (qsort_s)(void *base, rsize_t nmemb, rsize_t size, int (*compar)(const void *, const void *, void *), void *context)"
77+
"0" "stdlib.h" GIT_QSORT_S_C11)
78+
79+
# MSC qsort_s() has the 'context' parameter as the first argument of the
80+
# comparison function, and as the last argument of qsort_s():
81+
check_prototype_definition(qsort_s
82+
"void (qsort_s)(void *base, size_t num, size_t width, int (*compare )(void *, const void *, const void *), void *context)"
83+
"" "stdlib.h" GIT_QSORT_S_MSC)
7084

7185
# random / entropy data
7286

src/util/git2_features.h.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626

2727
#cmakedefine GIT_QSORT_R_BSD
2828
#cmakedefine GIT_QSORT_R_GNU
29-
#cmakedefine GIT_QSORT_S
29+
#cmakedefine GIT_QSORT_S_C11
30+
#cmakedefine GIT_QSORT_S_MSC
3031

3132
#cmakedefine GIT_SSH 1
3233
#cmakedefine GIT_SSH_MEMORY_CREDENTIALS 1

src/util/util.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# endif
1919
# include <windows.h>
2020

21-
# ifdef GIT_QSORT_S
21+
# ifdef GIT_QSORT_S_MSC
2222
# include <search.h>
2323
# endif
2424
#endif
@@ -673,7 +673,7 @@ size_t git__unescape(char *str)
673673
return (pos - str);
674674
}
675675

676-
#if defined(GIT_QSORT_S) || defined(GIT_QSORT_R_BSD)
676+
#if defined(GIT_QSORT_S_MSC) || defined(GIT_QSORT_R_BSD)
677677
typedef struct {
678678
git__sort_r_cmp cmp;
679679
void *payload;
@@ -690,7 +690,8 @@ static int GIT_LIBGIT2_CALL git__qsort_r_glue_cmp(
690690

691691
#if !defined(GIT_QSORT_R_BSD) && \
692692
!defined(GIT_QSORT_R_GNU) && \
693-
!defined(GIT_QSORT_S)
693+
!defined(GIT_QSORT_S_C11) && \
694+
!defined(GIT_QSORT_S_MSC)
694695
static void swap(uint8_t *a, uint8_t *b, size_t elsize)
695696
{
696697
char tmp[256];
@@ -721,12 +722,14 @@ static void insertsort(
721722
void git__qsort_r(
722723
void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
723724
{
724-
#if defined(GIT_QSORT_R_BSD)
725+
#if defined(GIT_QSORT_R_GNU)
726+
qsort_r(els, nel, elsize, cmp, payload);
727+
#elif defined(GIT_QSORT_S_C11)
728+
qsort_s(els, nel, elsize, cmp, payload);
729+
#elif defined(GIT_QSORT_R_BSD)
725730
git__qsort_r_glue glue = { cmp, payload };
726731
qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
727-
#elif defined(GIT_QSORT_R_GNU)
728-
qsort_r(els, nel, elsize, cmp, payload);
729-
#elif defined(GIT_QSORT_S)
732+
#elif defined(GIT_QSORT_S_MSC)
730733
git__qsort_r_glue glue = { cmp, payload };
731734
qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
732735
#else

0 commit comments

Comments
 (0)