1*1d3f2ddcSSimon J. Gerraty# $NetBSD: var-op-sunsh.mk,v 1.10 2022/02/09 21:09:24 rillig Exp $ 2956e45f6SSimon J. Gerraty# 3956e45f6SSimon J. Gerraty# Tests for the :sh= variable assignment operator, which runs its right-hand 4956e45f6SSimon J. Gerraty# side through the shell. It is a seldom-used alternative to the != 5*1d3f2ddcSSimon J. Gerraty# assignment operator, adopted from Sun make. 6956e45f6SSimon J. Gerraty 7956e45f6SSimon J. Gerraty.MAKEFLAGS: -dL # Enable sane error messages 8956e45f6SSimon J. Gerraty 9956e45f6SSimon J. Gerraty# This is the idiomatic form of the Sun shell assignment operator. 10956e45f6SSimon J. Gerraty# The assignment operator is directly preceded by the ':sh'. 11956e45f6SSimon J. GerratyVAR:sh= echo colon-sh 12956e45f6SSimon J. Gerraty.if ${VAR} != "colon-sh" 13956e45f6SSimon J. Gerraty. error 14956e45f6SSimon J. Gerraty.endif 15956e45f6SSimon J. Gerraty 16956e45f6SSimon J. Gerraty# It is also possible to have whitespace around the :sh assignment 17956e45f6SSimon J. Gerraty# operator modifier. 18956e45f6SSimon J. GerratyVAR :sh = echo colon-sh-spaced 19956e45f6SSimon J. Gerraty.if ${VAR} != "colon-sh-spaced" 20956e45f6SSimon J. Gerraty. error 21956e45f6SSimon J. Gerraty.endif 22956e45f6SSimon J. Gerraty 23956e45f6SSimon J. Gerraty# Until 2020-10-04, the ':sh' could even be followed by other characters. 24956e45f6SSimon J. Gerraty# This was neither documented by NetBSD make nor by Solaris make and was 25956e45f6SSimon J. Gerraty# an implementation error. 26956e45f6SSimon J. Gerraty# 279f45a3c8SSimon J. Gerraty# Since 2020-10-04, this is a normal variable assignment to the variable named 289f45a3c8SSimon J. Gerraty# 'VAR:shell', using the '=' assignment operator. 29956e45f6SSimon J. GerratyVAR:shell= echo colon-shell 309f45a3c8SSimon J. Gerraty# The variable name needs to be generated using a ${:U...} expression because 319f45a3c8SSimon J. Gerraty# it is not possible to express the ':' as part of a literal variable name, 329f45a3c8SSimon J. Gerraty# see ParseVarname. 33956e45f6SSimon J. Gerraty.if ${${:UVAR\:shell}} != "echo colon-shell" 34956e45f6SSimon J. Gerraty. error 35956e45f6SSimon J. Gerraty.endif 36956e45f6SSimon J. Gerraty 37956e45f6SSimon J. Gerraty# Several colons can syntactically appear in a variable name. 38956e45f6SSimon J. Gerraty# Until 2020-10-04, the last of them was interpreted as the ':sh' 39956e45f6SSimon J. Gerraty# assignment operator. 40956e45f6SSimon J. Gerraty# 41956e45f6SSimon J. Gerraty# Since 2020-10-04, the colons are part of the variable name. 42956e45f6SSimon J. GerratyVAR:shoe:shore= echo two-colons 43956e45f6SSimon J. Gerraty.if ${${:UVAR\:shoe\:shore}} != "echo two-colons" 44956e45f6SSimon J. Gerraty. error 45956e45f6SSimon J. Gerraty.endif 46956e45f6SSimon J. Gerraty 47956e45f6SSimon J. Gerraty# Until 2020-10-04, the following expression was wrongly marked as 48956e45f6SSimon J. Gerraty# a parse error. This was because the parser for variable assignments 49956e45f6SSimon J. Gerraty# just looked for the previous ":sh", without taking any contextual 50956e45f6SSimon J. Gerraty# information into account. 51956e45f6SSimon J. Gerraty# 52956e45f6SSimon J. Gerraty# There are two different syntactical elements that look exactly the same: 53956e45f6SSimon J. Gerraty# The variable modifier ':sh' and the assignment operator modifier ':sh'. 54956e45f6SSimon J. Gerraty# Intuitively this variable name contains the variable modifier, but until 55956e45f6SSimon J. Gerraty# 2020-10-04, the parser regarded it as an assignment operator modifier, in 56b0c40a00SSimon J. Gerraty# Parse_Var. 57956e45f6SSimon J. GerratyVAR.${:Uecho 123:sh}= ok-123 58956e45f6SSimon J. Gerraty.if ${VAR.123} != "ok-123" 59956e45f6SSimon J. Gerraty. error 60956e45f6SSimon J. Gerraty.endif 61956e45f6SSimon J. Gerraty 62956e45f6SSimon J. Gerraty# Same pattern here. Until 2020-10-04, the ':sh' inside the nested expression 63956e45f6SSimon J. Gerraty# was taken for the :sh assignment operator modifier, even though it was 64956e45f6SSimon J. Gerraty# escaped by a backslash. 65956e45f6SSimon J. GerratyVAR.${:U echo\:shell}= ok-shell 66956e45f6SSimon J. Gerraty.if ${VAR.${:U echo\:shell}} != "ok-shell" 67956e45f6SSimon J. Gerraty. error 68956e45f6SSimon J. Gerraty.endif 69956e45f6SSimon J. Gerraty 70956e45f6SSimon J. Gerraty# Until 2020-10-04, the word 'shift' was also affected since it starts with 71956e45f6SSimon J. Gerraty# ':sh'. 72956e45f6SSimon J. GerratyVAR.key:shift= Shift 73956e45f6SSimon J. Gerraty.if ${${:UVAR.key\:shift}} != "Shift" 74956e45f6SSimon J. Gerraty. error 75956e45f6SSimon J. Gerraty.endif 76956e45f6SSimon J. Gerraty 77956e45f6SSimon J. Gerraty# Just for fun: The code in Parse_IsVar allows for multiple appearances of 78956e45f6SSimon J. Gerraty# the ':sh' assignment operator modifier. Let's see what happens ... 79956e45f6SSimon J. Gerraty# 80956e45f6SSimon J. Gerraty# Well, the end result is correct but the way until there is rather 81b0c40a00SSimon J. Gerraty# adventurous. This only works because the parser replaces each and every 82b0c40a00SSimon J. Gerraty# whitespace character that is not nested with '\0' (see Parse_Var). 83956e45f6SSimon J. Gerraty# The variable name therefore ends before the first ':sh', and the last 84956e45f6SSimon J. Gerraty# ':sh' turns the assignment operator into the shell command evaluation. 85b0c40a00SSimon J. Gerraty# Parse_Var completely trusts Parse_IsVar to properly verify the syntax. 86956e45f6SSimon J. Gerraty# 87956e45f6SSimon J. Gerraty# The ':sh' is the only word that may occur between the variable name and 88956e45f6SSimon J. Gerraty# the assignment operator at nesting level 0. All other words would lead 89956e45f6SSimon J. Gerraty# to a parse error since the left-hand side of an assignment must be 90956e45f6SSimon J. Gerraty# exactly one word. 91956e45f6SSimon J. GerratyVAR :sh :sh :sh :sh= echo multiple 92956e45f6SSimon J. Gerraty.if ${VAR} != "multiple" 93956e45f6SSimon J. Gerraty. error 94956e45f6SSimon J. Gerraty.endif 95956e45f6SSimon J. Gerraty 96956e45f6SSimon J. Gerraty# The word ':sh' is not the only thing that can occur after a variable name. 97956e45f6SSimon J. Gerraty# Since the parser just counts braces and parentheses instead of properly 98956e45f6SSimon J. Gerraty# expanding nested expressions, the token ' :sh' can be used to add arbitrary 99956e45f6SSimon J. Gerraty# text between the variable name and the assignment operator, it just has to 100956e45f6SSimon J. Gerraty# be enclosed in braces or parentheses. 1019f45a3c8SSimon J. Gerraty# 1029f45a3c8SSimon J. Gerraty# Since the text to the left of the assignment operator '=' does not end with 1039f45a3c8SSimon J. Gerraty# ':sh', the effective assignment operator becomes '=', not '!='. 104956e45f6SSimon J. GerratyVAR :sh(Put a comment here)= comment in parentheses 105956e45f6SSimon J. Gerraty.if ${VAR} != "comment in parentheses" 106956e45f6SSimon J. Gerraty. error 107956e45f6SSimon J. Gerraty.endif 108956e45f6SSimon J. Gerraty 109956e45f6SSimon J. Gerraty# The unintended comment can include multiple levels of nested braces and 1109f45a3c8SSimon J. Gerraty# parentheses. Braces and parentheses are interchangeable, that is, a '(' can 1119f45a3c8SSimon J. Gerraty# be closed by either ')' or '}'. These braces and parentheses are only 1129f45a3c8SSimon J. Gerraty# counted by Parse_IsVar, in particular Parse_Var doesn't see them. 113956e45f6SSimon J. GerratyVAR :sh{Put}((((a}{comment}}}}{here}= comment in braces 114956e45f6SSimon J. Gerraty.if ${VAR} != "comment in braces" 115956e45f6SSimon J. Gerraty. error 116956e45f6SSimon J. Gerraty.endif 117956e45f6SSimon J. Gerraty 1189f45a3c8SSimon J. Gerraty# The assignment modifier ':sh' can be combined with the assignment operator 1199f45a3c8SSimon J. Gerraty# '+='. In such a case the ':sh' is silently ignored, and the effective 1209f45a3c8SSimon J. Gerraty# assignment operator is '+='. 121956e45f6SSimon J. Gerraty# 1229f45a3c8SSimon J. Gerraty# XXX: This combination should not be allowed at all, as it is confusing. 123956e45f6SSimon J. GerratyVAR= one 124956e45f6SSimon J. GerratyVAR :sh += echo two 125956e45f6SSimon J. Gerraty.if ${VAR} != "one echo two" 126956e45f6SSimon J. Gerraty. error ${VAR} 127956e45f6SSimon J. Gerraty.endif 128956e45f6SSimon J. Gerraty 1299f45a3c8SSimon J. Gerraty# The assignment modifier ':sh' can be combined with the assignment operator 1309f45a3c8SSimon J. Gerraty# '!='. In such a case the ':sh' is silently ignored, and the effective 1319f45a3c8SSimon J. Gerraty# assignment operator is '!=', just like with '+=' or the other compound 1329f45a3c8SSimon J. Gerraty# assignment operators. 1339f45a3c8SSimon J. Gerraty# 1349f45a3c8SSimon J. Gerraty# XXX: This combination should not be allowed at all, as it is confusing. 1359f45a3c8SSimon J. GerratyVAR :sh != echo echo echo echo spaces-around 1369f45a3c8SSimon J. Gerraty.if ${VAR} != "echo echo echo spaces-around" 1379f45a3c8SSimon J. Gerraty. error ${VAR} 1389f45a3c8SSimon J. Gerraty.endif 139e2eeea75SSimon J. Gerraty 1409f45a3c8SSimon J. Gerraty# If there is no space between the variable name and the assignment modifier 1419f45a3c8SSimon J. Gerraty# ':sh', the ':sh' becomes part of the variable name, as the parser only 1429f45a3c8SSimon J. Gerraty# expects a single assignment modifier to the left of the '=', which in this 1439f45a3c8SSimon J. Gerraty# case is the '!'. 1449f45a3c8SSimon J. GerratyVAR:sh != echo echo echo echo space-after 1459f45a3c8SSimon J. Gerraty.if ${${:UVAR\:sh}} != "echo echo echo space-after" 1469f45a3c8SSimon J. Gerraty. error ${${:UVAR\:sh}} 1479f45a3c8SSimon J. Gerraty.endif 1489f45a3c8SSimon J. Gerraty 1499f45a3c8SSimon J. Gerratyall: .PHONY 150