diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1 index c1bbcbeda655..8576a62fb660 100644 --- a/usr.bin/make/make.1 +++ b/usr.bin/make/make.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $ +.\" $NetBSD: make.1,v 1.297 2021/07/30 19:55:22 sjg Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -29,7 +29,7 @@ .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd December 22, 2020 +.Dd July 30, 2020 .Dt MAKE 1 .Os .Sh NAME @@ -1232,8 +1232,18 @@ but selects all words which do not match .Ar pattern . .It Cm \&:O Orders every word in variable alphabetically. +.It Cm \&:On +Orders every word in variable numerically. +A number followed by one of +.Ql K , +.Ql M +or +.Ql G +is multiplied by the appropriate factor. .It Cm \&:Or Orders every word in variable in reverse alphabetical order. +.It Cm \&:Orn +Orders every word in variable in reverse numerical order. .It Cm \&:Ox Shuffles the words in variable. The results will be different each time you are referring to the diff --git a/usr.bin/make/unit-tests/Makefile b/usr.bin/make/unit-tests/Makefile index 9babf4f3ca60..576b62bad583 100644 --- a/usr.bin/make/unit-tests/Makefile +++ b/usr.bin/make/unit-tests/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.280 2021/06/29 00:35:23 sjg Exp $ +# $NetBSD: Makefile,v 1.281 2021/07/30 19:55:22 sjg Exp $ # # Unit tests for make(1) # @@ -355,6 +355,7 @@ TESTS+= varmod-match TESTS+= varmod-match-escape TESTS+= varmod-no-match TESTS+= varmod-order +TESTS+= varmod-order-numeric TESTS+= varmod-order-reverse TESTS+= varmod-order-shuffle TESTS+= varmod-path diff --git a/usr.bin/make/unit-tests/varmod-order-numeric.exp b/usr.bin/make/unit-tests/varmod-order-numeric.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/usr.bin/make/unit-tests/varmod-order-numeric.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/usr.bin/make/unit-tests/varmod-order-numeric.mk b/usr.bin/make/unit-tests/varmod-order-numeric.mk new file mode 100644 index 000000000000..e895845c57e0 --- /dev/null +++ b/usr.bin/make/unit-tests/varmod-order-numeric.mk @@ -0,0 +1,18 @@ +# $NetBSD: varmod-order-numeric.mk,v 1.1 2021/07/30 19:55:22 sjg Exp $ +# +# Tests for the :On variable modifier, which returns the words, sorted in +# ascending numeric order. + +NUMBERS= 3 5 7 1 42 -42 1M 1k + +.if ${NUMBERS:On} != "-42 1 3 5 7 42 1k 1M" +. error ${NUMBERS:On} +.endif + +.if ${NUMBERS:Orn} != "1M 1k 42 7 5 3 1 -42" +. error ${NUMBERS:Orn} +.endif + + +all: + @:; diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c index 514c8e66ef52..7c02fe308ce9 100644 --- a/usr.bin/make/var.c +++ b/usr.bin/make/var.c @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.938 2021/06/21 18:25:20 rillig Exp $ */ +/* $NetBSD: var.c,v 1.939 2021/07/30 19:55:22 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -140,7 +140,7 @@ #include "metachar.h" /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: var.c,v 1.938 2021/06/21 18:25:20 rillig Exp $"); +MAKE_RCSID("$NetBSD: var.c,v 1.939 2021/07/30 19:55:22 sjg Exp $"); /* * Variables are defined using one of the VAR=value assignments. Their @@ -3272,6 +3272,56 @@ bad_modifier: return AMR_BAD; } +#ifndef NUM_TYPE +# define NUM_TYPE long long +#endif + +static NUM_TYPE +num_val(const char *s) +{ + NUM_TYPE val; + char *ep; + + val = strtoll(s, &ep, 0); + if (ep != s) { + switch (*ep) { + case 'K': + case 'k': + val <<= 10; + break; + case 'M': + case 'm': + val <<= 20; + break; + case 'G': + case 'g': + val <<= 30; + break; + } + } + return val; +} + +static int +num_cmp_asc(const void *sa, const void *sb) +{ + NUM_TYPE a, b; + + a = num_val(*(const char *const *)sa); + b = num_val(*(const char *const *)sb); + return (a > b) ? 1 : (b > a) ? -1 : 0; +} + +static int +num_cmp_desc(const void *sa, const void *sb) +{ + NUM_TYPE a, b; + + a = num_val(*(const char *const *)sa); + b = num_val(*(const char *const *)sb); + return (a > b) ? -1 : (b > a) ? 1 : 0; +} + static int str_cmp_asc(const void *a, const void *b) { @@ -3297,22 +3347,35 @@ ShuffleStrings(char **strs, size_t n) } } -/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */ +/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) or + * :On (numeric ascending) or :Onr or :Orn (numeric descending) + */ static ApplyModifierResult ApplyModifier_Order(const char **pp, ModChain *ch) { const char *mod = (*pp)++; /* skip past the 'O' in any case */ Words words; enum SortMode { - ASC, DESC, SHUFFLE + ASC, DESC, NUM_ASC, NUM_DESC, SHUFFLE } mode; if (IsDelimiter(mod[1], ch)) { mode = ASC; + } else if (mod[1] == 'n') { + mode = NUM_ASC; + (*pp)++; + if (!IsDelimiter(mod[2], ch)) { + (*pp)++; + if (mod[2] == 'r') + mode = NUM_DESC; + } } else if ((mod[1] == 'r' || mod[1] == 'x') && IsDelimiter(mod[2], ch)) { (*pp)++; mode = mod[1] == 'r' ? DESC : SHUFFLE; + } else if (mod[1] == 'r' && mod[2] == 'n') { + (*pp) += 2; + mode = NUM_DESC; } else return AMR_BAD; @@ -3322,6 +3385,9 @@ ApplyModifier_Order(const char **pp, ModChain *ch) words = Str_Words(ch->expr->value.str, false); if (mode == SHUFFLE) ShuffleStrings(words.words, words.len); + else if (mode == NUM_ASC || mode == NUM_DESC) + qsort(words.words, words.len, sizeof words.words[0], + mode == NUM_ASC ? num_cmp_asc : num_cmp_desc); else qsort(words.words, words.len, sizeof words.words[0], mode == ASC ? str_cmp_asc : str_cmp_desc);