Module Name: src Committed By: riastradh Date: Sun Mar 2 16:35:41 UTC 2025
Modified Files: src/common/lib/libc/stdlib: heapsort.c src/distrib/sets/lists/comp: mi src/distrib/sets/lists/debug: mi src/distrib/sets/lists/tests: mi src/include: stdlib.h src/lib/libc/include: namespace.h src/lib/libc/stdlib: Makefile.inc merge.c qsort.3 qsort.c src/sys/lib/libkern: libkern.h src/tests/lib/libc/stdlib: Makefile src/tools/compat: compat_defs.h Added Files: src/tests/lib/libc/stdlib: h_sort.c t_sort.sh Log Message: libc: New _r variants of heapsort, mergesort, qsort. Also kheapsort_r for kernel/standalone use. These variants allow the caller to pass a cookie through to the comparison function, e.g. if you want to sort an array of indices into a buffer. qsort_r is new in POSIX.1-2024; the others are obvious analogues of our nonstandard extensions for heapsort and mergesort. PR lib/58931: qsort_r() missing To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/common/lib/libc/stdlib/heapsort.c cvs rdiff -u -r1.2485 -r1.2486 src/distrib/sets/lists/comp/mi cvs rdiff -u -r1.466 -r1.467 src/distrib/sets/lists/debug/mi cvs rdiff -u -r1.1359 -r1.1360 src/distrib/sets/lists/tests/mi cvs rdiff -u -r1.129 -r1.130 src/include/stdlib.h cvs rdiff -u -r1.205 -r1.206 src/lib/libc/include/namespace.h cvs rdiff -u -r1.103 -r1.104 src/lib/libc/stdlib/Makefile.inc cvs rdiff -u -r1.16 -r1.17 src/lib/libc/stdlib/merge.c cvs rdiff -u -r1.14 -r1.15 src/lib/libc/stdlib/qsort.3 cvs rdiff -u -r1.23 -r1.24 src/lib/libc/stdlib/qsort.c cvs rdiff -u -r1.147 -r1.148 src/sys/lib/libkern/libkern.h cvs rdiff -u -r1.34 -r1.35 src/tests/lib/libc/stdlib/Makefile cvs rdiff -u -r0 -r1.1 src/tests/lib/libc/stdlib/h_sort.c \ src/tests/lib/libc/stdlib/t_sort.sh cvs rdiff -u -r1.123 -r1.124 src/tools/compat/compat_defs.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/common/lib/libc/stdlib/heapsort.c diff -u src/common/lib/libc/stdlib/heapsort.c:1.3 src/common/lib/libc/stdlib/heapsort.c:1.4 --- src/common/lib/libc/stdlib/heapsort.c:1.3 Mon Nov 17 10:21:30 2008 +++ src/common/lib/libc/stdlib/heapsort.c Sun Mar 2 16:35:40 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: heapsort.c,v 1.3 2008/11/17 10:21:30 jnemeth Exp $ */ +/* $NetBSD: heapsort.c,v 1.4 2025/03/02 16:35:40 riastradh Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -39,6 +39,7 @@ * XXX rename the versions found in the host's headers by mistake! */ #undef heapsort +#undef heapsort_r #endif #include <sys/cdefs.h> @@ -46,7 +47,7 @@ #if 0 static char sccsid[] = "from: @(#)heapsort.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: heapsort.c,v 1.3 2008/11/17 10:21:30 jnemeth Exp $"); +__RCSID("$NetBSD: heapsort.c,v 1.4 2025/03/02 16:35:40 riastradh Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -65,10 +66,12 @@ __RCSID("$NetBSD: heapsort.c,v 1.3 2008/ #if HAVE_NBTOOL_CONFIG_H /* XXX Now, re-apply the renaming that we undid above. */ #define heapsort __nbcompat_heapsort +#define heapsort_r __nbcompat_heapsort_r #endif #ifdef __weak_alias __weak_alias(heapsort,_heapsort) +__weak_alias(heapsort_r,_heapsort_r) #endif #endif /* _KERNEL || _STANDALONE */ @@ -109,12 +112,13 @@ __weak_alias(heapsort,_heapsort) for (par_i = initval; (child_i = par_i * 2) <= nmemb; \ par_i = child_i) { \ child = base + child_i * size; \ - if (child_i < nmemb && compar(child, child + size) < 0) { \ + if (child_i < nmemb && \ + compar(child, child + size, cookie) < 0) { \ child += size; \ ++child_i; \ } \ par = base + par_i * size; \ - if (compar(child, par) <= 0) \ + if (compar(child, par, cookie) <= 0) \ break; \ SWAP(par, child, count, size, tmp); \ } \ @@ -140,7 +144,8 @@ __weak_alias(heapsort,_heapsort) #define SELECT(par_i, child_i, nmemb, par, child, size, k, count, tmp1, tmp2) { \ for (par_i = 1; (child_i = par_i * 2) <= nmemb; par_i = child_i) { \ child = base + child_i * size; \ - if (child_i < nmemb && compar(child, child + size) < 0) { \ + if (child_i < nmemb && \ + compar(child, child + size, cookie) < 0) { \ child += size; \ ++child_i; \ } \ @@ -152,7 +157,7 @@ __weak_alias(heapsort,_heapsort) par_i = child_i / 2; \ child = base + child_i * size; \ par = base + par_i * size; \ - if (child_i == 1 || compar(k, par) < 0) { \ + if (child_i == 1 || compar(k, par, cookie) < 0) { \ COPY(child, k, count, size, tmp1, tmp2); \ break; \ } \ @@ -169,12 +174,13 @@ __weak_alias(heapsort,_heapsort) */ #if defined(_KERNEL) || defined(_STANDALONE) int -kheapsort(void *vbase, size_t nmemb, size_t size, - int (*compar)(const void *, const void *), void *k) +kheapsort_r(void *vbase, size_t nmemb, size_t size, + int (*compar)(const void *, const void *, void *), void *cookie, + void *k) #else int -heapsort(void *vbase, size_t nmemb, size_t size, - int (*compar)(const void *, const void *)) +heapsort_r(void *vbase, size_t nmemb, size_t size, + int (*compar)(const void *, const void *, void *), void *cookie) #endif { size_t cnt, i, j, l; @@ -227,3 +233,29 @@ heapsort(void *vbase, size_t nmemb, size #endif return (0); } + +static int +cmpnocookie(const void *a, const void *b, void *cookie) +{ + int (*cmp)(const void *, const void *) = cookie; + + return cmp(a, b); +} + +int +#if defined(_KERNEL) || defined(_STANDALONE) +kheapsort(void *a, size_t n, size_t es, + int (*cmp)(const void *, const void *), + void *k) +#else +heapsort(void *a, size_t n, size_t es, + int (*cmp)(const void *, const void *)) +#endif +{ + +#if defined(_KERNEL) || defined(_STANDALONE) + return kheapsort_r(a, n, es, cmpnocookie, cmp, k); +#else + return heapsort_r(a, n, es, cmpnocookie, cmp); +#endif +} Index: src/distrib/sets/lists/comp/mi diff -u src/distrib/sets/lists/comp/mi:1.2485 src/distrib/sets/lists/comp/mi:1.2486 --- src/distrib/sets/lists/comp/mi:1.2485 Sun Jan 26 16:35:42 2025 +++ src/distrib/sets/lists/comp/mi Sun Mar 2 16:35:40 2025 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.2485 2025/01/26 16:35:42 christos Exp $ +# $NetBSD: mi,v 1.2486 2025/03/02 16:35:40 riastradh Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. ./etc/mtree/set.comp comp-sys-root @@ -8183,6 +8183,7 @@ ./usr/share/man/cat3/hdestroy1_r.0 comp-c-catman .cat ./usr/share/man/cat3/hdestroy_r.0 comp-c-catman .cat ./usr/share/man/cat3/heapsort.0 comp-c-catman .cat +./usr/share/man/cat3/heapsort_r.0 comp-c-catman .cat ./usr/share/man/cat3/herror.0 comp-c-catman .cat ./usr/share/man/cat3/hesiod.0 comp-c-catman hesiod,.cat ./usr/share/man/cat3/hesiod_end.0 comp-c-catman hesiod,.cat @@ -9292,6 +9293,7 @@ ./usr/share/man/cat3/menu_win.0 comp-c-catman .cat ./usr/share/man/cat3/menus.0 comp-c-catman .cat ./usr/share/man/cat3/mergesort.0 comp-c-catman .cat +./usr/share/man/cat3/mergesort_r.0 comp-c-catman .cat ./usr/share/man/cat3/meta.0 comp-c-catman .cat ./usr/share/man/cat3/mi_vector_hash.0 comp-c-catman .cat ./usr/share/man/cat3/minor.0 comp-c-catman .cat @@ -10149,6 +10151,7 @@ ./usr/share/man/cat3/qdiv.0 comp-c-catman .cat ./usr/share/man/cat3/qiflush.0 comp-c-catman .cat ./usr/share/man/cat3/qsort.0 comp-c-catman .cat +./usr/share/man/cat3/qsort_r.0 comp-c-catman .cat ./usr/share/man/cat3/queue.0 comp-c-catman .cat ./usr/share/man/cat3/quick_exit.0 comp-c-catman .cat ./usr/share/man/cat3/quota_close.0 comp-c-catman .cat @@ -16723,6 +16726,7 @@ ./usr/share/man/html3/hdestroy1_r.html comp-c-htmlman html ./usr/share/man/html3/hdestroy_r.html comp-c-htmlman html ./usr/share/man/html3/heapsort.html comp-c-htmlman html +./usr/share/man/html3/heapsort_r.html comp-c-htmlman html ./usr/share/man/html3/herror.html comp-c-htmlman html ./usr/share/man/html3/hesiod.html comp-c-htmlman hesiod,html ./usr/share/man/html3/hesiod_end.html comp-c-htmlman hesiod,html @@ -17800,6 +17804,7 @@ ./usr/share/man/html3/menu_win.html comp-c-htmlman html ./usr/share/man/html3/menus.html comp-c-htmlman html ./usr/share/man/html3/mergesort.html comp-c-htmlman html +./usr/share/man/html3/mergesort_r.html comp-c-htmlman html ./usr/share/man/html3/meta.html comp-c-htmlman html ./usr/share/man/html3/mi_vector_hash.html comp-c-htmlman html ./usr/share/man/html3/minor.html comp-c-htmlman html @@ -18674,6 +18679,7 @@ ./usr/share/man/html3/qdiv.html comp-c-htmlman html ./usr/share/man/html3/qiflush.html comp-c-htmlman html ./usr/share/man/html3/qsort.html comp-c-htmlman html +./usr/share/man/html3/qsort_r.html comp-c-htmlman html ./usr/share/man/html3/queue.html comp-c-htmlman html ./usr/share/man/html3/quick_exit.html comp-c-htmlman html ./usr/share/man/html3/quota_close.html comp-c-htmlman html @@ -25207,6 +25213,7 @@ ./usr/share/man/man3/hdestroy1_r.3 comp-c-man .man ./usr/share/man/man3/hdestroy_r.3 comp-c-man .man ./usr/share/man/man3/heapsort.3 comp-c-man .man +./usr/share/man/man3/heapsort_r.3 comp-c-man .man ./usr/share/man/man3/herror.3 comp-c-man .man ./usr/share/man/man3/hesiod.3 comp-c-man hesiod,.man ./usr/share/man/man3/hesiod_end.3 comp-c-man hesiod,.man @@ -26317,6 +26324,7 @@ ./usr/share/man/man3/menu_win.3 comp-c-man .man ./usr/share/man/man3/menus.3 comp-c-man .man ./usr/share/man/man3/mergesort.3 comp-c-man .man +./usr/share/man/man3/mergesort_r.3 comp-c-man .man ./usr/share/man/man3/meta.3 comp-c-man .man ./usr/share/man/man3/mi_vector_hash.3 comp-c-man .man ./usr/share/man/man3/minor.3 comp-c-man .man @@ -27197,6 +27205,7 @@ ./usr/share/man/man3/qdiv.3 comp-c-man .man ./usr/share/man/man3/qiflush.3 comp-c-man .man ./usr/share/man/man3/qsort.3 comp-c-man .man +./usr/share/man/man3/qsort_r.3 comp-c-man .man ./usr/share/man/man3/queue.3 comp-c-man .man ./usr/share/man/man3/quick_exit.3 comp-c-man .man ./usr/share/man/man3/quota_close.3 comp-c-man .man Index: src/distrib/sets/lists/debug/mi diff -u src/distrib/sets/lists/debug/mi:1.466 src/distrib/sets/lists/debug/mi:1.467 --- src/distrib/sets/lists/debug/mi:1.466 Thu Feb 27 00:55:31 2025 +++ src/distrib/sets/lists/debug/mi Sun Mar 2 16:35:40 2025 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.466 2025/02/27 00:55:31 riastradh Exp $ +# $NetBSD: mi,v 1.467 2025/03/02 16:35:40 riastradh Exp $ # ./etc/mtree/set.debug comp-sys-root ./usr/lib comp-sys-usr compatdir @@ -2162,6 +2162,7 @@ ./usr/libdata/debug/usr/tests/lib/libc/stdlib/h_atexit.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/stdlib/h_getopt.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/stdlib/h_getopt_long.debug tests-lib-debug debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/stdlib/h_sort.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/stdlib/t_a64l.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/stdlib/t_abs.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/stdlib/t_atoi.debug tests-lib-debug debug,atf,compattestfile Index: src/distrib/sets/lists/tests/mi diff -u src/distrib/sets/lists/tests/mi:1.1359 src/distrib/sets/lists/tests/mi:1.1360 --- src/distrib/sets/lists/tests/mi:1.1359 Thu Feb 27 00:55:31 2025 +++ src/distrib/sets/lists/tests/mi Sun Mar 2 16:35:40 2025 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1359 2025/02/27 00:55:31 riastradh Exp $ +# $NetBSD: mi,v 1.1360 2025/03/02 16:35:40 riastradh Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -3267,6 +3267,7 @@ ./usr/tests/lib/libc/stdlib/h_atexit tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/h_getopt tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/h_getopt_long tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/stdlib/h_sort tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/t_a64l tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/t_abs tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/t_atexit tests-lib-tests compattestfile,atf @@ -3284,6 +3285,7 @@ ./usr/tests/lib/libc/stdlib/t_mktemp tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/t_posix_memalign tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/t_random tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/stdlib/t_sort tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/t_strtod tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/t_strtoi tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/stdlib/t_strtol tests-lib-tests compattestfile,atf Index: src/include/stdlib.h diff -u src/include/stdlib.h:1.129 src/include/stdlib.h:1.130 --- src/include/stdlib.h:1.129 Mon Feb 17 17:04:15 2025 +++ src/include/stdlib.h Sun Mar 2 16:35:40 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: stdlib.h,v 1.129 2025/02/17 17:04:15 nia Exp $ */ +/* $NetBSD: stdlib.h,v 1.130 2025/03/02 16:35:40 riastradh Exp $ */ /*- * Copyright (c) 1990, 1993 @@ -317,8 +317,12 @@ int getenv_r(const char *, char *, size void cfree(void *); int heapsort(void *, size_t, size_t, int (*)(const void *, const void *)); +int heapsort_r(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); int mergesort(void *, size_t, size_t, int (*)(const void *, const void *)); +int mergesort_r(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); int ptsname_r(int, char *, size_t); int radixsort(const unsigned char **, int, const unsigned char *, unsigned); @@ -400,6 +404,8 @@ void *reallocarray(void *, size_t, size_ #if (_POSIX_C_SOURCE - 0) >= 202405L || defined(_NETBSD_SOURCE) int mkostemp(char *, int); +void qsort_r(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); #endif /* _POSIX_C_SOURCE >= 202405L || _NETBSD_SOURCE */ __END_DECLS Index: src/lib/libc/include/namespace.h diff -u src/lib/libc/include/namespace.h:1.205 src/lib/libc/include/namespace.h:1.206 --- src/lib/libc/include/namespace.h:1.205 Sat Aug 17 21:24:53 2024 +++ src/lib/libc/include/namespace.h Sun Mar 2 16:35:41 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: namespace.h,v 1.205 2024/08/17 21:24:53 riastradh Exp $ */ +/* $NetBSD: namespace.h,v 1.206 2025/03/02 16:35:41 riastradh Exp $ */ /*- * Copyright (c) 1997-2004 The NetBSD Foundation, Inc. @@ -437,6 +437,7 @@ #define gmtime_r _gmtime_r #define group_from_gid _group_from_gid #define heapsort _heapsort +#define heapsort_r _heapsort_r #define herror _herror #define hes_error _hes_error #define hes_free _hes_free @@ -521,6 +522,7 @@ #define mbrtoc8_l _mbrtoc8_l #define membar_producer _membar_producer #define mergesort _mergesort +#define mergesort_r _mergesort_r #define mi_vector_hash _mi_vector_hash #define mkstemp _mkstemp #define mktime_z _mktime_z Index: src/lib/libc/stdlib/Makefile.inc diff -u src/lib/libc/stdlib/Makefile.inc:1.103 src/lib/libc/stdlib/Makefile.inc:1.104 --- src/lib/libc/stdlib/Makefile.inc:1.103 Mon Sep 23 15:49:42 2024 +++ src/lib/libc/stdlib/Makefile.inc Sun Mar 2 16:35:41 2025 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.inc,v 1.103 2024/09/23 15:49:42 christos Exp $ +# $NetBSD: Makefile.inc,v 1.104 2025/03/02 16:35:41 riastradh Exp $ # from: @(#)Makefile.inc 8.3 (Berkeley) 2/4/95 # stdlib sources @@ -88,6 +88,9 @@ MLINKS+=lsearch.3 lfind.3 MLINKS+=malloc.3 calloc.3 malloc.3 realloc.3 malloc.3 free.3 MLINKS+=posix_memalign.3 aligned_alloc.3 MLINKS+=qsort.3 heapsort.3 qsort.3 mergesort.3 +MLINKS+=qsort.3 heapsort_r.3 +MLINKS+=qsort.3 mergesort_r.3 +MLINKS+=qsort.3 qsort_r.3 MLINKS+=ptsname.3 ptsname_r.3 MLINKS+=rand.3 rand_r.3 MLINKS+=rand.3 srand.3 Index: src/lib/libc/stdlib/merge.c diff -u src/lib/libc/stdlib/merge.c:1.16 src/lib/libc/stdlib/merge.c:1.17 --- src/lib/libc/stdlib/merge.c:1.16 Wed May 23 21:21:27 2018 +++ src/lib/libc/stdlib/merge.c Sun Mar 2 16:35:41 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: merge.c,v 1.16 2018/05/23 21:21:27 joerg Exp $ */ +/* $NetBSD: merge.c,v 1.17 2025/03/02 16:35:41 riastradh Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "from: @(#)merge.c 8.2 (Berkeley) 2/14/94"; #else -__RCSID("$NetBSD: merge.c,v 1.16 2018/05/23 21:21:27 joerg Exp $"); +__RCSID("$NetBSD: merge.c,v 1.17 2025/03/02 16:35:41 riastradh Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -65,12 +65,13 @@ __RCSID("$NetBSD: merge.c,v 1.16 2018/05 #ifdef __weak_alias __weak_alias(mergesort,_mergesort) +__weak_alias(mergesort_r,_mergesort_r) #endif static void setup(u_char *, u_char *, size_t, size_t, - int (*)(const void *, const void *)); + int (*)(const void *, const void *, void *), void *); static void insertionsort(u_char *, size_t, size_t, - int (*)(const void *, const void *)); + int (*)(const void *, const void *, void *), void *); #define ISIZE sizeof(int) #define PSIZE sizeof(u_char *) @@ -93,7 +94,7 @@ static void insertionsort(u_char *, size do \ *dst++ = *src++; \ while (i -= 1) - + /* * Find the next possible pointer head. (Trickery for forcing an array * to do double duty as a linked list when objects do not align with word @@ -107,8 +108,8 @@ static void insertionsort(u_char *, size * Arguments are as for qsort. */ int -mergesort(void *base, size_t nmemb, size_t size, - int (*cmp)(const void *, const void *)) +mergesort_r(void *base, size_t nmemb, size_t size, + int (*cmp)(const void *, const void *, void *), void *cookie) { size_t i; int sense; @@ -139,7 +140,7 @@ mergesort(void *base, size_t nmemb, size return (-1); list1 = base; - setup(list1, list2, nmemb, size, cmp); + setup(list1, list2, nmemb, size, cmp, cookie); last = list2 + nmemb * size; i = big = 0; while (*EVAL(list2) != last) { @@ -153,7 +154,7 @@ mergesort(void *base, size_t nmemb, size p2 = *EVAL(p2); l2 = list1 + (p2 - list2); while (f1 < l1 && f2 < l2) { - if ((*cmp)(f1, f2) <= 0) { + if ((*cmp)(f1, f2, cookie) <= 0) { q = f2; b = f1, t = l1; sense = -1; @@ -166,7 +167,8 @@ mergesort(void *base, size_t nmemb, size #if 0 LINEAR: #endif - while ((b += size) < t && cmp(q, b) >sense) + while ((b += size) < t && + cmp(q, b, cookie) >sense) if (++i == 6) { big = 1; goto EXPONENTIAL; @@ -175,15 +177,17 @@ LINEAR: EXPONENTIAL: for (i = size; ; i <<= 1) if ((p = (b + i)) >= t) { if ((p = t - size) > b && - (*cmp)(q, p) <= sense) + ((*cmp)(q, p, cookie) <= + sense)) t = p; else b = p; break; - } else if ((*cmp)(q, p) <= sense) { + } else if ((*cmp)(q, p, cookie) <= + sense) { t = p; if (i == size) - big = 0; + big = 0; goto FASTCASE; } else b = p; @@ -192,19 +196,21 @@ SLOWCASE: #endif while (t > b+size) { i = (((t - b) / size) >> 1) * size; - if ((*cmp)(q, p = b + i) <= sense) + if ((*cmp)(q, p = b + i, cookie) <= + sense) t = p; else b = p; } goto COPY; -FASTCASE: while (i > size) - if ((*cmp)(q, - p = b + - (i = (unsigned int) i >> 1)) <= sense) +FASTCASE: while (i > size) { + i = (unsigned int)i >> 1; + p = b + i; + if ((*cmp)(q, p, cookie) <= sense) t = p; else b = p; + } COPY: b = t; } i = size; @@ -277,11 +283,9 @@ COPY: b = t; * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL * is defined. Otherwise simple pairwise merging is used.) */ - -/* XXX: shouldn't this function be static? - lukem 990810 */ -void +static void setup(u_char *list1, u_char *list2, size_t n, size_t size, - int (*cmp)(const void *, const void *)) + int (*cmp)(const void *, const void *, void *), void *cookie) { int length, tmp, sense; u_char *f1, *f2, *s, *l2, *last, *p2; @@ -293,7 +297,7 @@ setup(u_char *list1, u_char *list2, size size2 = size*2; if (n <= 5) { - insertionsort(list1, n, size, cmp); + insertionsort(list1, n, size, cmp, cookie); *EVAL(list2) = list2 + n*size; return; } @@ -302,19 +306,19 @@ setup(u_char *list1, u_char *list2, size * for simplicity. */ i = 4 + (n & 1); - insertionsort(list1 + (n - i) * size, i, size, cmp); + insertionsort(list1 + (n - i) * size, i, size, cmp, cookie); last = list1 + size * (n - i); *EVAL(list2 + (last - list1)) = list2 + n * size; #ifdef NATURAL p2 = list2; f1 = list1; - sense = (cmp(f1, f1 + size) > 0); + sense = (cmp(f1, f1 + size, cookie) > 0); for (; f1 < last; sense = !sense) { length = 2; /* Find pairs with same sense. */ for (f2 = f1 + size2; f2 < last; f2 += size2) { - if ((cmp(f2, f2+ size) > 0) != sense) + if ((cmp(f2, f2 + size, cookie) > 0) != sense) break; length += 2; } @@ -327,7 +331,7 @@ setup(u_char *list1, u_char *list2, size } else { /* Natural merge */ l2 = f2; for (f2 = f1 + size2; f2 < l2; f2 += size2) { - if ((cmp(f2-size, f2) > 0) != sense) { + if ((cmp(f2-size, f2, cookie) > 0) != sense) { p2 = *EVAL(p2) = f2 - list1 + list2; if (sense > 0) reverse(f1, f2 - size); @@ -337,7 +341,7 @@ setup(u_char *list1, u_char *list2, size if (sense > 0) reverse(f1, f2 - size); f1 = f2; - if (f2 < last || cmp(f2 - size, f2) > 0) + if (f2 < last || cmp(f2 - size, f2, cookie) > 0) p2 = *EVAL(p2) = f2 - list1 + list2; else p2 = *EVAL(p2) = list2 + n*size; @@ -346,7 +350,7 @@ setup(u_char *list1, u_char *list2, size #else /* pairwise merge only. */ for (f1 = list1, p2 = list2; f1 < last; f1 += size2) { p2 = *EVAL(p2) = p2 + size2; - if (cmp (f1, f1 + size) > 0) + if (cmp(f1, f1 + size, cookie) > 0) swap(f1, f1 + size); } #endif /* NATURAL */ @@ -358,7 +362,7 @@ setup(u_char *list1, u_char *list2, size */ static void insertionsort(u_char *a, size_t n, size_t size, - int (*cmp)(const void *, const void *)) + int (*cmp)(const void *, const void *, void *), void *cookie) { u_char *ai, *s, *t, *u, tmp; size_t i; @@ -369,8 +373,24 @@ insertionsort(u_char *a, size_t n, size_ for (ai = a+size; --n >= 1; ai += size) for (t = ai; t > a; t -= size) { u = t - size; - if (cmp(u, t) <= 0) + if (cmp(u, t, cookie) <= 0) break; swap(u, t); } } + +static int +cmpnocookie(const void *a, const void *b, void *cookie) +{ + int (*cmp)(const void *, const void *) = cookie; + + return cmp(a, b); +} + +int +mergesort(void *a, size_t n, size_t es, + int (*cmp)(const void *, const void *)) +{ + + return mergesort_r(a, n, es, cmpnocookie, cmp); +} Index: src/lib/libc/stdlib/qsort.3 diff -u src/lib/libc/stdlib/qsort.3:1.14 src/lib/libc/stdlib/qsort.3:1.15 --- src/lib/libc/stdlib/qsort.3:1.14 Fri Sep 19 16:02:58 2014 +++ src/lib/libc/stdlib/qsort.3 Sun Mar 2 16:35:41 2025 @@ -1,4 +1,4 @@ -.\" $NetBSD: qsort.3,v 1.14 2014/09/19 16:02:58 wiz Exp $ +.\" $NetBSD: qsort.3,v 1.15 2025/03/02 16:35:41 riastradh Exp $ .\" .\" Copyright (c) 1990, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -47,10 +47,16 @@ .In stdlib.h .Ft void .Fn qsort "void *base" "size_t nmemb" "size_t size" "int (*compar)(const void *, const void *)" +.Ft void +.Fn qsort_r "void *base" "size_t nmemb" "size_t size" "int (*compar)(const void *, const void *, void *)" "void *cookie" .Ft int .Fn heapsort "void *base" "size_t nmemb" "size_t size" "int (*compar)(const void *, const void *)" .Ft int +.Fn heapsort_r "void *base" "size_t nmemb" "size_t size" "int (*compar)(const void *, const void *, void *)" "void *cookie" +.Ft int .Fn mergesort "void *base" "size_t nmemb" "size_t size" "int (*compar)(const void *, const void *)" +.Ft int +.Fn mergesort_r "void *base" "size_t nmemb" "size_t size" "int (*compar)(const void *, const void *, void *)" "void *cookie" .Sh DESCRIPTION The .Fn qsort @@ -106,6 +112,14 @@ The function is stable. .Pp The +.Fn qsort_r , +.Fn heapsort_r , +and +.Fn mergesort_r +functions pass an additional cookie argument through to the comparison +function. +.Pp +The .Fn qsort function is an implementation of C.A.R. Hoare's ``quicksort'' algorithm, a variant of partition-exchange sorting; in particular, see D.E. Knuth's @@ -233,6 +247,10 @@ were unable to allocate memory. .Sh STANDARDS The .Fn qsort -function -conforms to +function conforms to .St -ansiC . +.Pp +The +.Fn qsort_r +function conforms to +.St -p1003.1-2024 . Index: src/lib/libc/stdlib/qsort.c diff -u src/lib/libc/stdlib/qsort.c:1.23 src/lib/libc/stdlib/qsort.c:1.24 --- src/lib/libc/stdlib/qsort.c:1.23 Fri May 19 19:48:19 2017 +++ src/lib/libc/stdlib/qsort.c Sun Mar 2 16:35:41 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: qsort.c,v 1.23 2017/05/19 19:48:19 christos Exp $ */ +/* $NetBSD: qsort.c,v 1.24 2025/03/02 16:35:41 riastradh Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -33,7 +33,7 @@ #if 0 static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: qsort.c,v 1.23 2017/05/19 19:48:19 christos Exp $"); +__RCSID("$NetBSD: qsort.c,v 1.24 2025/03/02 16:35:41 riastradh Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -44,7 +44,8 @@ __RCSID("$NetBSD: qsort.c,v 1.23 2017/05 #include <stdlib.h> static inline char *med3(char *, char *, char *, - int (*)(const void *, const void *)); + int (*)(const void *, const void *, void *), + void *); static inline void swapfunc(char *, char *, size_t, int); #define min(a, b) (a) < (b) ? a : b @@ -70,7 +71,7 @@ static inline void swapfunc(char *a, char *b, size_t n, int swaptype) { - if (swaptype <= 1) + if (swaptype <= 1) swapcode(long, a, b, n) else swapcode(char, a, b, n) @@ -88,17 +89,17 @@ swapfunc(char *a, char *b, size_t n, int static inline char * med3(char *a, char *b, char *c, - int (*cmp)(const void *, const void *)) + int (*cmp)(const void *, const void *, void *), void *cookie) { - return cmp(a, b) < 0 ? - (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) - :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); + return cmp(a, b, cookie) < 0 ? + (cmp(b, c, cookie) < 0 ? b : (cmp(a, c, cookie) < 0 ? c : a )) + :(cmp(b, c, cookie) > 0 ? b : (cmp(a, c, cookie) < 0 ? a : c )); } void -qsort(void *a, size_t n, size_t es, - int (*cmp)(const void *, const void *)) +qsort_r(void *a, size_t n, size_t es, + int (*cmp)(const void *, const void *, void *), void *cookie) { char *pa, *pb, *pc, *pd, *pl, *pm, *pn; size_t d, r, s; @@ -110,7 +111,8 @@ qsort(void *a, size_t n, size_t es, loop: SWAPINIT(a, es); if (n < 7) { for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) - for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + for (pl = pm; + pl > (char *) a && cmp(pl - es, pl, cookie) > 0; pl -= es) swap(pl, pl - es); return; @@ -121,25 +123,25 @@ loop: SWAPINIT(a, es); pn = (char *) a + (n - 1) * es; if (n > 40) { d = (n / 8) * es; - pl = med3(pl, pl + d, pl + 2 * d, cmp); - pm = med3(pm - d, pm, pm + d, cmp); - pn = med3(pn - 2 * d, pn - d, pn, cmp); + pl = med3(pl, pl + d, pl + 2 * d, cmp, cookie); + pm = med3(pm - d, pm, pm + d, cmp, cookie); + pn = med3(pn - 2 * d, pn - d, pn, cmp, cookie); } - pm = med3(pl, pm, pn, cmp); + pm = med3(pl, pm, pn, cmp, cookie); } swap(a, pm); pa = pb = (char *) a + es; pc = pd = (char *) a + (n - 1) * es; for (;;) { - while (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) { + while (pb <= pc && (cmp_result = cmp(pb, a, cookie)) <= 0) { if (cmp_result == 0) { swap(pa, pb); pa += es; } pb += es; } - while (pb <= pc && (cmp_result = cmp(pc, a)) >= 0) { + while (pb <= pc && (cmp_result = cmp(pc, a, cookie)) >= 0) { if (cmp_result == 0) { swap(pc, pd); pd -= es; @@ -168,7 +170,7 @@ loop: SWAPINIT(a, es); /* Recurse for 1st side, iterate for 2nd side. */ if (s > es) { if (r > es) - qsort(a, r / es, es, cmp); + qsort_r(a, r / es, es, cmp, cookie); a = pn - s; n = s / es; goto loop; @@ -177,9 +179,25 @@ loop: SWAPINIT(a, es); /* Recurse for 2nd side, iterate for 1st side. */ if (r > es) { if (s > es) - qsort(pn - s, s / es, es, cmp); + qsort_r(pn - s, s / es, es, cmp, cookie); n = r / es; goto loop; } } } + +static int +cmpnocookie(const void *a, const void *b, void *cookie) +{ + int (*cmp)(const void *, const void *) = cookie; + + return cmp(a, b); +} + +void +qsort(void *a, size_t n, size_t es, + int (*cmp)(const void *, const void *)) +{ + + qsort_r(a, n, es, cmpnocookie, cmp); +} Index: src/sys/lib/libkern/libkern.h diff -u src/sys/lib/libkern/libkern.h:1.147 src/sys/lib/libkern/libkern.h:1.148 --- src/sys/lib/libkern/libkern.h:1.147 Fri Nov 1 21:11:37 2024 +++ src/sys/lib/libkern/libkern.h Sun Mar 2 16:35:41 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: libkern.h,v 1.147 2024/11/01 21:11:37 riastradh Exp $ */ +/* $NetBSD: libkern.h,v 1.148 2025/03/02 16:35:41 riastradh Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -476,7 +476,10 @@ void hexdump(void (*)(const char *, ... int snprintb(char *, size_t, const char *, uint64_t); int snprintb_m(char *, size_t, const char *, uint64_t, size_t); int kheapsort(void *, size_t, size_t, int (*)(const void *, const void *), - void *); + void *); +int kheapsort_r(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *, + void *); uint32_t crc32(uint32_t, const uint8_t *, size_t); #if __GNUC_PREREQ__(4, 5) \ && (defined(__alpha_cix__) || defined(__mips_popcount)) Index: src/tests/lib/libc/stdlib/Makefile diff -u src/tests/lib/libc/stdlib/Makefile:1.34 src/tests/lib/libc/stdlib/Makefile:1.35 --- src/tests/lib/libc/stdlib/Makefile:1.34 Tue Jul 4 15:06:36 2023 +++ src/tests/lib/libc/stdlib/Makefile Sun Mar 2 16:35:41 2025 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.34 2023/07/04 15:06:36 riastradh Exp $ +# $NetBSD: Makefile,v 1.35 2025/03/02 16:35:41 riastradh Exp $ .include <bsd.own.mk> @@ -23,6 +23,7 @@ TESTS_C+= t_system TESTS_SH+= t_atexit TESTS_SH+= t_getopt +TESTS_SH+= t_sort MKMAN=no @@ -30,6 +31,7 @@ BINDIR= ${TESTSDIR} PROGS+= h_atexit PROGS+= h_getopt h_getopt_long +PROGS+= h_sort CFLAGS.t_posix_memalign.c+= -fno-builtin-posix_memalign CFLAGS.t_posix_memalign.c+= -fno-builtin-aligned_alloc Index: src/tools/compat/compat_defs.h diff -u src/tools/compat/compat_defs.h:1.123 src/tools/compat/compat_defs.h:1.124 --- src/tools/compat/compat_defs.h:1.123 Thu Oct 31 17:05:05 2024 +++ src/tools/compat/compat_defs.h Sun Mar 2 16:35:41 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: compat_defs.h,v 1.123 2024/10/31 17:05:05 kre Exp $ */ +/* $NetBSD: compat_defs.h,v 1.124 2025/03/02 16:35:41 riastradh Exp $ */ #ifndef __NETBSD_COMPAT_DEFS_H__ #define __NETBSD_COMPAT_DEFS_H__ @@ -462,8 +462,12 @@ int __nbcompat_gettemp(char *, int *, in ssize_t pread(int, void *, size_t, off_t); #endif -int __nbcompat_heapsort (void *, size_t, size_t, int (*)(const void *, const void *)); +int __nbcompat_heapsort(void *, size_t, size_t, + int (*)(const void *, const void *)); +int __nbcompat_heapsort_r(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); #define heapsort __nbcompat_heapsort +#define heapsort_r __nbcompat_heapsort_r #if !HAVE_DECL_HEAPSORT int heapsort (void *, size_t, size_t, int (*)(const void *, const void *)); Added files: Index: src/tests/lib/libc/stdlib/h_sort.c diff -u /dev/null src/tests/lib/libc/stdlib/h_sort.c:1.1 --- /dev/null Sun Mar 2 16:35:42 2025 +++ src/tests/lib/libc/stdlib/h_sort.c Sun Mar 2 16:35:41 2025 @@ -0,0 +1,177 @@ +/* $NetBSD: h_sort.c,v 1.1 2025/03/02 16:35:41 riastradh Exp $ */ + +/*- + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: h_sort.c,v 1.1 2025/03/02 16:35:41 riastradh Exp $"); + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static void +heapsort_r_wrapper(void *a, size_t n, size_t sz, + int (*cmp)(const void *, const void *, void *), void *cookie) +{ + + if (heapsort_r(a, n, sz, cmp, cookie) == -1) + err(1, "heapsort_r"); +} + +static void +mergesort_r_wrapper(void *a, size_t n, size_t sz, + int (*cmp)(const void *, const void *, void *), void *cookie) +{ + + if (mergesort_r(a, n, sz, cmp, cookie) == -1) + err(1, "mergesort_r"); +} + +static int +cmp(const void *va, const void *vb, void *cookie) +{ + const char *buf = cookie; + const size_t *a = va; + const size_t *b = vb; + + return strcmp(buf + *a, buf + *b); +} + +int +main(int argc, char **argv) +{ + void (*sortfn)(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); + char *buf = NULL; + size_t nbuf; + size_t *lines = NULL; + size_t nlines; + size_t off; + ssize_t nread; + char *p; + size_t i; + int error; + + /* + * Parse arguments. + */ + setprogname(argv[0]); + if (argc < 2) + errx(1, "Usage: %s <sortfn>", getprogname()); + if (strcmp(argv[1], "heapsort_r") == 0) + sortfn = &heapsort_r_wrapper; + else if (strcmp(argv[1], "mergesort_r") == 0) + sortfn = &mergesort_r_wrapper; + else if (strcmp(argv[1], "qsort_r") == 0) + sortfn = &qsort_r; + else + errx(1, "unknown sort: %s", argv[1]); + + /* + * Allocate an initial 4K buffer. + */ + nbuf = 0x1000; + error = reallocarr(&buf, nbuf, 1); + if (error) + err(1, "reallocarr"); + + /* + * Read the input into a contiguous buffer. Reject input with + * embedded NULs so we can use strcmp(3) to compare lines. + */ + off = 0; + while ((nread = read(STDIN_FILENO, buf + off, nbuf - off)) != 0) { + if (nread == -1) + err(1, "read"); + if ((size_t)nread > nbuf - off) + errx(1, "overlong read: %zu", (size_t)nread); + if (memchr(buf + off, '\0', (size_t)nread) != NULL) + errx(1, "NUL byte in input"); + off += (size_t)nread; + + /* + * If we filled the buffer, reallocate it with double + * the size. Bail if that would overflow. + */ + if (nbuf - off == 0) { + if (nbuf > SIZE_MAX/2) + errx(1, "input overflow"); + nbuf *= 2; + error = reallocarr(&buf, nbuf, 1); + if (error) + err(1, "reallocarr"); + } + } + + /* + * If the input was empty, nothing to do. + */ + if (off == 0) + return 0; + + /* + * NUL-terminate the input and count the lines. The last line + * may have no trailing \n. + */ + buf[off] = '\0'; + nlines = 1; + for (p = buf; (p = strchr(p, '\n')) != NULL;) { + if (*++p == '\0') + break; + nlines++; + } + + /* + * Create an array of line offsets to sort. NUL-terminate each + * line so we can use strcmp(3). + */ + error = reallocarr(&lines, nlines, sizeof(lines[0])); + if (lines == NULL) + err(1, "reallocarr"); + i = 0; + for (p = buf; lines[i++] = p - buf, (p = strchr(p, '\n')) != NULL;) { + *p = '\0'; + if (*++p == '\0') + break; + } + + /* + * Sort the array of line offsets via comparison function that + * consults the buffer as a cookie. + */ + (*sortfn)(lines, nlines, sizeof(lines[0]), &cmp, buf); + + /* + * Print the lines in sorted order. + */ + for (i = 0; i < nlines; i++) + printf("%s\n", buf + lines[i]); + fflush(stdout); + return ferror(stdout); +} Index: src/tests/lib/libc/stdlib/t_sort.sh diff -u /dev/null src/tests/lib/libc/stdlib/t_sort.sh:1.1 --- /dev/null Sun Mar 2 16:35:42 2025 +++ src/tests/lib/libc/stdlib/t_sort.sh Sun Mar 2 16:35:41 2025 @@ -0,0 +1,61 @@ +# $NetBSD: t_sort.sh,v 1.1 2025/03/02 16:35:41 riastradh Exp $ +# +# Copyright (c) 2025 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +check_sort() +{ + local sortfn + + set -Ceu + + sortfn="$1" + + printf 'foo\nbar\nbaz\nquux' >in1 + printf 'bar\nbaz\nfoo\nquux\n' >out + atf_check -s exit:0 -o file:out \ + "$(atf_get_srcdir)"/h_sort "$sortfn" <in1 + atf_check -s exit:0 -o empty sh -c 'exec shuffle -f - <in1 >in2' + atf_check -s exit:0 -o file:out \ + "$(atf_get_srcdir)"/h_sort "$sortfn" <in2 +} + +sortfn_case() +{ + local sortfn + + sortfn="$1" + + eval "${sortfn}_head() { atf_set descr \"Test ${sortfn}\"; }" + eval "${sortfn}_body() { check_sort $sortfn; }" + atf_add_test_case "$sortfn" +} + +atf_init_test_cases() +{ + + sortfn_case heapsort_r + sortfn_case mergesort_r + sortfn_case qsort_r +}