Module Name:    src
Committed By:   rillig
Date:           Fri Jul  3 22:10:42 UTC 2020

Modified Files:
        src/usr.bin/make: var.c
        src/usr.bin/make/unit-tests: modmisc.exp modmisc.mk

Log Message:
make(1): clean up documentation, small refactorings for variables

- document the callback for VarModify
- clearly mark the callbacks
- shorten the documentation for some callback functions
- fix the documentation of VarSYSVMatch
- remove unnecessary null check from VarLoopExpand
- add test for applying modifiers to empty strings


To generate a diff of this commit:
cvs rdiff -u -r1.243 -r1.244 src/usr.bin/make/var.c
cvs rdiff -u -r1.7 -r1.8 src/usr.bin/make/unit-tests/modmisc.exp
cvs rdiff -u -r1.6 -r1.7 src/usr.bin/make/unit-tests/modmisc.mk

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/make/var.c
diff -u src/usr.bin/make/var.c:1.243 src/usr.bin/make/var.c:1.244
--- src/usr.bin/make/var.c:1.243	Fri Jul  3 18:41:50 2020
+++ src/usr.bin/make/var.c	Fri Jul  3 22:10:42 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: var.c,v 1.243 2020/07/03 18:41:50 rillig Exp $	*/
+/*	$NetBSD: var.c,v 1.244 2020/07/03 22:10:42 rillig Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
  */
 
 #ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: var.c,v 1.243 2020/07/03 18:41:50 rillig Exp $";
+static char rcsid[] = "$NetBSD: var.c,v 1.244 2020/07/03 22:10:42 rillig Exp $";
 #else
 #include <sys/cdefs.h>
 #ifndef lint
 #if 0
 static char sccsid[] = "@(#)var.c	8.3 (Berkeley) 3/19/94";
 #else
-__RCSID("$NetBSD: var.c,v 1.243 2020/07/03 18:41:50 rillig Exp $");
+__RCSID("$NetBSD: var.c,v 1.244 2020/07/03 22:10:42 rillig Exp $");
 #endif
 #endif /* not lint */
 #endif
@@ -1168,7 +1168,22 @@ Var_Value(const char *name, GNode *ctxt,
     return p;
 }
 
-/* Add the dirname of the given word to the buffer. */
+
+/* This callback for VarModify gets a single word from an expression and
+ * typically adds a modification of this word to the buffer. It may also do
+ * nothing or add several words.
+ *
+ * If addSpaces is TRUE, it must add a space before adding anything else to
+ * the buffer.
+ *
+ * It returns the addSpace value for the next call of this callback. Typical
+ * return values are the current addSpaces or TRUE. */
+typedef Boolean (*VarModifyCallback)(GNode *ctxt, Var_Parse_State *vpstate,
+    char *word, Boolean addSpace, Buffer *buf, void *data);
+
+
+/* Callback function for VarModify to implement the :H modifier.
+ * Add the dirname of the given word to the buffer. */
 static Boolean
 VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
 	char *word, Boolean addSpace, Buffer *buf,
@@ -1186,7 +1201,8 @@ VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var
     return TRUE;
 }
 
-/* Add the basename of the given word to the buffer. */
+/* Callback function for VarModify to implement the :T modifier.
+ * Add the basename of the given word to the buffer. */
 static Boolean
 VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
 	char *word, Boolean addSpace, Buffer *buf,
@@ -1198,11 +1214,11 @@ VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var
     if (addSpace && vpstate->varSpace)
 	Buf_AddByte(buf, vpstate->varSpace);
     Buf_AddBytes(buf, strlen(base), base);
-
     return TRUE;
 }
 
-/* Add the filename suffix of the given word to the buffer, if it exists. */
+/* Callback function for VarModify to implement the :E modifier.
+ * Add the filename suffix of the given word to the buffer, if it exists. */
 static Boolean
 VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
 	  char *word, Boolean addSpace, Buffer *buf,
@@ -1218,7 +1234,8 @@ VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, V
     return TRUE;
 }
 
-/* Add the filename basename of the given word to the buffer. */
+/* Callback function for VarModify to implement the :R modifier.
+ * Add the filename basename of the given word to the buffer. */
 static Boolean
 VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
 	char *word, Boolean addSpace, Buffer *buf,
@@ -1233,167 +1250,78 @@ VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var
     return TRUE;
 }
 
-/*-
- *-----------------------------------------------------------------------
- * VarMatch --
- *	Place the word in the buffer if it matches the given pattern.
- *	Callback function for VarModify to implement the :M modifier.
- *
- * Input:
- *	word		Word to examine
- *	addSpace	TRUE if need to add a space to the buffer
- *			before adding the word, if it matches
- *	buf		Buffer in which to store it
- *	pattern		Pattern the word must match
- *
- * Results:
- *	TRUE if a space should be placed in the buffer before the next
- *	word.
- *
- * Side Effects:
- *	The word may be copied to the buffer.
- *
- *-----------------------------------------------------------------------
- */
+/* Callback function for VarModify to implement the :M modifier.
+ * Place the word in the buffer if it matches the given pattern. */
 static Boolean
 VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
 	 char *word, Boolean addSpace, Buffer *buf,
-	 void *pattern)
+	 void *data)
 {
+    const char *pattern = data;
     if (DEBUG(VAR))
-	fprintf(debug_file, "VarMatch [%s] [%s]\n", word, (char *)pattern);
-    if (Str_Match(word, (char *)pattern)) {
-	if (addSpace && vpstate->varSpace) {
-	    Buf_AddByte(buf, vpstate->varSpace);
-	}
-	addSpace = TRUE;
-	Buf_AddBytes(buf, strlen(word), word);
-    }
-    return addSpace;
+	fprintf(debug_file, "VarMatch [%s] [%s]\n", word, pattern);
+    if (!Str_Match(word, pattern))
+	return addSpace;
+    if (addSpace && vpstate->varSpace)
+	Buf_AddByte(buf, vpstate->varSpace);
+    Buf_AddBytes(buf, strlen(word), word);
+    return TRUE;
 }
 
 #ifdef SYSVVARSUB
-/*-
- *-----------------------------------------------------------------------
- * VarSYSVMatch --
- *	Place the word in the buffer if it matches the given pattern.
- *	Callback function for VarModify to implement the System V %
- *	modifiers.
- *
- * Input:
- *	word		Word to examine
- *	addSpace	TRUE if need to add a space to the buffer
- *			before adding the word, if it matches
- *	buf		Buffer in which to store it
- *	patp		Pattern the word must match
- *
- * Results:
- *	TRUE if a space should be placed in the buffer before the next
- *	word.
- *
- * Side Effects:
- *	The word may be copied to the buffer.
- *
- *-----------------------------------------------------------------------
- */
+/* Callback function for VarModify to implement the :%.from=%.to modifier. */
 static Boolean
 VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate,
 	     char *word, Boolean addSpace, Buffer *buf,
-	     void *patp)
+	     void *data)
 {
     size_t len;
     char *ptr;
     Boolean hasPercent;
-    VarPattern 	  *pat = (VarPattern *)patp;
-    char *varexp;
+    VarPattern *pat = data;
 
     if (addSpace && vpstate->varSpace)
 	Buf_AddByte(buf, vpstate->varSpace);
 
-    addSpace = TRUE;
-
     if ((ptr = Str_SYSVMatch(word, pat->lhs, &len, &hasPercent)) != NULL) {
-	varexp = Var_Subst(NULL, pat->rhs, ctx, VARF_WANTRES);
+	char *varexp = Var_Subst(NULL, pat->rhs, ctx, VARF_WANTRES);
 	Str_SYSVSubst(buf, varexp, ptr, len, hasPercent);
 	free(varexp);
     } else {
 	Buf_AddBytes(buf, strlen(word), word);
     }
 
-    return addSpace;
+    return TRUE;
 }
 #endif
 
-
-/*-
- *-----------------------------------------------------------------------
- * VarNoMatch --
- *	Place the word in the buffer if it doesn't match the given pattern.
- *	Callback function for VarModify to implement the :N modifier.
- *
- * Input:
- *	word		Word to examine
- *	addSpace	TRUE if need to add a space to the buffer
- *			before adding the word, if it matches
- *	buf		Buffer in which to store it
- *	pattern		Pattern the word must match
- *
- * Results:
- *	TRUE if a space should be placed in the buffer before the next
- *	word.
- *
- * Side Effects:
- *	The word may be copied to the buffer.
- *
- *-----------------------------------------------------------------------
- */
+/* Callback function for VarModify to implement the :N modifier.
+ * Place the word in the buffer if it doesn't match the given pattern. */
 static Boolean
 VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
 	   char *word, Boolean addSpace, Buffer *buf,
-	   void *pattern)
+	   void *data)
 {
-    if (!Str_Match(word, (char *)pattern)) {
-	if (addSpace && vpstate->varSpace) {
-	    Buf_AddByte(buf, vpstate->varSpace);
-	}
-	addSpace = TRUE;
-	Buf_AddBytes(buf, strlen(word), word);
-    }
-    return addSpace;
+    const char *pattern = data;
+    if (Str_Match(word, pattern))
+	return addSpace;
+    if (addSpace && vpstate->varSpace)
+	Buf_AddByte(buf, vpstate->varSpace);
+    Buf_AddBytes(buf, strlen(word), word);
+    return TRUE;
 }
 
-
-/*-
- *-----------------------------------------------------------------------
- * VarSubstitute --
- *	Perform a string-substitution on the given word, placing the
- *	result in the passed buffer.
- *
- * Input:
- *	word		Word to modify
- *	addSpace	True if space should be added before
- *			other characters
- *	buf		Buffer for result
- *	patternp	Pattern for substitution
- *
- * Results:
- *	TRUE if a space is needed before more characters are added.
- *
- * Side Effects:
- *	None.
- *
- *-----------------------------------------------------------------------
- */
+/* Callback function for VarModify to implement the :S,from,to, modifier.
+ * Perform a string substitution on the given word. */
 static Boolean
 VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
 	      char *word, Boolean addSpace, Buffer *buf,
-	      void *patternp)
+	      void *data)
 {
-    int  	wordLen;    /* Length of word */
-    char 	*cp;	    /* General pointer */
-    VarPattern	*pattern = (VarPattern *)patternp;
+    int wordLen = strlen(word);
+    char *cp;			/* General pointer */
+    VarPattern *pattern = data;
 
-    wordLen = strlen(word);
     if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
 	(VAR_SUB_ONE|VAR_SUB_MATCHED)) {
 	/*
@@ -1565,32 +1493,19 @@ VarREError(int reerr, regex_t *pat, cons
     free(errbuf);
 }
 
-
-/*-
- *-----------------------------------------------------------------------
- * VarRESubstitute --
- *	Perform a regex substitution on the given word, placing the
- *	result in the passed buffer.
- *
- * Results:
- *	TRUE if a space is needed before more characters are added.
- *
- * Side Effects:
- *	None.
- *
- *-----------------------------------------------------------------------
- */
+/* Callback function for VarModify to implement the :C/from/to/ modifier.
+ * Perform a regex substitution on the given word. */
 static Boolean
 VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED,
 		Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
 		char *word, Boolean addSpace, Buffer *buf,
-		void *patternp)
+		void *data)
 {
-    VarREPattern *pat;
+    VarREPattern *pat = data;
     int xrv;
-    char *wp;
+    char *wp = word;
     char *rp;
-    int added;
+    int added = 0;
     int flags = 0;
 
 #define MAYBE_ADD_SPACE()		\
@@ -1598,10 +1513,6 @@ VarRESubstitute(GNode *ctx MAKE_ATTR_UNU
 	    Buf_AddByte(buf, ' ');	\
 	added = 1
 
-    added = 0;
-    wp = word;
-    pat = patternp;
-
     if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
 	(VAR_SUB_ONE|VAR_SUB_MATCHED))
 	xrv = REG_NOMATCH;
@@ -1697,40 +1608,20 @@ VarRESubstitute(GNode *ctx MAKE_ATTR_UNU
 #endif
 
 
-
-/*-
- *-----------------------------------------------------------------------
- * VarLoopExpand --
- *	Implements the :@<temp>@<string>@ modifier of ODE make.
- *	We set the temp variable named in pattern.lhs to word and expand
- *	pattern.rhs storing the result in the passed buffer.
- *
- * Input:
- *	word		Word to modify
- *	addSpace	True if space should be added before
- *			other characters
- *	buf		Buffer for result
- *	pattern		Datafor substitution
- *
- * Results:
- *	TRUE if a space is needed before more characters are added.
- *
- * Side Effects:
- *	None.
- *
- *-----------------------------------------------------------------------
- */
+/* Callback function for VarModify to implement the :@var@...@ modifier of
+ * ODE make. We set the temp variable named in pattern.lhs to word and
+ * expand pattern.rhs. */
 static Boolean
 VarLoopExpand(GNode *ctx MAKE_ATTR_UNUSED,
 	      Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
 	      char *word, Boolean addSpace, Buffer *buf,
-	      void *loopp)
+	      void *data)
 {
-    VarLoop_t	*loop = (VarLoop_t *)loopp;
+    VarLoop_t *loop = data;
     char *s;
     int slen;
 
-    if (word && *word) {
+    if (*word) {
         Var_Set_with_flags(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT);
         s = Var_Subst(NULL, loop->str, loop->ctxt, loop->flags);
         if (s != NULL && *s != '\0') {
@@ -1834,7 +1725,8 @@ VarSelectWords(GNode *ctx MAKE_ATTR_UNUS
 }
 
 
-/* Replace each word with the result of realpath() if successful. */
+/* Callback function for VarModify to implement the :tA modifier.
+ * Replace each word with the result of realpath() if successful. */
 static Boolean
 VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
 	    char *word, Boolean addSpace, Buffer *buf,
@@ -1856,14 +1748,12 @@ VarRealpath(GNode *ctx MAKE_ATTR_UNUSED,
 
 /*-
  *-----------------------------------------------------------------------
- * VarModify --
- *	Modify each of the words of the passed string using the given
- *	function. Used to implement all modifiers.
+ * Modify each of the words of the passed string using the given function.
  *
  * Input:
  *	str		String whose words should be trimmed
  *	modProc		Function to use to modify them
- *	datum		Datum to pass it
+ *	data		Custom data for the modProc
  *
  * Results:
  *	A string of all the words modified appropriately.
@@ -1875,10 +1765,7 @@ VarRealpath(GNode *ctx MAKE_ATTR_UNUSED,
  */
 static char *
 VarModify(GNode *ctx, Var_Parse_State *vpstate,
-    const char *str,
-    Boolean (*modProc)(GNode *, Var_Parse_State *, char *,
-		       Boolean, Buffer *, void *),
-    void *datum)
+    const char *str, VarModifyCallback modProc, void *datum)
 {
     Buffer buf;			/* Buffer for the new string */
     Boolean addSpace; 		/* TRUE if need to add a space to the

Index: src/usr.bin/make/unit-tests/modmisc.exp
diff -u src/usr.bin/make/unit-tests/modmisc.exp:1.7 src/usr.bin/make/unit-tests/modmisc.exp:1.8
--- src/usr.bin/make/unit-tests/modmisc.exp:1.7	Fri Jul  3 19:21:11 2020
+++ src/usr.bin/make/unit-tests/modmisc.exp	Fri Jul  3 22:10:42 2020
@@ -11,4 +11,10 @@ dirname of 'a/b/c def a.b.c a.b/c a a.a 
 basename of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c def a.b.c c a a.a .gitignore a a.a'
 suffix of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c b/c a gitignore a'
 root of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b/c def a.b a a a  a a'
+S:
+C:
+@:
+S:empty
+C:empty
+@:
 exit status 0

Index: src/usr.bin/make/unit-tests/modmisc.mk
diff -u src/usr.bin/make/unit-tests/modmisc.mk:1.6 src/usr.bin/make/unit-tests/modmisc.mk:1.7
--- src/usr.bin/make/unit-tests/modmisc.mk:1.6	Fri Jul  3 18:41:50 2020
+++ src/usr.bin/make/unit-tests/modmisc.mk	Fri Jul  3 22:10:42 2020
@@ -1,4 +1,4 @@
-# $Id: modmisc.mk,v 1.6 2020/07/03 18:41:50 rillig Exp $
+# $Id: modmisc.mk,v 1.7 2020/07/03 22:10:42 rillig Exp $
 #
 # miscellaneous modifier tests
 
@@ -15,7 +15,7 @@ MOD_HOMES=S,/home/,/homes/,
 MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@
 MOD_SEP=S,:, ,g
 
-all:	modvar modvarloop modsysv mod-HTE
+all:	modvar modvarloop modsysv mod-HTE emptyvar undefvar
 
 modsysv:
 	@echo "The answer is ${libfoo.a:L:libfoo.a=42}"
@@ -43,3 +43,18 @@ mod-HTE:
 	@echo "basename of '"${PATHNAMES:Q}"' is '"${PATHNAMES:T:Q}"'"
 	@echo "suffix of '"${PATHNAMES:Q}"' is '"${PATHNAMES:E:Q}"'"
 	@echo "root of '"${PATHNAMES:Q}"' is '"${PATHNAMES:R:Q}"'"
+
+# When a modifier is applied to the "" variable, the result is discarded.
+emptyvar:
+	@echo S:${:S,^$,empty,}
+	@echo C:${:C,^$,empty,}
+	@echo @:${:@var@${var}@}
+
+# The :U modifier turns even the "" variable into something that has a value.
+# The resulting variable is empty, but is still considered to contain a
+# single empty word. This word can be accessed by the :S and :C modifiers,
+# but not by the :@ modifier since it explicitly skips empty words.
+undefvar:
+	@echo S:${:U:S,^$,empty,}
+	@echo C:${:U:C,^$,empty,}
+	@echo @:${:U:@var@empty@}

Reply via email to