Add a :ts[c] modifier to allow controlling the separator used between

words in a variable expansion.  If 'c' is omitted no separator is used.
This commit is contained in:
sjg 2003-07-14 20:39:20 +00:00
parent fadd31bf33
commit f1cf540a8d
2 changed files with 113 additions and 37 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.80 2003/06/26 18:21:45 wiz Exp $ .\" $NetBSD: make.1,v 1.81 2003/07/14 20:39:20 sjg Exp $
.\" .\"
.\" Copyright (c) 1990, 1993 .\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -625,6 +625,13 @@ Replaces each word in the variable with everything but its suffix.
Converts variable to lower-case letters. Converts variable to lower-case letters.
.It Cm tu .It Cm tu
Converts variable to upper-case letters. Converts variable to upper-case letters.
.It Cm ts Ar c
Words in the variable are normally separated by a space on expansion.
This modifier sets the separator to the character
.Ar c .
If
.Ar c
is omitted, then no separator is used.
.Sm off .Sm off
.It Cm S No \&/ Ar old_string Xo .It Cm S No \&/ Ar old_string Xo
.No \&/ Ar new_string .No \&/ Ar new_string

View File

@ -1,4 +1,4 @@
/* $NetBSD: var.c,v 1.73 2003/07/14 18:19:13 christos Exp $ */ /* $NetBSD: var.c,v 1.74 2003/07/14 20:39:20 sjg Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -39,14 +39,14 @@
*/ */
#ifdef MAKE_BOOTSTRAP #ifdef MAKE_BOOTSTRAP
static char rcsid[] = "$NetBSD: var.c,v 1.73 2003/07/14 18:19:13 christos Exp $"; static char rcsid[] = "$NetBSD: var.c,v 1.74 2003/07/14 20:39:20 sjg Exp $";
#else #else
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint #ifndef lint
#if 0 #if 0
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
#else #else
__RCSID("$NetBSD: var.c,v 1.73 2003/07/14 18:19:13 christos Exp $"); __RCSID("$NetBSD: var.c,v 1.74 2003/07/14 20:39:20 sjg Exp $");
#endif #endif
#endif /* not lint */ #endif /* not lint */
#endif #endif
@ -165,6 +165,8 @@ typedef struct Var {
#define VAR_MATCH_END 0x10 /* Match at end of word */ #define VAR_MATCH_END 0x10 /* Match at end of word */
#define VAR_NOSUBST 0x20 /* don't expand vars in VarGetPattern */ #define VAR_NOSUBST 0x20 /* don't expand vars in VarGetPattern */
static Byte varSpace = ' '; /* word separator in expansions */
/* Var_Set flags */ /* Var_Set flags */
#define VAR_NO_EXPORT 0x01 /* do not export */ #define VAR_NO_EXPORT 0x01 /* do not export */
@ -656,8 +658,8 @@ VarHead(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
slash = strrchr (word, '/'); slash = strrchr (word, '/');
if (slash != (char *)NULL) { if (slash != (char *)NULL) {
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte (buf, (Byte)' '); Buf_AddByte (buf, varSpace);
} }
*slash = '\0'; *slash = '\0';
Buf_AddBytes (buf, strlen (word), (Byte *)word); Buf_AddBytes (buf, strlen (word), (Byte *)word);
@ -667,12 +669,10 @@ VarHead(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
/* /*
* If no directory part, give . (q.v. the POSIX standard) * If no directory part, give . (q.v. the POSIX standard)
*/ */
if (addSpace) { if (addSpace && varSpace)
Buf_AddBytes(buf, 2, (const Byte *)" ."); Buf_AddByte(buf, varSpace);
} else {
Buf_AddByte(buf, (Byte)'.'); Buf_AddByte(buf, (Byte)'.');
} }
}
return(dummy ? TRUE : TRUE); return(dummy ? TRUE : TRUE);
} }
@ -703,8 +703,8 @@ VarTail(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
{ {
char *slash; char *slash;
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte (buf, (Byte)' '); Buf_AddByte (buf, varSpace);
} }
slash = strrchr (word, '/'); slash = strrchr (word, '/');
@ -746,8 +746,8 @@ VarSuffix(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
dot = strrchr (word, '.'); dot = strrchr (word, '.');
if (dot != (char *)NULL) { if (dot != (char *)NULL) {
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte (buf, (Byte)' '); Buf_AddByte (buf, varSpace);
} }
*dot++ = '\0'; *dot++ = '\0';
Buf_AddBytes (buf, strlen (dot), (Byte *)dot); Buf_AddBytes (buf, strlen (dot), (Byte *)dot);
@ -784,8 +784,8 @@ VarRoot(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
{ {
char *dot; char *dot;
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte (buf, (Byte)' '); Buf_AddByte (buf, varSpace);
} }
dot = strrchr (word, '.'); dot = strrchr (word, '.');
@ -826,8 +826,8 @@ VarMatch(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
ClientData pattern) ClientData pattern)
{ {
if (Str_Match(word, (char *) pattern)) { if (Str_Match(word, (char *) pattern)) {
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
} }
addSpace = TRUE; addSpace = TRUE;
Buf_AddBytes(buf, strlen(word), (Byte *)word); Buf_AddBytes(buf, strlen(word), (Byte *)word);
@ -868,8 +868,8 @@ VarSYSVMatch(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
VarPattern *pat = (VarPattern *) patp; VarPattern *pat = (VarPattern *) patp;
char *varexp; char *varexp;
if (addSpace) if (addSpace && varSpace)
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
addSpace = TRUE; addSpace = TRUE;
@ -913,8 +913,8 @@ VarNoMatch(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
ClientData pattern) ClientData pattern)
{ {
if (!Str_Match(word, (char *) pattern)) { if (!Str_Match(word, (char *) pattern)) {
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
} }
addSpace = TRUE; addSpace = TRUE;
Buf_AddBytes(buf, strlen(word), (Byte *)word); Buf_AddBytes(buf, strlen(word), (Byte *)word);
@ -972,8 +972,8 @@ VarSubstitute(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
* if rhs is non-null. * if rhs is non-null.
*/ */
if (pattern->rightLen != 0) { if (pattern->rightLen != 0) {
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
} }
addSpace = TRUE; addSpace = TRUE;
Buf_AddBytes(buf, pattern->rightLen, Buf_AddBytes(buf, pattern->rightLen,
@ -990,8 +990,8 @@ VarSubstitute(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
* Matches at start but need to copy in trailing characters * Matches at start but need to copy in trailing characters
*/ */
if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
} }
addSpace = TRUE; addSpace = TRUE;
} }
@ -1023,8 +1023,8 @@ VarSubstitute(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
* by the right-hand-side. * by the right-hand-side.
*/ */
if (((cp - word) + pattern->rightLen) != 0) { if (((cp - word) + pattern->rightLen) != 0) {
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
} }
addSpace = TRUE; addSpace = TRUE;
} }
@ -1059,7 +1059,7 @@ VarSubstitute(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
cp = Str_FindSubstring(word, pattern->lhs); cp = Str_FindSubstring(word, pattern->lhs);
if (cp != (char *)NULL) { if (cp != (char *)NULL) {
if (addSpace && (((cp - word) + pattern->rightLen) != 0)){ if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
addSpace = FALSE; addSpace = FALSE;
} }
Buf_AddBytes(buf, cp-word, (const Byte *)word); Buf_AddBytes(buf, cp-word, (const Byte *)word);
@ -1079,8 +1079,8 @@ VarSubstitute(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
} }
} }
if (wordLen != 0) { if (wordLen != 0) {
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
} }
Buf_AddBytes(buf, wordLen, (Byte *)word); Buf_AddBytes(buf, wordLen, (Byte *)word);
} }
@ -1094,8 +1094,8 @@ VarSubstitute(GNode *ctx, char *word, Boolean addSpace, Buffer buf,
return (addSpace); return (addSpace);
} }
nosub: nosub:
if (addSpace) { if (addSpace && varSpace) {
Buf_AddByte(buf, (Byte)' '); Buf_AddByte(buf, varSpace);
} }
Buf_AddBytes(buf, wordLen, (Byte *)word); Buf_AddBytes(buf, wordLen, (Byte *)word);
return(TRUE); return(TRUE);
@ -1703,6 +1703,8 @@ Var_Parse(const char *str, GNode *ctxt, Boolean err, int *lengthPtr,
dynamic = FALSE; dynamic = FALSE;
start = str; start = str;
varSpace = ' '; /* reset this */
if (str[1] != '(' && str[1] != '{') { if (str[1] != '(' && str[1] != '{') {
/* /*
* If it's not bounded by braces of some sort, life is much simpler. * If it's not bounded by braces of some sort, life is much simpler.
@ -1968,6 +1970,10 @@ Var_Parse(const char *str, GNode *ctxt, Boolean err, int *lengthPtr,
* :u ("uniq") Remove adjacent duplicate words. * :u ("uniq") Remove adjacent duplicate words.
* :tu Converts the variable contents to uppercase. * :tu Converts the variable contents to uppercase.
* :tl Converts the variable contents to lowercase. * :tl Converts the variable contents to lowercase.
* :ts[c] Sets varSpace - the char used to
* separate words to 'c'. If 'c' is
* omitted then no separation is used.
*
* :?<true-value>:<false-value> * :?<true-value>:<false-value>
* If the variable evaluates to true, return * If the variable evaluates to true, return
* true value, else return the second value. * true value, else return the second value.
@ -2025,6 +2031,7 @@ Var_Parse(const char *str, GNode *ctxt, Boolean err, int *lengthPtr,
if (DEBUG(VAR)) { if (DEBUG(VAR)) {
printf("Applying :%c to \"%s\"\n", *tstr, nstr); printf("Applying :%c to \"%s\"\n", *tstr, nstr);
} }
newStr = var_Error;
switch (*tstr) { switch (*tstr) {
case ':': case ':':
@ -2245,12 +2252,67 @@ Var_Parse(const char *str, GNode *ctxt, Boolean err, int *lengthPtr,
} }
case 't': case 't':
{ {
cp = tstr + 1; /* make sure it is set */
if (tstr[1] != endc && tstr[1] != ':') { if (tstr[1] != endc && tstr[1] != ':') {
if (tstr[2] == endc || tstr[2] == ':') { if (tstr[1] == 's') {
/*
* Use the char (if any) at tstr[2]
* as the word separator.
*/
VarPattern pattern;
if (tstr[3] == endc || tstr[3] == ':') {
varSpace = tstr[2];
cp = tstr + 3;
} else if (tstr[2] == endc || tstr[2] == ':') {
varSpace = 0; /* no separator */
cp = tstr + 2;
} else if (tstr[2] == '\\') {
switch (tstr[3]) {
case 'n':
varSpace = '\n';
cp = tstr + 4;
break;
case 't':
varSpace = '\t';
cp = tstr + 4;
break;
default:
if (isdigit(tstr[3])) {
char *ep;
varSpace = strtoul(&tstr[3], &ep, 0);
cp = ep;
} else {
goto bad_modifier;
}
break;
}
} else
break; /* not us */
termc = *cp;
/*
* We cannot be certain that VarModify
* will be used - even if there is a
* subsequent modifier, so do a no-op
* VarSubstitute now to for str to be
* re-expanded without the spaces.
*/
pattern.flags = VAR_SUB_ONE;
pattern.lhs = pattern.rhs = "\032";
pattern.leftLen = pattern.rightLen = 1;
newStr = VarModify(ctxt, str, VarSubstitute,
(ClientData)&pattern);
} else if (tstr[2] == endc || tstr[2] == ':') {
if (tstr[1] == 'u' || tstr[1] == 'l') { if (tstr[1] == 'u' || tstr[1] == 'l') {
newStr = VarChangeCase (nstr, (tstr[1] == 'u')); newStr = VarChangeCase (nstr, (tstr[1] == 'u'));
cp = tstr + 2; cp = tstr + 2;
termc = *cp; termc = *cp;
} else {
goto bad_modifier;
} }
} }
} }
@ -2464,6 +2526,7 @@ Var_Parse(const char *str, GNode *ctxt, Boolean err, int *lengthPtr,
*lengthPtr = cp - start + 1; *lengthPtr = cp - start + 1;
VarREError(error, &pattern.re, "RE substitution error"); VarREError(error, &pattern.re, "RE substitution error");
free(pattern.replace); free(pattern.replace);
varSpace = ' '; /* reset this */
return (var_Error); return (var_Error);
} }
@ -2706,9 +2769,15 @@ Var_Parse(const char *str, GNode *ctxt, Boolean err, int *lengthPtr,
free((Address)v->name); free((Address)v->name);
free((Address)v); free((Address)v);
} }
varSpace = ' '; /* reset this */
return (nstr); return (nstr);
bad_modifier:
Error("Bad modifier `:%.*s' for %s", (int)strcspn(tstr, ":)}"), tstr,
v->name);
cleanup: cleanup:
varSpace = ' '; /* reset this */
*lengthPtr = cp - start + 1; *lengthPtr = cp - start + 1;
if (*freePtr) if (*freePtr)
free(nstr); free(nstr);