Subject: Re: make(1) variables: sort and disorder [patch]
To: Simon J. Gerraty <sjg@crufty.net>
From: Mike M. Volokhov <mishka@apk.od.ua>
List: tech-userlevel
Date: 05/31/2005 18:14:11
On Mon, 30 May 2005 12:39:05 -0700 (PDT)
sjg@crufty.net (Simon J. Gerraty) wrote:

> >To change this behaviour, I've modified make(1) program to provide a
> >new variable modifier (:X) which is designed to disorder all words in
> >variable, opposite to :O. For example:
> 
> This is a very interesting idea - the patch is a bit hard to read due to
> the transport encoding.  One suggestion up front though.
> 
> Rathere than add a new option :X, turn allow :O to take an optional 2nd
> flag - eg. 
> :O	ordered
> :Or	reversed
> :Ox	random
> 
> or something like that.

Done. Please take a look to the patch (below) and thanks for the advice.

--
Mishka.


Index: make.1
===================================================================
RCS file: /usr/home/mishka/NetBSD-CVS/src/usr.bin/make/make.1,v
retrieving revision 1.107
diff -u -r1.107 make.1
--- make.1	8 May 2005 00:38:47 -0000	1.107
+++ make.1	31 May 2005 15:10:59 -0000
@@ -664,6 +664,17 @@
 .Ar pattern .
 .It Cm \&:O
 Order every word in variable alphabetically.
+.It Cm \&:Or
+Reverse words in variable from head to tail.
+.It Cm \&:Ox
+Randomize words in variable. The results will be different each
+time you are referring to the modified variable; use the assignment
+with expansion
+.Pq Ql Cm \&:=
+to prevent such behaviour. If randomization will be done on unsorted
+source sequence, it may produce an alphabetically ordered result;
+to avoid this explicitly sort the source, and then randomize it as
+.Ql Cm \&:O:Ox .
 .It Cm \&:Q
 Quotes every shell meta-character in the variable, so that it can be passed
 safely through recursive invocations of
Index: var.c
===================================================================
RCS file: /usr/home/mishka/NetBSD-CVS/src/usr.bin/make/var.c,v
retrieving revision 1.92
diff -u -r1.92 var.c
--- var.c	16 Feb 2005 15:11:53 -0000	1.92
+++ var.c	31 May 2005 15:10:59 -0000
@@ -130,6 +130,7 @@
 #include    <ctype.h>
 #include    <stdlib.h>
 #include    <limits.h>
+#include    <sys/time.h>
 
 #include    "make.h"
 #include    "buf.h"
@@ -286,8 +287,9 @@
     const char *,
     Boolean (*)(GNode *, Var_Parse_State *, char *, Boolean, Buffer, ClientData),
     ClientData);
-static char *VarSort(const char *);
+static char *VarOrder(const char *, const char);
 static char *VarUniq(const char *);
+static int VarNumbCompare(const void *, const void *);
 static int VarWordCompare(const void *, const void *);
 static void VarPrintVar(ClientData);
 
@@ -1540,13 +1542,24 @@
 	return r;
 }
 
+static int
+VarNumbCompare(const void *a, const void *b)
+{
+	if (*(int *)a > *(int *)b)
+		return 1;
+	else if (*(int *)a < *(int *)b)
+		return -1;
+	return 0;
+}
+
 /*-
  *-----------------------------------------------------------------------
- * VarSort --
- *	Sort the words in the string.
+ * VarOrder --
+ *	Order the words in the string.
  *
  * Input:
  *	str		String whose words should be sorted
+ *	otype		How to order: (s)ort, (r)everse, intermi(x) 
  *
  * Results:
  *	A string containing the words sorted
@@ -1557,7 +1570,7 @@
  *-----------------------------------------------------------------------
  */
 static char *
-VarSort(const char *str)
+VarOrder(const char *str, const char otype)
 {
     Buffer  	  buf;	    	    /* Buffer for the new string */
     char **av;			    /* word list [first word does not count] */
@@ -1569,7 +1582,48 @@
     av = brk_string(str, &ac, FALSE, &as);
 
     if (ac > 0)
-	qsort(av, ac, sizeof(char *), VarWordCompare);
+	switch (otype) {
+	case 's':	/* sort alphabetically */
+	    qsort(av, ac, sizeof(char *), VarWordCompare);
+	    break;
+	case 'r':	/* reverse */
+	{
+	    char *t;
+	    int ac1, ac2;
+
+	    ac1 = ac - 1;
+	    ac2 = ac / 2 - 1;
+	    for (i = 0; i <= ac2; i++) {
+		t = av[i];
+		av[i] = av[ac1-i];
+		av[ac1-i] = t;
+	    }
+	    break;
+	}
+	case 'x':	/* randomize */
+	{
+	    struct ai {
+		int rnd;  /* this must be placed first; see VarNumbCompare */
+		char *avi;
+	    } *ai = NULL;
+	    struct timeval rightnow;
+
+	    /* intermixed variable should return different values each time */
+	    gettimeofday(&rightnow, NULL);
+	    srandom(rightnow.tv_sec + rightnow.tv_usec);
+
+	    ai = emalloc(ac * sizeof(struct ai));
+	    for (i = 0; i < ac; i++) {
+		    ai[i].rnd = random();
+		    ai[i].avi = av[i];
+	    }
+	    /* sorting ai by the first field will disorder the second */
+	    qsort(ai, ac, sizeof(struct ai), VarNumbCompare);
+	    for (i = 0; i < ac; i++)
+		    av[i] = ai[i].avi;
+	    free(ai);
+	}
+	} /* end of switch */
 
     for (i = 0; i < ac; i++) {
 	Buf_AddBytes(buf, strlen(av[i]), (Byte *) av[i]);
@@ -2149,7 +2203,9 @@
      *  	  	    	each word
      *  	  :R	    	Substitute the root of each word
      *  	  	    	(pathname minus the suffix).
-     *		  :O		("Order") Sort words in variable.
+     *		  :O		("Order") Alphabeticaly sort words in variable.
+     *		  :Or		("Reverse") Reverse words backwards.
+     *		  :Ox		("intermiX") Randomize words in variable.
      *		  :u		("uniq") Remove adjacent duplicate words.
      *		  :tu		Converts the variable contents to uppercase.
      *		  :tl		Converts the variable contents to lowercase.
@@ -3007,13 +3063,23 @@
 		    }
 		    /*FALLTHRU*/
 		case 'O':
+		{
+		    char otype;
+
 		    if (tstr[1] == endc || tstr[1] == ':') {
-			newStr = VarSort(nstr);
+			otype = 's';
 			cp = tstr + 1;
-			termc = *cp;
-			break;
+		    } else if ( (tstr[1] == 'r' || tstr[1] == 'x') &&
+		    		(tstr[2] == endc || tstr[2] == ':') ) {
+			otype = tstr[1];
+			cp = tstr + 2;
+		    } else {
+			goto bad_modifier;
 		    }
-		    /*FALLTHRU*/
+		    termc = *cp;
+		    newStr = VarOrder(nstr, otype);
+		    break;
+		}   /*FALLTHRU*/
 		case 'u':
 		    if (tstr[1] == endc || tstr[1] == ':') {
 			newStr = VarUniq(nstr);