Index: include/stdlib.h =================================================================== --- include/stdlib.h +++ include/stdlib.h @@ -339,6 +339,9 @@ errno_t); /* K3.6.1.3 */ void ignore_handler_s(const char * __restrict, void * __restrict, errno_t); +/* K.3.6.3.2 */ +errno_t qsort_s(void *, size_t, size_t, void *, + int (*)(const void *, const void *, void *)); #endif /* __EXT1_VISIBLE */ __END_DECLS Index: lib/libc/stdlib/Makefile.inc =================================================================== --- lib/libc/stdlib/Makefile.inc +++ lib/libc/stdlib/Makefile.inc @@ -11,7 +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 quick_exit.c \ + merge.c mergesort_b.c ptsname.c qsort.c qsort_r.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 \ @@ -51,7 +52,7 @@ MLINKS+=insque.3 remque.3 MLINKS+=lsearch.3 lfind.3 MLINKS+=ptsname.3 grantpt.3 ptsname.3 unlockpt.3 -MLINKS+=qsort.3 heapsort.3 qsort.3 mergesort.3 qsort.3 qsort_r.3 +MLINKS+=qsort.3 heapsort.3 qsort.3 mergesort.3 qsort.3 qsort_r.3 qsort_s.3 MLINKS+=rand.3 rand_r.3 rand.3 srand.3 MLINKS+=random.3 initstate.3 random.3 setstate.3 random.3 srandom.3 \ random.3 srandomdev.3 Index: lib/libc/stdlib/Symbol.map =================================================================== --- lib/libc/stdlib/Symbol.map +++ lib/libc/stdlib/Symbol.map @@ -120,6 +120,7 @@ __cxa_thread_atexit_impl; abort_handler_s; ignore_handler_s; + qsort_s; set_constraint_handler_s; }; Index: lib/libc/stdlib/qsort.3 =================================================================== --- lib/libc/stdlib/qsort.3 +++ lib/libc/stdlib/qsort.3 @@ -32,7 +32,7 @@ .\" @(#)qsort.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd February 20, 2013 +.Dd January 14, 2020 .Dt QSORT 3 .Os .Sh NAME @@ -98,6 +98,15 @@ .Fa "size_t size" .Fa "int \*[lp]^compar\*[rp]\*[lp]const void *, const void *\*[rp]" .Fc +.Fd #define __STDC_WANT_LIB_EXT1__ 1 +.Ft errno_t +.Fo qsort_s +.Fa "void *base" +.Fa "size_t nmemb" +.Fa "size_t size" +.Fa "void *thunk" +.Fa "int \*[lp]*compar\*[rp]\*[lp]const void *, const void *, void *\*[rp]" +.Fc .Sh DESCRIPTION The .Fn qsort @@ -238,6 +247,27 @@ .Fn heapsort . Memory availability and pre-existing order in the data can make this untrue. +.Pp +The +.Fn qsort_s +function behaves the same as +.Fn qsort_r +except the order of arguments to +.Fa compar +is changed, +and that an error is returned and the currently registered +runtime-constraint handler is called if +.Fa nmemb +or +.Fa size +are greater than +.Dv RSIZE_MAX , +or +.Fa nmemb +is not zero and +.Fa compar +is NULL. +The runtime-constraint handler is called first and may not return. .Sh RETURN VALUES The .Fn qsort @@ -245,6 +275,9 @@ .Fn qsort_r functions return no value. +The +.Fn qsort_s +function returns zero on success, non-zero on error. .Pp .Rv -std heapsort mergesort .Sh EXAMPLES @@ -288,6 +321,19 @@ } .Ed .Sh COMPATIBILITY +The order of arguments for the comparison function used with +.Fn qsort_r +is different from the one used by +.Fn qsort_s , +and the GNU libc implementation of +.Fn qsort_r . +When porting software written for GNU libc, it is usually possible +to replace +.Fn qsort_r +with +.Fn qsort_s +to work around this problem. +.Pp Previous versions of .Fn qsort did not permit the comparison routine itself to call @@ -366,6 +412,10 @@ function conforms to .St -isoC . +.Fn qsort_s +conforms to +.St -isoC-2011 +K.3.6.3.2. .Sh HISTORY The variants of these functions that take blocks as arguments first appeared in Mac OS X. Index: lib/libc/stdlib/qsort.c =================================================================== --- lib/libc/stdlib/qsort.c +++ lib/libc/stdlib/qsort.c @@ -35,10 +35,15 @@ #include __FBSDID("$FreeBSD$"); +#include +#include #include +#include "libc_private.h" -#ifdef I_AM_QSORT_R +#if defined(I_AM_QSORT_R) typedef int cmp_t(void *, const void *, const void *); +#elif defined(I_AM_QSORT_S) +typedef int cmp_t(const void *, const void *, void *); #else typedef int cmp_t(const void *, const void *); #endif @@ -65,15 +70,17 @@ #define vecswap(a, b, n) \ if ((n) > 0) swapfunc(a, b, n) -#ifdef I_AM_QSORT_R +#if defined(I_AM_QSORT_R) #define CMP(t, x, y) (cmp((t), (x), (y))) +#elif defined(I_AM_QSORT_S) +#define CMP(t, x, y) (cmp((x), (y), (t))) #else #define CMP(t, x, y) (cmp((x), (y))) #endif static inline char * med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk -#ifndef I_AM_QSORT_R +#if !defined(I_AM_QSORT_R) && !defined(I_AM_QSORT_S) __unused #endif ) @@ -83,9 +90,12 @@ :(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c )); } -#ifdef I_AM_QSORT_R +#if defined(I_AM_QSORT_R) void qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +#elif defined(I_AM_QSORT_S) +errno_t +qsort_s(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) #else #define thunk NULL void @@ -97,6 +107,24 @@ int cmp_result; int swap_cnt; +#ifdef I_AM_QSORT_S + if (n > RSIZE_MAX) { + __throw_constraint_handler_s("qsort_s : n > RSIZE_MAX", EINVAL); + return (EINVAL); + } else if (es > RSIZE_MAX) { + __throw_constraint_handler_s("qsort_s : es > RSIZE_MAX", EINVAL); + return (EINVAL); + } else if (n != 0) { + if (a == NULL) { + __throw_constraint_handler_s("qsort_s : a == NULL", EINVAL); + return (EINVAL); + } else if (cmp == NULL) { + __throw_constraint_handler_s("qsort_s : cmp == NULL", EINVAL); + return (EINVAL); + } + } +#endif + loop: swap_cnt = 0; if (n < 7) { @@ -105,7 +133,11 @@ pl > (char *)a && CMP(thunk, pl - es, pl) > 0; pl -= es) swapfunc(pl, pl - es, es); +#ifdef I_AM_QSORT_S + return (0); +#else return; +#endif } pm = (char *)a + (n / 2) * es; if (n > 7) { @@ -154,7 +186,11 @@ pl > (char *)a && CMP(thunk, pl - es, pl) > 0; pl -= es) swapfunc(pl, pl - es, es); +#ifdef I_AM_QSORT_S + return (0); +#else return; +#endif } pn = (char *)a + n * es; @@ -168,8 +204,10 @@ if (d1 <= d2) { /* Recurse on left partition, then iterate on right partition */ if (d1 > es) { -#ifdef I_AM_QSORT_R +#if defined(I_AM_QSORT_R) qsort_r(a, d1 / es, es, thunk, cmp); +#elif defined(I_AM_QSORT_S) + qsort_s(a, d1 / es, es, thunk, cmp); #else qsort(a, d1 / es, es, cmp); #endif @@ -184,8 +222,10 @@ } else { /* Recurse on right partition, then iterate on left partition */ if (d2 > es) { -#ifdef I_AM_QSORT_R +#if defined(I_AM_QSORT_R) qsort_r(pn - d2, d2 / es, es, thunk, cmp); +#elif defined(I_AM_QSORT_S) + qsort_s(pn - d2, d2 / es, es, thunk, cmp); #else qsort(pn - d2, d2 / es, es, cmp); #endif @@ -197,4 +237,8 @@ goto loop; } } + +#ifdef I_AM_QSORT_S + return (0); +#endif } Index: lib/libc/stdlib/qsort_s.c =================================================================== --- lib/libc/stdlib/qsort_s.c +++ lib/libc/stdlib/qsort_s.c @@ -2,18 +2,8 @@ * This file is in the public domain. Originally written by Garrett * A. Wollman. * - * $FreeBSD: head/lib/libc/stdlib/qsort_r.c 264143 2014-04-05 08:17:48Z theraven $ + * $FreeBSD: head/lib/libc/stdlib/qsort_s.c 264143 2014-04-05 08:17:48Z theraven $ */ #include "block_abi.h" -#define I_AM_QSORT_R +#define I_AM_QSORT_S #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)); -}