bin/61673: make(1) word splitting swallows quotes

Oliver Eikemeier eikemeier at fillmore-labs.com
Wed Jan 21 04:00:42 PST 2004


>Number:         61673
>Category:       bin
>Synopsis:       make(1) word splitting swallows quotes
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jan 21 04:00:31 PST 2004
>Closed-Date:
>Last-Modified:
>Originator:     Oliver Eikemeier
>Release:        FreeBSD 4.9-STABLE i386
>Organization:
Fillmore Labs - http://www.fillmore-labs.com
>Environment:
System: FreeBSD nuuk.fillmore-labs.com 4.9-STABLE

>Description:

Make reduces two (empty) quotes to one if they are not the first word
in a string. This work with :S, :C, :M and others and is true for double
and single quotes.

>How-To-Repeat:

* Makefile:

QUOTETEST1=	""
QUOTETEST2=	"" ab
QUOTETEST3=	ab ""
QUOTETEST4=	ab "" cd
QUOTETEST5=	ab "" "cd  - ef" "" jk

all:
	@echo 'QUOTETEST1: ${QUOTETEST1:M*}'
	@echo 'QUOTETEST2: ${QUOTETEST2:M*}'
	@echo 'QUOTETEST3: ${QUOTETEST3:M*}'
	@echo 'QUOTETEST4: ${QUOTETEST4:M*}'
	@echo 'QUOTETEST5a: ${QUOTETEST5:S/^/</:S/$/>/}'
	@echo 'QUOTETEST5b: ${QUOTETEST5:S/$/>/:S/^/</}'

* Result:

QUOTETEST1: ""
QUOTETEST2: "" ab
QUOTETEST3: ab "
QUOTETEST4: ab " cd
QUOTETEST5a: <ab> <" <"cd> -> ef" <"> <jk>
QUOTETEST5b: <ab> <"> "cd <- <ef"> "> <jk>

* Expected result:

QUOTETEST1: ""
QUOTETEST2: "" ab
QUOTETEST3: ab ""
QUOTETEST4: ab "" cd
QUOTETEST5a: <ab> <""> <"cd - ef"> <""> <jk>
QUOTETEST5b: <ab> <""> <"cd - ef"> <""> <jk>

>Fix:

It looks like brk_string in src/usr.bin/make/str.c is responsible for that:

inquote = '\0';
for (p = str, start = t = buffer;; ++p) {
	switch(ch = *p) {
	case '"':
	case '\'':
		if (inquote) {
			if (inquote == ch)
				inquote = '\0';
			else
				break;
		} else {
			inquote = (char) ch;
			/* Don't miss "" or '' */
			if (start == NULL && p[1] == inquote) {
				start = t + 1;
				break;
			}
		}
		if (!expand) {
			if (!start)
				start = t;
			*t++ = ch;
		}
		continue;
	[...]
	}
	if (!start)
		start = t;
	*t++ = (char) ch;
}

Essentially the part after /* Don't miss "" or '' */.
Maybe something like this will do the trick:

inquote = '\0';
start = (char *)NULL;
for (p = str, t = buffer;; ++p) {
	switch(ch = *p) {
	case '"':
	case '\'':
		if (inquote) {
			if (inquote == ch)
				inquote = '\0';
			else
				break;
		} else {
			inquote = (char) ch;
		}
		if (!expand) {
			if (!start)
				start = t;
			*t++ = ch;
		}
		continue;
	[...]
	}
	if (!start)
		start = t;
	*t++ = (char) ch;
}

>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list