scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | Submodules | README | LICENSE

commit 534a9fa01a8a0c080df60800a370421dc594532c
parent d94c6547755e253374bb6da489b5c01f69680dbc
Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
Date:   Sun, 15 Feb 2026 11:12:28 +0100

make: Escape values passed in MAKEFLAGS

The POSIX standard says:

	The macro= value macro definition operands can also be
	included. The difference between the contents of MAKEFLAGS
	and the make utility command line is that the contents of the
	variable shall not be subjected to the word expansions (see Word
	Expansions) associated with parsing the command line values.

And it basically means that we have to escape the parameters passed
in MAKEFLAGS and then unescape and doing the word spliting. The code
had a naive strtok() implementation for word spliting, and it had to
be replaced for a more complex parsing function which also handles
escapes and quotes with ' and ".

Diffstat:
Msrc/cmd/scc-make/main.c | 92++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Atests/make/execute/0109-makeflags.sh | 18++++++++++++++++++
Atests/make/execute/test2.mk | 15+++++++++++++++
3 files changed, 117 insertions(+), 8 deletions(-)

diff --git a/src/cmd/scc-make/main.c b/src/cmd/scc-make/main.c @@ -1,3 +1,4 @@ +#include <ctype.h> #include <errno.h> #include <signal.h> #include <stdarg.h> @@ -141,6 +142,44 @@ appendmakeflags(char *text) free(t); } +static void +appendmacro(char *text) +{ + int n, idlen; + char *p, *t, *s; + + n = strlen(text) + 2; + for (p = text; p = strchr(p, '\''); ++p) + n++; + s = emalloc(n + 1); + + p = strchr(text, '='); + idlen = p - text; + + memcpy(s, text, idlen); + s[idlen] = '\0'; + + if (strpbrk(s, " \t\v\n'\"")) { + fprintf(stderr, "make: invalid macro name '%s'\n", text); + exit(EXIT_FAILURE); + } + + p = s + idlen; + *p++ = '='; + + *p++ = '\''; + for (t = text + idlen + 1; *t; ++t) { + if (*t == '\'') + *p++ = '\\'; + *p++ = *t; + } + *p++ = '\''; + + *p = '\0'; + appendmakeflags(s); + free(s); +} + static int hasargs(int c) { @@ -183,7 +222,7 @@ parseflag(int flag, char **args, char ***argv) case 'r': addtarget(".SUFFIXES", 0); appendmakeflags("-r"); - break; + break; case 'S': kflag = 0; appendmakeflags("-S"); @@ -216,7 +255,7 @@ assign(char *s, int where, int export) pos = t - s; - appendmakeflags(s); + appendmacro(s); t = estrdup(s); t[pos] = '\0'; @@ -253,8 +292,8 @@ parseargv(char **argv, char ***targets, int where, int export) static void parsemakeflags(void) { - int c, n; - char *s, *flags, **arr; + int esc, c, n; + char *t, *s, *buf, *flags, **arr; if ((flags = getenv("MAKEFLAGS")) == NULL) return; @@ -272,16 +311,53 @@ parsemakeflags(void) } else { n = 0; arr = NULL; - for (s = strtok(flags, " \t"); s; s = strtok(NULL, " \t")) { + buf = emalloc(strlen(flags) + 1); + + for (s = flags; *s; ) { + while (isspace(*s)) + ++s; + + t = buf; + for (esc = 0; c = *s; ++s) { + if (!esc && isspace(c)) + break; + if (!esc && (c == '\'' || c == '"')) { + esc = c; + continue; + } + if (c == esc) { + esc = 0; + continue; + } + if (c == '\\') + ++s; + if ((*t++ = *s) == '\0') { + fputs("make: trailing \\ in MAKEFLAGS\n", + stderr); + exit(EXIT_FAILURE); + } + } + if (esc) { + fputs("make: no closing quote\n", stderr); + exit(EXIT_FAILURE); + } + if (t == buf) + continue; + *t = '\0'; + n++; arr = erealloc(arr, sizeof(char *) * (n+1)); - arr[n-1] = s; + arr[n-1] = estrdup(buf); arr[n] = NULL; } + free(buf); - if (arr) + if (arr) { parseargv(arr, NULL, MAKEFLAGS, NOEXPORT); - free(arr); + while (n-- > 0) + free(arr[n]); + free(arr); + } } } diff --git a/tests/make/execute/0109-makeflags.sh b/tests/make/execute/0109-makeflags.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +trap 'rm -f $tmp' EXIT +trap 'exit $?' HUP INT TERM + +tmp=$$.tmp + +$EXEC scc make -f test2.mk MOREOBJS='a.o b.o' <<'EOF' > $tmp + +diff -u - $tmp <<EOF +MAKEFLAGS=MOREOBJS=a.o b.o +MOREOBJS=a.o b.o +OBJS= a.o b.o main.o +MAKEFLAGS=MOREOBJS='a.o b.o' +MAKEFLAGS=MOREOBJS=a.o b.o +MOREOBJS=a.o b.o +OBJS= a.o b.o main.o +EOF diff --git a/tests/make/execute/test2.mk b/tests/make/execute/test2.mk @@ -0,0 +1,15 @@ +OBJS=\ + $(MOREOBJS)\ + main.o + +all: + @echo MAKEFLAGS='$(MAKEFLAGS)' + @echo MOREOBJS='$(MOREOBJS)' + @echo OBJS='$(OBJS)' + @$(MAKE) -f test2.mk debug + +debug: + @env | grep MAKEFLAGS + @echo MAKEFLAGS='$(MAKEFLAGS)' + @echo MOREOBJS='$(MOREOBJS)' + @echo OBJS='$(OBJS)'