diff --git a/include/stdlib.h b/include/stdlib.h --- a/include/stdlib.h +++ b/include/stdlib.h @@ -311,8 +311,8 @@ int mkostemp(char *, int); int mkostemps(char *, int, int); int mkostempsat(int, char *, int, int); -void qsort_r(void *, size_t, size_t, void *, - int (*)(void *, const void *, const void *)); +void qsort_r(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); int radixsort(const unsigned char **, int, const unsigned char *, unsigned); void *reallocarray(void *, size_t, size_t) __result_use_check @@ -332,6 +332,40 @@ __uint64_t strtouq(const char *, char **, int); +/* + * In FreeBSD 14, the prototype of qsort_r() was modified to comply with + * POSIX. The standardized qsort_r()'s order of last two parameters was + * changed, and the comparator function is now taking thunk as its last + * parameter, and both are different from the ones expected by the historical + * FreeBSD qsort_r() interface. + * + * Apply a workaround where we explicitly link against the historical + * interface, qsort_r@FBSD_1.0, in case when qsort_r() is called with + * the last parameter with a function pointer that exactly matches the + * historical FreeBSD qsort_r() comparator signature, so applications + * written for the historical interface can continue to work without + * modification. + */ +#if defined(__generic) || defined(__cplusplus) +void __qsort_r_compat(void *, size_t, size_t, void *, + int (*)(void *, const void *, const void *)); +__sym_compat(qsort_r, __qsort_r_compat, FBSD_1.0); +#endif +#if defined(__generic) && !defined(__cplusplus) +#define qsort_r(base, nel, width, arg4, arg5) \ + __generic(arg5, int (*)(void *, const void *, const void *), \ + __qsort_r_compat, qsort_r)(base, nel, width, arg4, arg5) +#elif defined(__cplusplus) +__END_DECLS +extern "C++" { +static inline void qsort_r(void *base, size_t nmemb, size_t size, + void *thunk, int (*compar)(void *, const void *, const void *)) { + __qsort_r_compat(base, nmemb, size, thunk, compar); +} +} +__BEGIN_DECLS +#endif + extern char *suboptarg; /* getsubopt(3) external variable */ #endif /* __BSD_VISIBLE */ diff --git a/lib/libc/gen/scandir-compat11.c b/lib/libc/gen/scandir-compat11.c --- a/lib/libc/gen/scandir-compat11.c +++ b/lib/libc/gen/scandir-compat11.c @@ -58,8 +58,8 @@ #define SELECT(x) select(x) -static int freebsd11_scandir_thunk_cmp(void *thunk, const void *p1, - const void *p2); +static int freebsd11_scandir_thunk_cmp(const void *p1, const void *p2, + void *thunk); int freebsd11_scandir(const char *dirname, struct freebsd11_dirent ***namelist, @@ -116,7 +116,7 @@ closedir(dirp); if (numitems && dcomp != NULL) qsort_r(names, numitems, sizeof(struct freebsd11_dirent *), - &dcomp, freebsd11_scandir_thunk_cmp); + freebsd11_scandir_thunk_cmp, &dcomp); *namelist = names; return (numitems); @@ -141,7 +141,7 @@ } static int -freebsd11_scandir_thunk_cmp(void *thunk, const void *p1, const void *p2) +freebsd11_scandir_thunk_cmp(const void *p1, const void *p2, void *thunk) { int (*dc)(const struct freebsd11_dirent **, const struct freebsd11_dirent **); diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c --- a/lib/libc/gen/scandir.c +++ b/lib/libc/gen/scandir.c @@ -63,7 +63,7 @@ typedef DECLARE_BLOCK(int, dcomp_block, const struct dirent **, const struct dirent **); #else -static int scandir_thunk_cmp(void *thunk, const void *p1, const void *p2); +static int scandir_thunk_cmp(const void *p1, const void *p2, void *thunk); #endif static int @@ -123,7 +123,7 @@ qsort_b(names, numitems, sizeof(struct dirent *), (void*)dcomp); #else qsort_r(names, numitems, sizeof(struct dirent *), - &dcomp, scandir_thunk_cmp); + scandir_thunk_cmp, &dcomp); #endif *namelist = names; return (numitems); @@ -199,7 +199,7 @@ } static int -scandir_thunk_cmp(void *thunk, const void *p1, const void *p2) +scandir_thunk_cmp(const void *p1, const void *p2, void *thunk) { int (*dc)(const struct dirent **, const struct dirent **); diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -11,8 +11,8 @@ getsubopt.c hcreate.c hcreate_r.c hdestroy_r.c heapsort.c heapsort_b.c \ hsearch_r.c imaxabs.c imaxdiv.c \ insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c \ - merge.c mergesort_b.c ptsname.c qsort.c qsort_r.c qsort_s.c \ - quick_exit.c radixsort.c rand.c \ + merge.c mergesort_b.c ptsname.c qsort.c qsort_r.c qsort_r_compat.c \ + qsort_s.c quick_exit.c radixsort.c rand.c \ random.c reallocarray.c reallocf.c realpath.c remque.c \ set_constraint_handler_s.c strfmon.c strtoimax.c \ strtol.c strtold.c strtoll.c strtoq.c strtoul.c strtonum.c strtoull.c \ diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map --- a/lib/libc/stdlib/Symbol.map +++ b/lib/libc/stdlib/Symbol.map @@ -49,7 +49,6 @@ lfind; mergesort; putenv; - qsort_r; qsort; radixsort; sradixsort; @@ -130,6 +129,7 @@ FBSD_1.7 { clearenv; + qsort_r; }; FBSDprivate_1.0 { diff --git a/lib/libc/stdlib/qsort.3 b/lib/libc/stdlib/qsort.3 --- a/lib/libc/stdlib/qsort.3 +++ b/lib/libc/stdlib/qsort.3 @@ -32,7 +32,7 @@ .\" @(#)qsort.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd January 20, 2020 +.Dd September 30, 2022 .Dt QSORT 3 .Os .Sh NAME @@ -67,8 +67,8 @@ .Fa "void *base" .Fa "size_t nmemb" .Fa "size_t size" +.Fa "int \*[lp]*compar\*[rp]\*[lp]const void *, const void *, void *\*[rp]" .Fa "void *thunk" -.Fa "int \*[lp]*compar\*[rp]\*[lp]void *, const void *, const void *\*[rp]" .Fc .Ft int .Fo heapsort @@ -157,7 +157,7 @@ .Fn qsort , except that it takes an additional argument, .Fa thunk , -which is passed unchanged as the first argument to function pointed to +which is passed unchanged as the last argument to function pointed to .Fa compar . This allows the comparison function to access additional data without using global variables, and thus @@ -436,3 +436,9 @@ The variants of these functions that take blocks as arguments first appeared in Mac OS X. This implementation was created by David Chisnall. +.Pp +In +.Fx 14.0 , +the prototype of +.Fn qsort_r +was updated to match POSIX. diff --git a/lib/libc/stdlib/qsort.c b/lib/libc/stdlib/qsort.c --- a/lib/libc/stdlib/qsort.c +++ b/lib/libc/stdlib/qsort.c @@ -42,6 +42,8 @@ #include "libc_private.h" #if defined(I_AM_QSORT_R) +typedef int cmp_t(const void *, const void *, void *); +#elif defined(I_AM_QSORT_R_COMPAT) typedef int cmp_t(void *, const void *, const void *); #elif defined(I_AM_QSORT_S) typedef int cmp_t(const void *, const void *, void *); @@ -72,6 +74,8 @@ if ((n) > 0) swapfunc(a, b, n) #if defined(I_AM_QSORT_R) +#define CMP(t, x, y) (cmp((x), (y), (t))) +#elif defined(I_AM_QSORT_R_COMPAT) #define CMP(t, x, y) (cmp((t), (x), (y))) #elif defined(I_AM_QSORT_S) #define CMP(t, x, y) (cmp((x), (y), (t))) @@ -81,7 +85,7 @@ static inline char * med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk -#if !defined(I_AM_QSORT_R) && !defined(I_AM_QSORT_S) +#if !defined(I_AM_QSORT_R) && !defined(I_AM_QSORT_R_COMPAT) && !defined(I_AM_QSORT_S) __unused #endif ) @@ -97,6 +101,8 @@ */ #if defined(I_AM_QSORT_R) #define local_qsort local_qsort_r +#elif defined(I_AM_QSORT_R_COMPAT) +#define local_qsort local_qsort_r_compat #elif defined(I_AM_QSORT_S) #define local_qsort local_qsort_s #endif @@ -211,10 +217,16 @@ #if defined(I_AM_QSORT_R) void -qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +(qsort_r)(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) { local_qsort_r(a, n, es, cmp, thunk); } +#elif defined(I_AM_QSORT_R_COMPAT) +void +__qsort_r_compat(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +{ + local_qsort_r_compat(a, n, es, cmp, thunk); +} #elif defined(I_AM_QSORT_S) errno_t qsort_s(void *a, rsize_t n, rsize_t es, cmp_t *cmp, void *thunk) diff --git a/lib/libc/stdlib/qsort_r.c b/lib/libc/stdlib/qsort_r.c --- a/lib/libc/stdlib/qsort_r.c +++ b/lib/libc/stdlib/qsort_r.c @@ -4,16 +4,5 @@ * * $FreeBSD$ */ -#include "block_abi.h" #define I_AM_QSORT_R #include "qsort.c" - -typedef DECLARE_BLOCK(int, qsort_block, const void *, const void *); - -void -qsort_b(void *base, size_t nel, size_t width, qsort_block compar) -{ - qsort_r(base, nel, width, compar, - (int (*)(void *, const void *, const void *)) - GET_BLOCK_FUNCTION(compar)); -} diff --git a/lib/libc/stdlib/qsort_r.c b/lib/libc/stdlib/qsort_r_compat.c copy from lib/libc/stdlib/qsort_r.c copy to lib/libc/stdlib/qsort_r_compat.c --- a/lib/libc/stdlib/qsort_r.c +++ b/lib/libc/stdlib/qsort_r_compat.c @@ -5,7 +5,7 @@ * $FreeBSD$ */ #include "block_abi.h" -#define I_AM_QSORT_R +#define I_AM_QSORT_R_COMPAT #include "qsort.c" typedef DECLARE_BLOCK(int, qsort_block, const void *, const void *); @@ -13,7 +13,9 @@ void qsort_b(void *base, size_t nel, size_t width, qsort_block compar) { - qsort_r(base, nel, width, compar, + __qsort_r_compat(base, nel, width, compar, (int (*)(void *, const void *, const void *)) GET_BLOCK_FUNCTION(compar)); } + +__sym_compat(qsort_r, __qsort_r_compat, FBSD_1.0); diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile --- a/lib/libc/tests/stdlib/Makefile +++ b/lib/libc/tests/stdlib/Makefile @@ -8,6 +8,7 @@ ATF_TESTS_C+= mergesort_test ATF_TESTS_C+= qsort_test ATF_TESTS_C+= qsort_b_test +ATF_TESTS_C+= qsort_r_compat_test ATF_TESTS_C+= qsort_r_test ATF_TESTS_C+= qsort_s_test ATF_TESTS_C+= set_constraint_handler_s_test diff --git a/lib/libc/tests/stdlib/qsort_r_test.c b/lib/libc/tests/stdlib/qsort_r_compat_test.c copy from lib/libc/tests/stdlib/qsort_r_test.c copy to lib/libc/tests/stdlib/qsort_r_compat_test.c --- a/lib/libc/tests/stdlib/qsort_r_test.c +++ b/lib/libc/tests/stdlib/qsort_r_compat_test.c @@ -26,7 +26,7 @@ */ /* - * Test for qsort_r(3) routine. + * Test for historical qsort_r(3) routine. */ #include @@ -56,8 +56,8 @@ return (0); } -ATF_TC_WITHOUT_HEAD(qsort_r_test); -ATF_TC_BODY(qsort_r_test, tc) +ATF_TC_WITHOUT_HEAD(qsort_r_compat_test); +ATF_TC_BODY(qsort_r_compat_test, tc) { int testvector[IVEC_LEN]; int sresvector[IVEC_LEN]; @@ -86,7 +86,7 @@ ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, qsort_r_test); + ATF_TP_ADD_TC(tp, qsort_r_compat_test); return (atf_no_error()); } diff --git a/lib/libc/tests/stdlib/qsort_r_test.c b/lib/libc/tests/stdlib/qsort_r_test.c --- a/lib/libc/tests/stdlib/qsort_r_test.c +++ b/lib/libc/tests/stdlib/qsort_r_test.c @@ -40,7 +40,7 @@ #define THUNK 42 static int -sorthelp_r(void *thunk, const void *a, const void *b) +sorthelp_r(const void *a, const void *b, void *thunk) { const int *oa, *ob; @@ -70,8 +70,8 @@ testvector[i] = sresvector[i] = initvector[i]; /* Sort using qsort_r(3) */ - qsort_r(testvector, j, sizeof(testvector[0]), &thunk, - sorthelp_r); + qsort_r(testvector, j, sizeof(testvector[0]), sorthelp_r, + &thunk); /* Sort using reference slow sorting routine */ ssort(sresvector, j); diff --git a/lib/libproc/proc_sym.c b/lib/libproc/proc_sym.c --- a/lib/libproc/proc_sym.c +++ b/lib/libproc/proc_sym.c @@ -105,7 +105,7 @@ }; static int -symvalcmp(void *_thunk, const void *a1, const void *a2) +symvalcmp(const void *a1, const void *a2, void *_thunk) { GElf_Sym sym1, sym2; struct symsort_thunk *thunk; @@ -192,7 +192,7 @@ thunk.e = e; thunk.symtab = symtab; - qsort_r(symtab->index, nsyms, sizeof(u_int), &thunk, symvalcmp); + qsort_r(symtab->index, nsyms, sizeof(u_int), symvalcmp, &thunk); return (0); } diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c --- a/sys/compat/linuxkpi/common/src/linux_compat.c +++ b/sys/compat/linuxkpi/common/src/linux_compat.c @@ -2562,7 +2562,7 @@ }; static inline int -linux_le_cmp(void *priv, const void *d1, const void *d2) +linux_le_cmp(const void *d1, const void *d2, void *priv) { struct list_head *le1, *le2; struct list_sort_thunk *thunk; @@ -2590,7 +2590,7 @@ ar[i++] = le; thunk.cmp = cmp; thunk.priv = priv; - qsort_r(ar, count, sizeof(struct list_head *), &thunk, linux_le_cmp); + qsort_r(ar, count, sizeof(struct list_head *), linux_le_cmp, &thunk); INIT_LIST_HEAD(head); for (i = 0; i < count; i++) list_add_tail(ar[i], head); diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c b/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c --- a/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c +++ b/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c @@ -59,8 +59,7 @@ #include "bhnd_nvram_storevar.h" -static int bhnd_nvstore_idx_cmp(void *ctx, - const void *lhs, const void *rhs); +static int bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx); /** * Allocate and initialize a new path instance. @@ -198,7 +197,7 @@ /* sort function for bhnd_nvstore_index_prepare() */ static int -bhnd_nvstore_idx_cmp(void *ctx, const void *lhs, const void *rhs) +bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx) { struct bhnd_nvram_store *sc; void *l_cookiep, *r_cookiep; @@ -259,8 +258,8 @@ BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED); /* Sort the index table */ - qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]), sc, - bhnd_nvstore_idx_cmp); + qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]), + bhnd_nvstore_idx_cmp, sc); return (0); } diff --git a/sys/dev/drm2/drm_linux_list_sort.c b/sys/dev/drm2/drm_linux_list_sort.c --- a/sys/dev/drm2/drm_linux_list_sort.c +++ b/sys/dev/drm2/drm_linux_list_sort.c @@ -37,7 +37,7 @@ }; static int -drm_le_cmp(void *priv, const void *d1, const void *d2) +drm_le_cmp(const void *d1, const void *d2, void *priv) { struct list_head *le1, *le2; struct drm_list_sort_thunk *thunk; @@ -68,7 +68,7 @@ ar[i++] = le; thunk.cmp = cmp; thunk.priv = priv; - qsort_r(ar, count, sizeof(struct list_head *), &thunk, drm_le_cmp); + qsort_r(ar, count, sizeof(struct list_head *), drm_le_cmp, &thunk); INIT_LIST_HEAD(head); for (i = 0; i < count; i++) list_add_tail(ar[i], head); diff --git a/sys/libkern/qsort.c b/sys/libkern/qsort.c --- a/sys/libkern/qsort.c +++ b/sys/libkern/qsort.c @@ -36,7 +36,7 @@ #include #ifdef I_AM_QSORT_R -typedef int cmp_t(void *, const void *, const void *); +typedef int cmp_t(const void *, const void *, void *); #else typedef int cmp_t(const void *, const void *); #endif @@ -88,7 +88,7 @@ if ((n) > 0) swapfunc(a, b, n, swaptype_long, swaptype_int) #ifdef I_AM_QSORT_R -#define CMP(t, x, y) (cmp((t), (x), (y))) +#define CMP(t, x, y) (cmp((x), (y), (t))) #else #define CMP(t, x, y) (cmp((x), (y))) #endif @@ -107,7 +107,7 @@ #ifdef I_AM_QSORT_R void -qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +(qsort_r)(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) #else #define thunk NULL void @@ -192,7 +192,7 @@ /* Recurse on left partition, then iterate on right partition */ if (d1 > es) { #ifdef I_AM_QSORT_R - qsort_r(a, d1 / es, es, thunk, cmp); + qsort_r(a, d1 / es, es, cmp, thunk); #else qsort(a, d1 / es, es, cmp); #endif @@ -208,7 +208,7 @@ /* Recurse on right partition, then iterate on left partition */ if (d2 > es) { #ifdef I_AM_QSORT_R - qsort_r(pn - d2, d2 / es, es, thunk, cmp); + qsort_r(pn - d2, d2 / es, es, cmp, thunk); #else qsort(pn - d2, d2 / es, es, cmp); #endif diff --git a/sys/netgraph/ng_ppp.c b/sys/netgraph/ng_ppp.c --- a/sys/netgraph/ng_ppp.c +++ b/sys/netgraph/ng_ppp.c @@ -322,7 +322,7 @@ static void ng_ppp_frag_checkstale(node_p node); static void ng_ppp_frag_reset(node_p node); static void ng_ppp_mp_strategy(node_p node, int len, int *distrib); -static int ng_ppp_intcmp(void *latency, const void *v1, const void *v2); +static int ng_ppp_intcmp(const void *v1, const void *v2, void *latency); static struct mbuf *ng_ppp_addproto(struct mbuf *m, uint16_t proto, int compOK); static struct mbuf *ng_ppp_cutproto(struct mbuf *m, uint16_t *proto); static struct mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len); @@ -2316,8 +2316,8 @@ } /* Sort active links by latency */ - qsort_r(sortByLatency, - priv->numActiveLinks, sizeof(*sortByLatency), latency, ng_ppp_intcmp); + qsort_r(sortByLatency, priv->numActiveLinks, sizeof(*sortByLatency), + ng_ppp_intcmp, latency); /* Find the interval we need (add links in sortByLatency[] order) */ for (numFragments = 1; @@ -2401,7 +2401,7 @@ * Compare two integers */ static int -ng_ppp_intcmp(void *latency, const void *v1, const void *v2) +ng_ppp_intcmp(const void *v1, const void *v2, void *latency) { const int index1 = *((const int *) v1); const int index2 = *((const int *) v2); diff --git a/sys/sys/libkern.h b/sys/sys/libkern.h --- a/sys/sys/libkern.h +++ b/sys/sys/libkern.h @@ -163,8 +163,8 @@ void *memmem(const void *l, size_t l_len, const void *s, size_t s_len); void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); -void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, - int (*compar)(void *, const void *, const void *)); +void qsort_r(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *, void *), void *thunk); u_long random(void); int scanc(u_int, const u_char *, const u_char *, int); int strcasecmp(const char *, const char *);