summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog30
-rw-r--r--NEWS8
-rw-r--r--function.c2668
-rw-r--r--job.c31
-rw-r--r--make.texinfo67
5 files changed, 1609 insertions, 1195 deletions
diff --git a/ChangeLog b/ChangeLog
index b0d344d..2d23dac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+1999-03-22 Paul D. Smith <psmith@gnu.org>
+
+ * make.texinfo (Functions): Add a new section documenting the new
+ $(error ...) and $(warning ...) functions. Also updated copyright
+ dates.
+ * NEWS: Updated for the new functions.
+ * function.c (func_error): Implement the new $(error ...) and
+ $(warning ...) functions.
+ (function_table): Insert new functions into the table.
+ (func_firstword): Don't call find_next_token() with argv[0]
+ itself, since that function modifies the pointer.
+ * function.c: Cleanups and slight changes to the new method of
+ calling functions.
+
+1999-03-20 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+
+ * function.c: Rewrite to use one C function per make function,
+ instead of a huge switch statement. Also allows some cleanup of
+ multi-architecture issues.
+
+1999-03-19 Eli Zaretskii <eliz@is.elta.co.il>
+1999-03-19 Rob Tulloh <rob_tulloh@dev.tivoli.com>
+
+ * job.c (construct_command_argv_internal): Don't treat _all_
+ backslashes as escapes, only those which really escape a special
+ character. This allows most normal "\" directory separators to be
+ treated normally.
+
1999-03-05 Paul D. Smith <psmith@gnu.org>
* configure.in: Check for a system strdup().
@@ -17,7 +45,7 @@
1999-03-04 Paul D. Smith <psmith@gnu.org>
* amiga.c, amiga.h, ar.c, arscan.c, commands.c, commands.h,
- * default.c, dep.h, dir.c, expand.c, file.c, filedef.h, functions.c,
+ * default.c, dep.h, dir.c, expand.c, file.c, filedef.h, function.c,
* implicit.c, job.c, job.h, main.c, make.h, misc.c, read.c, remake.c
* remote-cstms.c, remote-stub.c, rule.h, variable.c, variable.h,
* vpath.c, Makefile.ami, NMakefile.template, build.template,
diff --git a/NEWS b/NEWS
index fcff5f2..c7044b6 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,6 @@
GNU make NEWS -*-indented-text-*-
History of user-visible changes.
- 22 Feb 1999
+ 22 Mar 1999
Copyright (C) 1992,93,94,95,96,97,98,1999 Free Software Foundation, Inc.
See the end for copying conditions.
@@ -12,6 +12,12 @@ Please send GNU make bug reports to bug-make@gnu.org.
Version 3.78
+* Two new functions, $(error ...) and $(warning ...) are provided. The
+ former will cause make to fail and exit immediately upon expansion of
+ the function, with the text provided as the error message. The latter
+ causes the text provided to be printed as a warning message, but make
+ proceeds normally.
+
* Make defines a new variable, .LIBPATTERNS. This variable controls how
the link library dependency expansion (dependencies like ``-lfoo'') is
performed.
diff --git a/function.c b/function.c
index def3863..2183cd8 100644
--- a/function.c
+++ b/function.c
@@ -1,4 +1,4 @@
-/* Variable function expansion for GNU Make.
+/* Builtin function expansion for GNU Make.
Copyright (C) 1988,89,91,92,93,94,95,96,97 Free Software Foundation, Inc.
This file is part of GNU Make.
@@ -27,13 +27,19 @@ Boston, MA 02111-1307, USA. */
#ifdef _AMIGA
#include "amiga.h"
#endif
-#ifdef WINDOWS32
-#include <windows.h>
-#include <io.h>
-#include "sub_proc.h"
-#endif
-static char *string_glob PARAMS ((char *line));
+
+struct function_table_entry
+ {
+ const char *name;
+ int len;
+ int required_arguments;
+ int expand_all_arguments;
+ char *(*func_ptr) PARAMS((char *output, char **argv, const char*funcname));
+ };
+
+static struct function_table_entry function_table[];
+
/* Store into VARIABLE_BUFFER at O the result of scanning TEXT and replacing
each occurrence of SUBST with REPLACE. TEXT is null-terminated. SLEN is
@@ -214,75 +220,29 @@ patsubst_expand (o, text, pattern, replace, pattern_percent, replace_percent)
return o;
}
-/* Handle variable-expansion-time functions such as $(dir foo/bar) ==> foo/ */
-/* These enumeration constants distinguish the
- various expansion-time built-in functions. */
-
-enum function
- {
- function_subst,
- function_addsuffix,
- function_addprefix,
- function_dir,
- function_notdir,
- function_suffix,
- function_basename,
- function_wildcard,
- function_firstword,
- function_word,
- function_words,
- function_wordlist,
- function_findstring,
- function_strip,
- function_join,
- function_patsubst,
- function_filter,
- function_filter_out,
- function_foreach,
- function_sort,
- function_origin,
- function_shell,
- function_invalid
- };
+/* Look up a function by name.
+ The table is currently small enough that it's not really worthwhile to use
+ a fancier lookup algorithm. If it gets larger, maybe...
+*/
-/* Greater than the length of any function name. */
-#define MAXFUNCTIONLEN 11
+static const struct function_table_entry *
+lookup_function (table, s)
+ const struct function_table_entry *table;
+ const char *s;
+{
+ int len = strlen(s);
-/* The function names and lengths of names, for looking them up. */
+ for (; table->name != NULL; ++table)
+ if (table->len <= len
+ && (isblank (s[table->len]) || s[table->len] == '\0')
+ && strneq (s, table->name, table->len))
+ return table;
-static struct
- {
- char *name;
- unsigned int len;
- enum function function;
- } function_table[] =
- {
- { "subst", 5, function_subst },
- { "addsuffix", 9, function_addsuffix },
- { "addprefix", 9, function_addprefix },
- { "dir", 3, function_dir },
- { "notdir", 6, function_notdir },
- { "suffix", 6, function_suffix },
- { "basename", 8, function_basename },
- { "wildcard", 8, function_wildcard },
- { "firstword", 9, function_firstword },
- { "word", 4, function_word },
- { "words", 5, function_words },
- { "wordlist", 8, function_wordlist },
- { "findstring", 10, function_findstring },
- { "strip", 5, function_strip },
- { "join", 4, function_join },
- { "patsubst", 8, function_patsubst },
- { "filter", 6, function_filter },
- { "filter-out", 10, function_filter_out },
- { "foreach", 7, function_foreach },
- { "sort", 4, function_sort },
- { "origin", 6, function_origin },
- { "shell", 5, function_shell },
- { 0, 0, function_invalid }
- };
+ return NULL;
+}
+
/* Return 1 if PATTERN matches STR, 0 if not. */
int
@@ -294,9 +254,9 @@ pattern_matches (pattern, percent, str)
if (percent == 0)
{
unsigned int len = strlen (pattern) + 1;
- char *new = (char *) alloca (len);
- bcopy (pattern, new, len);
- pattern = new;
+ char *new_chars = (char *) alloca (len);
+ bcopy (pattern, new_chars, len);
+ pattern = new_chars;
percent = find_percent (pattern);
if (percent == 0)
return streq (pattern, str);
@@ -312,1288 +272,1626 @@ pattern_matches (pattern, percent, str)
return !strcmp (percent + 1, str + (strlength - sfxlen));
}
-int shell_function_pid = 0, shell_function_completed;
-/* Perform the function specified by FUNCTION on the text at TEXT.
- END is points to the end of the argument text (exclusive).
- The output is written into VARIABLE_BUFFER starting at O. */
+/* Find the next comma or ENDPAREN (counting nested STARTPAREN and
+ ENDPARENtheses), starting at PTR before END. Return a pointer to
+ next character.
+
+ If no next argument is found, return NULL.
+*/
+
+static char *
+find_next_argument (startparen, endparen, ptr, end)
+ char startparen;
+ char endparen;
+ const char *ptr;
+ const char *end;
+{
+ int count = 0;
+
+ for (; ptr < end; ++ptr)
+ if (*ptr == startparen)
+ ++count;
-/* Note this absorbs a semicolon and is safe to use in conditionals. */
-#define BADARGS(func) do { \
- fatal (reading_file, "insufficient arguments to function `%s'", func); \
- } while (0)
+ else if (*ptr == endparen)
+ {
+ --count;
+ if (count < 0)
+ return NULL;
+ }
+
+ else if (*ptr == ',' && !count)
+ return (char *)ptr;
+
+ /* We didn't find anything. */
+ return NULL;
+}
+
static char *
-expand_function (o, function, text, end)
+expand_builtin_function (o, argc, argv, entry_p)
char *o;
- enum function function;
- char *text;
- char *end;
+ int argc;
+ char **argv;
+ struct function_table_entry *entry_p;
+{
+ if (argc < entry_p->required_arguments && entry_p->required_arguments >= 0)
+ fatal (reading_file,
+ "Insufficient number of arguments (%d) to function `%s'",
+ argc, entry_p->name);
+
+ if (!entry_p->func_ptr)
+ fatal (reading_file, "Unimplemented on this platform: function `%s'",
+ entry_p->name);
+
+ return entry_p->func_ptr (o, argv, entry_p->name);
+}
+
+/* Check for a function invocation in *STRINGP. *STRINGP points at the
+ opening ( or { and is not null-terminated. If a function invocation
+ is found, expand it into the buffer at *OP, updating *OP, incrementing
+ *STRINGP past the reference and returning nonzero. If not, return zero. */
+
+int
+handle_function (op, stringp)
+ char **op;
+ char **stringp;
+{
+ const struct function_table_entry *entry_p;
+ char openparen = (*stringp)[0];
+ char closeparen = openparen == '(' ? ')' : '}';
+ char *beg = *stringp + 1;
+ char *endref;
+ int count = 0;
+ char *argbeg;
+ register char *p;
+ char **argv, **argvp;
+ int nargs;
+
+ entry_p = lookup_function (function_table, beg);
+
+ if (!entry_p)
+ return 0;
+
+ /* We have found a call to a builtin function. Find the end of the
+ arguments, and do the function. */
+
+ endref = beg + entry_p->len;
+
+ /* Space after function name isn't part of the args. */
+ p = next_token (endref);
+ argbeg = p;
+
+ /* Find the end of the function invocation, counting nested use of
+ whichever kind of parens we use. Since we're looking, count commas
+ to get a rough estimate of how many arguments we might have. The
+ count might be high, but it'll never be low. */
+
+ for (nargs=1; *p != '\0'; ++p)
+ if (*p == ',')
+ ++nargs;
+ else if (*p == openparen)
+ ++count;
+ else if (*p == closeparen && --count < 0)
+ break;
+
+ if (count >= 0)
+ fatal (reading_file,
+ "unterminated call to function `%s': missing `%c'",
+ entry_p->name, closeparen);
+
+ /* Get some memory to store the arg pointers. */
+ argvp = argv = (char **) alloca (sizeof(char *) * (nargs + 2));
+
+ /* Chop the string into arguments, then store the end pointer and a nul. */
+ *argvp = argbeg;
+ nargs = 1;
+ while (1)
+ {
+ char *next = find_next_argument (openparen, closeparen, *argvp, p);
+
+ if (!next)
+ break;
+
+ *(++argvp) = next+1;
+ ++nargs;
+ }
+
+ *(++argvp) = p+1;
+ *(++argvp) = 0;
+
+ /* If we should expand, do it. */
+ if (entry_p->expand_all_arguments)
+ {
+ for (argvp=argv; argvp[1] != 0; ++argvp)
+ *argvp = expand_argument (*argvp, argvp[1]-1);
+
+ /* end pointer doesn't make sense for expanded stuff. */
+ *argvp = 0;
+ }
+
+ /* Finally! Run the function... */
+ *op = expand_builtin_function (*op, nargs, argv, entry_p);
+
+ /* If we allocated memory for the expanded args, free it again. */
+ if (entry_p->expand_all_arguments)
+ for (argvp=argv; *argvp != 0; ++argvp)
+ free (*argvp);
+
+ *stringp = p;
+
+ return 1;
+}
+
+
+/* Glob-expand LINE. The returned pointer is
+ only good until the next call to string_glob. */
+
+static char *
+string_glob (line)
+ char *line;
+{
+ static char *result = 0;
+ static unsigned int length;
+ register struct nameseq *chain;
+ register unsigned int idx;
+
+ chain = multi_glob (parse_file_seq
+ (&line, '\0', sizeof (struct nameseq),
+ /* We do not want parse_file_seq to strip `./'s.
+ That would break examples like:
+ $(patsubst ./%.c,obj/%.o,$(wildcard ./*.c)). */
+
+ /* Yep, and the preceding comment triggers a GCC
+ warning. Nothing wrong though. */
+ 0),
+ sizeof (struct nameseq));
+
+ if (result == 0)
+ {
+ length = 100;
+ result = (char *) xmalloc (100);
+ }
+
+ idx = 0;
+ while (chain != 0)
+ {
+ register char *name = chain->name;
+ unsigned int len = strlen (name);
+
+ struct nameseq *next = chain->next;
+ free ((char *) chain);
+ chain = next;
+
+ /* multi_glob will pass names without globbing metacharacters
+ through as is, but we want only files that actually exist. */
+ if (file_exists_p (name))
+ {
+ if (idx + len + 1 > length)
+ {
+ length += (len + 1) * 2;
+ result = (char *) xrealloc (result, length);
+ }
+ bcopy (name, &result[idx], len);
+ idx += len;
+ result[idx++] = ' ';
+ }
+
+ free (name);
+ }
+
+ /* Kill the last space and terminate the string. */
+ if (idx == 0)
+ result[0] = '\0';
+ else
+ result[idx - 1] = '\0';
+
+ return result;
+}
+
+/*
+ Builtin functions
+ */
+
+static char *
+func_patsubst (o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ o = patsubst_expand (o, argv[2], argv[0], argv[1], (char *) 0, (char *) 0);
+ return o;
+}
+
+
+static char *
+func_join(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
{
- char *p, *p2, *p3;
- unsigned int i, j, len;
int doneany = 0;
- int count;
- char endparen = *end, startparen = *end == ')' ? '(' : '{';
- switch (function)
+ /* Write each word of the first argument directly followed
+ by the corresponding word of the second argument.
+ If the two arguments have a different number of words,
+ the excess words are just output separated by blanks. */
+ register char *tp;
+ register char *pp;
+ char *list1_iterator = argv[0];
+ char *list2_iterator = argv[1];
+ do
{
- default:
- abort ();
- break;
+ unsigned int len1, len2;
+
+ tp = find_next_token (&list1_iterator, &len1);
+ if (tp != 0)
+ o = variable_buffer_output (o, tp, len1);
+
+ pp = find_next_token (&list2_iterator, &len2);
+ if (pp != 0)
+ o = variable_buffer_output (o, pp, len2);
-#ifndef VMS /* not supported for vms yet */
- case function_shell:
+ if (tp != 0 || pp != 0)
+ {
+ o = variable_buffer_output (o, " ", 1);
+ doneany = 1;
+ }
+ }
+ while (tp != 0 || pp != 0);
+ if (doneany)
+ /* Kill the last blank. */
+ --o;
+
+ return o;
+}
+
+
+static char *
+func_origin(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ /* Expand the argument. */
+ register struct variable *v = lookup_variable (argv[0], strlen (argv[0]));
+ if (v == 0)
+ o = variable_buffer_output (o, "undefined", 9);
+ else
+ switch (v->origin)
{
- char* batch_filename = NULL;
-#ifdef WINDOWS32
- SECURITY_ATTRIBUTES saAttr;
- HANDLE hIn;
- HANDLE hErr;
- HANDLE hChildOutRd;
- HANDLE hChildOutWr;
- HANDLE hProcess;
-#endif
+ default:
+ case o_invalid:
+ abort ();
+ break;
+ case o_default:
+ o = variable_buffer_output (o, "default", 7);
+ break;
+ case o_env:
+ o = variable_buffer_output (o, "environment", 11);
+ break;
+ case o_file:
+ o = variable_buffer_output (o, "file", 4);
+ break;
+ case o_env_override:
+ o = variable_buffer_output (o, "environment override", 20);
+ break;
+ case o_command:
+ o = variable_buffer_output (o, "command line", 12);
+ break;
+ case o_override:
+ o = variable_buffer_output (o, "override", 8);
+ break;
+ case o_automatic:
+ o = variable_buffer_output (o, "automatic", 9);
+ break;
+ }
+
+ return o;
+}
+
+#ifdef VMS
+#define IS_PATHSEP(c) ((c) == ']')
+#else
#ifdef __MSDOS__
- FILE *fpipe;
+#define IS_PATHSEP(c) ((c) == '/' || (c) == '\\')
+#else
+#define IS_PATHSEP(c) ((c) == '/')
#endif
- char **argv;
- char *error_prefix;
-#ifndef _AMIGA
- char **envp;
- int pipedes[2];
- int pid;
#endif
- /* Expand the command line. */
- text = expand_argument (text, end);
-#ifndef __MSDOS__
- /* Construct the argument list. */
- argv = construct_command_argv (text,
- (char **) NULL, (struct file *) 0, &batch_filename);
- if (argv == 0)
- break;
-#endif
+static char *
+func_notdir_suffix(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ /* Expand the argument. */
+ char *list_iterator = argv[0];
+ char *p2 =0;
+ int doneany =0;
+ int len=0;
+
+ int is_suffix = streq(funcname, "suffix");
+ int is_notdir = !is_suffix;
+ while ((p2 = find_next_token (&list_iterator, &len)) != 0)
+ {
+ char *p = p2 + len;
-#ifndef _AMIGA
- /* Using a target environment for `shell' loses in cases like:
- export var = $(shell echo foobie)
- because target_environment hits a loop trying to expand $(var)
- to put it in the environment. This is even more confusing when
- var was not explicitly exported, but just appeared in the
- calling environment. */
-#if 1
- envp = environ;
-#else
- /* Construct the environment. */
- envp = target_environment ((struct file *) 0);
-#endif
-#endif /* Not Amiga. */
- /* For error messages. */
- if (reading_file != 0)
- {
- error_prefix = (char *) alloca (strlen(reading_file->filenm)+100);
- sprintf (error_prefix,
- "%s:%lu: ", reading_file->filenm, reading_file->lineno);
- }
- else
- error_prefix = "";
+ while (p >= p2 && (!is_suffix || *p != '.'))
+ {
+ if (IS_PATHSEP (*p))
+ break;
+ --p;
+ }
-#ifndef _AMIGA
-# ifdef WINDOWS32
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
-
- if (DuplicateHandle(GetCurrentProcess(),
- GetStdHandle(STD_INPUT_HANDLE),
- GetCurrentProcess(),
- &hIn,
- 0,
- TRUE,
- DUPLICATE_SAME_ACCESS) == FALSE) {
- fatal (NILF, "create_child_process: DuplicateHandle(In) failed (e=%d)\n",
- GetLastError());
+ if (p >= p2)
+ {
+ if (is_notdir)
+ ++p;
+ else if (*p != '.')
+ continue;
+ o = variable_buffer_output (o, p, len - (p - p2));
}
- if (DuplicateHandle(GetCurrentProcess(),
- GetStdHandle(STD_ERROR_HANDLE),
- GetCurrentProcess(),
- &hErr,
- 0,
- TRUE,
- DUPLICATE_SAME_ACCESS) == FALSE) {
- fatal (NILF, "create_child_process: DuplicateHandle(Err) failed (e=%d)\n",
- GetLastError());
+#if defined(WINDOWS32) || defined(__MSDOS__)
+ /* Handle the case of "d:foo/bar". */
+ else if (streq(funcname, "notdir") && p2[0] && p2[1] == ':')
+ {
+ p = p2 + 2;
+ o = variable_buffer_output (o, p, len - (p - p2));
}
+#endif
+ else if (is_notdir)
+ o = variable_buffer_output (o, p2, len);
- if (!CreatePipe(&hChildOutRd, &hChildOutWr, &saAttr, 0))
- fatal (NILF, "CreatePipe() failed (e=%d)\n", GetLastError());
-
- hProcess = process_init_fd(hIn, hChildOutWr, hErr);
+ if (is_notdir || p >= p2)
+ {
+ o = variable_buffer_output (o, " ", 1);
+ doneany = 1;
+ }
+ }
+ if (doneany)
+ /* Kill last space. */
+ --o;
- if (!hProcess)
- fatal (NILF, "expand_function: process_init_fd() failed\n");
- else
- process_register(hProcess);
- /* make sure that CreateProcess() has Path it needs */
- sync_Path_environment();
+ return o;
- if (!process_begin(hProcess, argv, envp, argv[0], NULL))
- pid = (int) hProcess;
- else
- fatal (NILF, "expand_function: unable to launch process (e=%d)\n",
- process_last_err(hProcess));
+}
- /* set up to read data from child */
- pipedes[0] = _open_osfhandle((long) hChildOutRd, O_RDONLY);
- /* this will be closed almost right away */
- pipedes[1] = _open_osfhandle((long) hChildOutWr, O_APPEND);
-# else /* WINDOWS32 */
-# ifdef __MSDOS__
+static char *
+func_basename_dir(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ /* Expand the argument. */
+ char *p3 = argv[0];
+ char *p2=0;
+ int doneany=0;
+ int len=0;
+ char *p=0;
+ int is_basename= streq(funcname, "basename");
+ int is_dir= !is_basename;
+
+ while ((p2 = find_next_token (&p3, &len)) != 0)
{
- /* MSDOS can't fork, but it has `popen'.
- (Bwt, why isn't `popen' used in all the versions?) */
- struct variable *sh = lookup_variable ("SHELL", 5);
- int e;
- extern int dos_command_running, dos_status;
-
- /* Make sure not to bother processing an empty line. */
- while (isblank (*text))
- ++text;
- if (*text == '\0')
- break;
-
- if (sh)
+ p = p2 + len;
+ while (p >= p2 && (!is_basename || *p != '.'))
{
- char buf[PATH_MAX + 7];
- /* This makes sure $SHELL value is used by $(shell), even
- though the target environment is not passed to it. */
- sprintf (buf, "SHELL=%s", sh->value);
- putenv (buf);
+ if (IS_PATHSEP(*p))
+ break;
+ --p;
}
- e = errno;
- errno = 0;
- dos_command_running = 1;
- dos_status = 0;
- fpipe = popen (text, "rt");
- dos_command_running = 0;
- if (!fpipe || dos_status)
- {
- pipedes[0] = -1;
- pid = -1;
- if (dos_status)
- errno = EINTR;
- else if (errno == 0)
- errno = ENOMEM;
- shell_function_completed = -1;
- }
+ if (p >= p2 && (is_dir))
+ o = variable_buffer_output (o, p2, ++p - p2);
+ else if (p >= p2 && (*p == '.'))
+ o = variable_buffer_output (o, p2, p - p2);
+#if defined(WINDOWS32) || defined(__MSDOS__)
+ /* Handle the "d:foobar" case */
+ else if (p2[0] && p2[1] == ':' && is_dir)
+ o = variable_buffer_output (o, p2, 2);
+#endif
+ else if (is_dir)
+#ifdef VMS
+ o = variable_buffer_output (o, "[]", 2);
+#else
+#ifndef _AMIGA
+ o = variable_buffer_output (o, "./", 2);
+#else
+ ; /* Just a nop... */
+#endif /* AMIGA */
+#endif /* !VMS */
else
- {
- pipedes[0] = fileno (fpipe);
- pid = 42;
- errno = e;
- shell_function_completed = 1;
- }
+ /* The entire name is the basename. */
+ o = variable_buffer_output (o, p2, len);
+
+ o = variable_buffer_output (o, " ", 1);
+ doneany = 1;
}
- if (pipedes[0] < 0)
-# else /* ! __MSDOS__ */
- if (pipe (pipedes) < 0)
-# endif /* __MSDOS__ */
- {
- perror_with_name (error_prefix, "pipe");
- break;
- }
+ if (doneany)
+ /* Kill last space. */
+ --o;
-# ifndef __MSDOS__
- pid = vfork ();
- if (pid < 0)
- perror_with_name (error_prefix, "fork");
- else if (pid == 0)
- child_execute_job (0, pipedes[1], argv, envp);
- else
-# endif /* ! __MSDOS__ */
-# endif /* WINDOWS32 */
- {
- /* We are the parent. */
-
- char *buffer;
- unsigned int maxlen;
- int cc;
-#if 0
- for (i = 0; envp[i] != 0; ++i)
- free (envp[i]);
- free ((char *) envp);
-#endif
- /* Record the PID for reap_children. */
- shell_function_pid = pid;
-#ifndef __MSDOS__
- shell_function_completed = 0;
+ return o;
+}
- /* Free the storage only the child needed. */
- free (argv[0]);
- free ((char *) argv);
+static char *
+func_addsuffix_addprefix(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ int fixlen = strlen (argv[0]);
+ char *list_iterator = argv[1];
+ int is_addprefix = streq (funcname, "addprefix");
+ int is_addsuffix = !is_addprefix;
- /* Close the write side of the pipe. */
- (void) close (pipedes[1]);
-#endif
+ int doneany =0;
+ char *p=0;
+ int len =0;
- /* Set up and read from the pipe. */
-
- maxlen = 200;
- buffer = (char *) xmalloc (maxlen + 1);
-
- /* Read from the pipe until it gets EOF. */
- i = 0;
- do
- {
- if (i == maxlen)
- {
- maxlen += 512;
- buffer = (char *) xrealloc (buffer, maxlen + 1);
- }
-
- errno = 0;
- cc = read (pipedes[0], &buffer[i], maxlen - i);
- if (cc > 0)
- i += cc;
- }
-#ifdef EINTR
- while (cc > 0 || errno == EINTR);
-#else
- while (cc > 0);
-#endif
+ while ((p = find_next_token (&list_iterator, &len)) != 0)
+ {
+ if (is_addprefix)
+ o = variable_buffer_output (o, argv[0], fixlen);
+ o = variable_buffer_output (o, p, len);
+ if (is_addsuffix)
+ o = variable_buffer_output (o, argv[0], fixlen);
+ o = variable_buffer_output (o, " ", 1);
+ doneany = 1;
+ }
- /* Close the read side of the pipe. */
-#ifdef __MSDOS__
- if (fpipe)
- (void) pclose (fpipe);
-#else
- (void) close (pipedes[0]);
-#endif
+ if (doneany)
+ /* Kill last space. */
+ --o;
- /* Loop until child_handler sets shell_function_completed
- to the status of our child shell. */
- while (shell_function_completed == 0)
- reap_children (1, 0);
-
- if (batch_filename) {
- if (debug_flag)
- printf("Cleaning up temporary batch file %s\n", batch_filename);
- remove(batch_filename);
- free(batch_filename);
- }
- shell_function_pid = 0;
-
- /* The child_handler function will set shell_function_completed
- to 1 when the child dies normally, or to -1 if it
- dies with status 127, which is most likely an exec fail. */
-
- if (shell_function_completed == -1)
- {
- /* This most likely means that the execvp failed,
- so we should just write out the error message
- that came in over the pipe from the child. */
- fputs (buffer, stderr);
- fflush (stderr);
- }
- else
- {
- /* The child finished normally. Replace all
- newlines in its output with spaces, and put
- that in the variable output buffer. */
- if (i > 0)
- {
- if (buffer[i - 1] == '\n')
- {
- if (i > 1 && buffer[i - 2] == '\r')
- --i;
- buffer[--i] = '\0';
- }
- else
- buffer[i] = '\0';
-
- p = buffer;
- for (p2=p; *p != '\0'; ++p)
- {
- if (p[0] == '\r' && p[1] == '\n')
- continue;
- if (*p == '\n')
- *p2++ = ' ';
- else
- *p2++ = *p;
- }
- *p2 = '\0';
- o = variable_buffer_output (o, buffer, i);
- }
- }
-
- free (buffer);
- }
-#else /* Amiga */
- {
- /* Amiga can't fork nor spawn, but I can start a program with
- redirection of my choice. However, this means that we
- don't have an opportunity to reopen stdout to trap it. Thus,
- we save our own stdout onto a new descriptor and dup a temp
- file's descriptor onto our stdout temporarily. After we
- spawn the shell program, we dup our own stdout back to the
- stdout descriptor. The buffer reading is the same as above,
- except that we're now reading from a file. */
-#include <dos/dos.h>
-#include <proto/dos.h>
+ return o;
+}
- BPTR child_stdout;
- char tmp_output[FILENAME_MAX];
- unsigned int maxlen = 200;
- int cc;
- char * buffer, * ptr;
- char ** aptr;
- int len = 0;
-
- strcpy (tmp_output, "t:MakeshXXXXXXXX");
- mktemp (tmp_output);
- child_stdout = Open (tmp_output, MODE_NEWFILE);
-
- for (aptr=argv; *aptr; aptr++)
- {
- len += strlen (*aptr) + 1;
- }
-
- buffer = xmalloc (len + 1);
- ptr = buffer;
-
- for (aptr=argv; *aptr; aptr++)
- {
- strcpy (ptr, *aptr);
- ptr += strlen (ptr) + 1;
- *ptr ++ = ' ';
- *ptr = 0;
- }
-
- ptr[-1] = '\n';
-
- Execute (buffer, NULL, child_stdout);
- free (buffer);
-
- Close (child_stdout);
-
- child_stdout = Open (tmp_output, MODE_OLDFILE);
-
- buffer = xmalloc (maxlen);
- i = 0;
- do
- {
- if (i == maxlen)
- {
- maxlen += 512;
- buffer = (char *) xrealloc (buffer, maxlen + 1);
- }
-
- cc = Read (child_stdout, &buffer[i], maxlen - i);
- if (cc > 0)
- i += cc;
- } while (cc > 0);
-
- Close (child_stdout);
- DeleteFile (tmp_output);
-
- if (i > 0)
- {
- if (buffer[i - 1] == '\n')
- buffer[--i] = '\0';
- else
- buffer[i] = '\0';
- p = buffer;
- while ((p = index (p, '\n')) != 0)
- *p++ = ' ';
- o = variable_buffer_output (o, buffer, i);
- }
- free (buffer);
- }
-#endif /* Not Amiga. */
-
- free (text);
- break;
- }
-#endif /* !VMS */
+static char *
+func_subst(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ o = subst_expand (o, argv[2], argv[0], argv[1], strlen (argv[0]),
+ strlen (argv[1]), 0, 0);
- case function_origin:
- /* Expand the argument. */
- text = expand_argument (text, end);
+ return o;
+}
- {
- register struct variable *v = lookup_variable (text, strlen (text));
- if (v == 0)
- o = variable_buffer_output (o, "undefined", 9);
- else
- switch (v->origin)
- {
- default:
- case o_invalid:
- abort ();
- break;
- case o_default:
- o = variable_buffer_output (o, "default", 7);
- break;
- case o_env:
- o = variable_buffer_output (o, "environment", 11);
- break;
- case o_file:
- o = variable_buffer_output (o, "file", 4);
- break;
- case o_env_override:
- o = variable_buffer_output (o, "environment override", 20);
- break;
- case o_command:
- o = variable_buffer_output (o, "command line", 12);
- break;
- case o_override:
- o = variable_buffer_output (o, "override", 8);
- break;
- case o_automatic:
- o = variable_buffer_output (o, "automatic", 9);
- break;
- }
- }
- free (text);
- break;
+static char *
+func_firstword(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ int i=0;
+ char *words = argv[0];
+ char *p = find_next_token (&words, &i);
- case function_sort:
- /* Expand the argument. */
- text = expand_argument (text, end);
+ if (p != 0)
+ o = variable_buffer_output (o, p, i);
- {
- char **words = (char **) xmalloc (10 * sizeof (char *));
- unsigned int nwords = 10;
- register unsigned int wordi = 0;
- char *t;
-
- /* Chop TEXT into words and put them in WORDS. */
- t = text;
- while ((p = find_next_token (&t, &len)) != 0)
- {
- if (wordi >= nwords - 1)
- {
- nwords *= 2;
- words = (char **) xrealloc ((char *) words,
- nwords * sizeof (char *));
- }
- words[wordi++] = savestring (p, len);
- }
+ return o;
+}
- if (wordi > 0)
- {
- /* Now sort the list of words. */
- qsort ((char *) words, wordi, sizeof (char *), alpha_compare);
-
- /* Now write the sorted list. */
- for (i = 0; i < wordi; ++i)
- {
- len = strlen (words[i]);
- if (i == wordi - 1 || strlen (words[i + 1]) != len
- || strcmp (words[i], words[i + 1]))
- {
- o = variable_buffer_output (o, words[i], len);
- o = variable_buffer_output (o, " ", 1);
- }
- free (words[i]);
- }
- /* Kill the last space. */
- --o;
- }
- free ((char *) words);
- }
+static char *
+func_words(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ int i = 0;
+ char *word_iterator = argv[0];
+ char buf[20];
- free (text);
- break;
+ while (find_next_token (&word_iterator, (unsigned int *) 0) != 0)
+ ++i;
- case function_foreach:
- {
- /* Get three comma-separated arguments but
- expand only the first two. */
- char *var, *list;
- register struct variable *v;
+ sprintf (buf, "%d", i);
+ o = variable_buffer_output (o, buf, strlen (buf));
- count = 0;
- for (p = text; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("foreach");
- var = expand_argument (text, p);
- p2 = p + 1;
- count = 0;
- for (p = p2; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("foreach");
- list = expand_argument (p2, p);
+ return o;
+}
- ++p;
- text = savestring (p, end - p);
-
- push_new_variable_scope ();
- v = define_variable (var, strlen (var), "", o_automatic, 0);
- free (v->value);
- v->value = 0;
- p3 = list;
- while ((p = find_next_token (&p3, &len)) != 0)
- {
- char *result;
- char save = p[len];
- p[len] = '\0';
- v->value = p;
- result = allocated_variable_expand (text);
- p[len] = save;
-
- o = variable_buffer_output (o, result, strlen (result));
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- free (result);
- }
- if (doneany)
- /* Kill the last space. */
- --o;
+char *
+strip_whitespace (begpp, endpp)
+ char **begpp;
+ char **endpp;
+{
+ while (isspace (**begpp) && *begpp <= *endpp)
+ (*begpp) ++;
+ while (isspace (**endpp) && *endpp >= *begpp)
+ (*endpp) --;
+ return *begpp;
+}
- v->value = 0;
- pop_variable_scope ();
+int
+is_numeric (p)
+ char *p;
+{
+ char *end = p + strlen (p) -1;
+ char *beg = p;
+ strip_whitespace (&p, &end);
+ while (p <= end)
+ {
+ if (!isdigit (*p))
+ return 0;
- free (var);
- free (list);
- free (text);
- }
- break;
+ p++;
+ }
- case function_filter:
- case function_filter_out:
- {
- struct a_word
- {
- struct a_word *next;
- char *str;
- int matched;
- } *words, *wordtail, *wp;
-
- /* Get two comma-separated arguments and expand each one. */
- count = 0;
- for (p = text; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS (function == function_filter ? "filter" : "filter-out");
- p2 = expand_argument (text, p);
+ return (end - beg >= 0);
+}
- text = expand_argument (p + 1, end);
+void
+check_numeric (s, message)
+ char *s;
+ char *message;
+{
+ if (!is_numeric (s))
+ fatal (reading_file, message);
+}
- /* Chop TEXT up into words and then run each pattern through. */
- words = wordtail = 0;
- p3 = text;
- while ((p = find_next_token (&p3, &len)) != 0)
- {
- struct a_word *w = (struct a_word *)alloca(sizeof(struct a_word));
- if (words == 0)
- words = w;
- else
- wordtail->next = w;
- wordtail = w;
-
- if (*p3 != '\0')
- ++p3;
- p[len] = '\0';
- w->str = p;
- w->matched = 0;
- }
- if (words != 0)
- {
- wordtail->next = 0;
-
- /* Run each pattern through the words, killing words. */
- p3 = p2;
- while ((p = find_next_token (&p3, &len)) != 0)
- {
- char *percent;
- char save = p[len];
- p[len] = '\0';
-
- percent = find_percent (p);
- for (wp = words; wp != 0; wp = wp->next)
- wp->matched |= (percent == 0 ? streq (p, wp->str)
- : pattern_matches (p, percent, wp->str));
-
- p[len] = save;
- }
-
- /* Output the words that matched (or didn't, for filter-out). */
- for (wp = words; wp != 0; wp = wp->next)
- if (function == function_filter ? wp->matched : !wp->matched)
- {
- o = variable_buffer_output (o, wp->str, strlen (wp->str));
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
- if (doneany)
- /* Kill the last space. */
- --o;
- }
- free (p2);
- free (text);
- }
+static char *
+func_word(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ char *end_p=0;
+ int i=0;
+ char *p=0;
+
+ /* Check the first argument. */
+ check_numeric (argv[0], "non-numeric first argument to `word' function");
+ i = atoi (argv[0]);
+
+ if (i == 0)
+ fatal (reading_file, "the `word' function takes a positive index argument");
+
+
+ end_p = argv[1];
+ while ((p = find_next_token (&end_p, 0)) != 0)
+ if (--i == 0)
break;
- case function_patsubst:
- /* Get three comma-separated arguments and expand each one. */
- count = 0;
- for (p = text; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("patsubst");
+ if (i == 0)
+ o = variable_buffer_output (o, p, end_p - p);
- p2 = p;
- count = 0;
- for (++p; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("patsubst");
+ return o;
+}
- text = expand_argument (text, p2);
- p3 = expand_argument (p2 + 1, p);
- p2 = expand_argument (p + 1, end);
+static char *
+func_wordlist (o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ int i=0;
+ int j=0;
- o = patsubst_expand (o, p2, text, p3, (char *) 0, (char *) 0);
+ /* Check the first argument. */
+ check_numeric (argv[0],
+ "non-numeric first argument to `wordlist' function");
+ i =atoi(argv[0]);
+ check_numeric (argv[1],
+ "non-numeric second argument to `wordlist' function");
- free (text);
- free (p3);
- free (p2);
- break;
+ j = atoi(argv[1]);
- case function_join:
- /* Get two comma-separated arguments and expand each one. */
- count = 0;
- for (p = text; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("join");
- text = expand_argument (text, p);
- p = expand_argument (p + 1, end);
+ {
+ char *p;
+ char *end_p = argv[2];
+
+ int start = (i < j) ? i : j;
+ int count = j -i ;
+ if (count < 0)
+ count = - count;
+ count ++;
+
+
+ while (((p = find_next_token (&end_p, 0)) != 0) && --start)
+ {}
+ if (p)
{
- /* Write each word of the first argument directly followed
- by the corresponding word of the second argument.
- If the two arguments have a different number of words,
- the excess words are just output separated by blanks. */
- register char *tp, *pp;
- p2 = text;
- p3 = p;
- do
- {
- unsigned int tlen, plen;
+ while (--count && (find_next_token (&end_p, 0) != 0))
+ {}
+ o = variable_buffer_output (o, p, end_p - p);
+ }
+ }
+ return o;
+}
- tp = find_next_token (&p2, &tlen);
- if (tp != 0)
- o = variable_buffer_output (o, tp, tlen);
+static char*
+func_findstring(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ /* Find the first occurrence of the first string in the second. */
+ int i = strlen (argv[0]);
+ if (sindex (argv[1], 0, argv[0], i) != 0)
+ o = variable_buffer_output (o, argv[0], i);
- pp = find_next_token (&p3, &plen);
- if (pp != 0)
- o = variable_buffer_output (o, pp, plen);
+ return o;
+}
- if (tp != 0 || pp != 0)
- {
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
- }
- while (tp != 0 || pp != 0);
- if (doneany)
- /* Kill the last blank. */
- --o;
+static char *
+func_foreach (o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ /* expand only the first two. */
+ char *varname = expand_argument (argv[0], argv[1] -1);
+ char *list = expand_argument (argv[1], argv[2] -1);
+ char *body = savestring (argv[2], argv[3] - argv[2] -1 );
+
+ int len =0;
+ char *list_iterator = list;
+ char *p;
+ register struct variable *var=0;
+ int doneany =0;
+
+ push_new_variable_scope ();
+ var = define_variable (varname, strlen (varname), "", o_automatic, 0);
+
+ /* loop through LIST, put the value in VAR and expand BODY */
+ while ((p = find_next_token (&list_iterator, &len)) != 0)
+ {
+ char *result = 0;
+
+ {
+ char save = p[len];
+
+ p[len] = '\0';
+ free (var->value);
+ var->value = (char *) xstrdup ((char*) p);
+ p[len] = save;
}
- free (text);
- free (p);
- break;
+ result = allocated_variable_expand (body);
+
+ o = variable_buffer_output (o, result, strlen (result));
+ o = variable_buffer_output (o, " ", 1);
+ doneany = 1;
+ free (result);
+ }
+
+ if (doneany)
+ /* Kill the last space. */
+ --o;
+
+ pop_variable_scope ();
+ free (varname);
+ free (list);
+ free (body);
+
+ return o;
+}
+
+struct a_word
+{
+ struct a_word *next;
+ char *str;
+ int matched;
+};
+
+static char *
+func_filter_filterout (o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ struct a_word *wordhead =0;
+ struct a_word *wordtail =0;
+
+ int is_filter = streq (funcname, "filter");
+ char *patterns = argv[0];
+
- case function_strip:
- /* Expand the argument. */
- text = expand_argument (text, end);
+ char *p;
+ int len;
+ char *word_iterator = argv[1];
+
+ /* Chop ARGV[1] up into words and then run each pattern through. */
+ while ((p = find_next_token (&word_iterator, &len)) != 0)
+ {
+ struct a_word *w = (struct a_word *)alloca(sizeof(struct a_word));
+ if (wordhead == 0)
+ wordhead = w;
+ else
+ wordtail->next = w;
+ wordtail = w;
+
+ if (*word_iterator != '\0')
+ ++word_iterator;
+ p[len] = '\0';
+ w->str = p;
+ w->matched = 0;
+ }
+
+ if (wordhead != 0)
+ {
+ struct a_word *wp =0;
+ char *pat_iterator = patterns;
+ int doneany = 0;
- p2 = text;
- while (*p2 != '\0')
+ wordtail->next = 0;
+
+
+ /* Run each pattern through the words, killing words. */
+ while ((p = find_next_token (&pat_iterator, &len)) != 0)
{
- while (isspace(*p2))
- ++p2;
- p = p2;
- for (i=0; *p2 != '\0' && !isspace(*p2); ++p2, ++i)
- {}
- if (!i)
- break;
- o = variable_buffer_output (o, p, i);
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
+ char *percent;
+ char save = p[len];
+ p[len] = '\0';
+
+ percent = find_percent (p);
+ for (wp = wordhead; wp != 0; wp = wp->next)
+ wp->matched |= (percent == 0 ? streq (p, wp->str)
+ : pattern_matches (p, percent, wp->str));
+
+ p[len] = save;
}
+
+ /* Output the words that matched (or didn't, for filter-out). */
+ for (wp = wordhead; wp != 0; wp = wp->next)
+ if (is_filter ? wp->matched : !wp->matched)
+ {
+ o = variable_buffer_output (o, wp->str, strlen (wp->str));
+ o = variable_buffer_output (o, " ", 1);
+ doneany = 1;
+ }
+
if (doneany)
/* Kill the last space. */
--o;
+ }
- free (text);
- break;
+ return o;
+}
- case function_wildcard:
- text = expand_argument (text, end);
-#ifdef _AMIGA
- o = wildcard_expansion (text, o);
-#else
- p = string_glob (text);
- o = variable_buffer_output (o, p, strlen (p));
-#endif
+static char *
+func_strip(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ char *p = argv[0];
+ int doneany =0;
- free (text);
- break;
+ while (*p != '\0')
+ {
+ int i=0;
+ char *word_start=0;
- case function_subst:
- /* Get three comma-separated arguments and expand each one. */
- count = 0;
- for (p = text; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("subst");
+ while (isspace(*p))
+ ++p;
+ word_start = p;
+ for (i=0; *p != '\0' && !isspace(*p); ++p, ++i)
+ {}
+ if (!i)
+ break;
+ o = variable_buffer_output (o, word_start, i);
+ o = variable_buffer_output (o, " ", 1);
+ doneany = 1;
+ }
- p2 = p;
- count = 0;
- for (++p; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("subst");
+ if (doneany)
+ /* Kill the last space. */
+ --o;
+ return o;
+}
- text = expand_argument (text, p2);
- p3 = expand_argument (p2 + 1, p);
- p2 = expand_argument (p + 1, end);
+/*
+ Print a warning or fatal message.
+*/
+static char *
+func_error (o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ char **argvp;
+ char *msg, *p;
+ int len;
- o = subst_expand (o, p2, text, p3, strlen (text), strlen (p3), 0, 0);
+ /* The arguments will be broken on commas. Rather than create yet
+ another special case where function arguments aren't broken up,
+ just create a format string that puts them back together. */
+ for (len=0, argvp=argv; *argvp != 0; ++argvp)
+ len += strlen(*argvp) + 2;
- free (text);
- free (p3);
- free (p2);
- break;
+ p = msg = alloca (len + 1);
- case function_firstword:
- /* Expand the argument. */
- text = expand_argument (text, end);
+ for (argvp=argv; argvp[1] != 0; ++argvp)
+ {
+ strcpy(p, *argvp);
+ p += strlen(*argvp);
+ *(p++) = ',';
+ *(p++) = ' ';
+ }
+ strcpy(p, *argvp);
- /* Find the first word in TEXT. */
- p2 = text;
- p = find_next_token (&p2, &i);
- if (p != 0)
- o = variable_buffer_output (o, p, i);
+ if (*funcname == 'e')
+ fatal (reading_file, "%s", msg);
- free (text);
- break;
+ /* The warning function expands to the empty string. */
+ error (reading_file, "%s", msg);
+
+ return o;
+}
- case function_word:
- /* Get two comma-separated arguments and expand each one. */
- count = 0;
- for (p = text; p < end; ++p)
+
+/*
+ chop argv[0] into words, and sort them.
+ */
+static char *
+func_sort (o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ char **words = 0;
+ int nwords = 0;
+ register int wordi = 0;
+
+ /* Chop ARGV[0] into words and put them in WORDS. */
+ char *t = argv[0];
+ char *p=0;
+ int len;
+ while ((p = find_next_token (&t, &len)) != 0)
+ {
+ if (wordi >= nwords - 1)
{
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
+ nwords = 2* nwords + 5;
+ words = (char **) xrealloc ((char *) words,
+ nwords * sizeof (char *));
}
- if (p == end)
- BADARGS ("word");
- text = expand_argument (text, p);
-
- p3 = expand_argument (p + 1, end);
-
- /* Check the first argument. */
- for (p2 = text; *p2 != '\0'; ++p2)
- if (*p2 < '0' || *p2 > '9')
- fatal (reading_file,
- "non-numeric first argument to `word' function");
-
- i = (unsigned int) atoi (text);
- if (i == 0)
- fatal (reading_file,
- "the `word' function takes a one-origin index argument");
-
- p2 = p3;
- while ((p = find_next_token (&p2, &len)) != 0)
- if (--i == 0)
- break;
- if (i == 0)
- o = variable_buffer_output (o, p, len);
-
- free (text);
- free (p3);
- break;
+ words[wordi++] = savestring (p, len);
+ }
- case function_words:
- /* Expand the argument. */
- text = expand_argument (text, end);
+ if (wordi > 0)
+ {
+ int i;
+ /* Now sort the list of words. */
+ qsort ((char *) words, wordi, sizeof (char *), alpha_compare);
- i = 0;
- p2 = text;
- while (find_next_token (&p2, (unsigned int *) 0) != 0)
- ++i;
+ /* Now write the sorted list. */
+ for (i = 0; i < wordi; ++i)
+ {
+ len = strlen (words[i]);
+ if (i == wordi - 1 || strlen (words[i + 1]) != len
+ || strcmp (words[i], words[i + 1]))
+ {
+ o = variable_buffer_output (o, words[i], len);
+ o = variable_buffer_output (o, " ", 1);
+ }
+ free (words[i]);
+ }
+ /* Kill the last space. */
+ --o;
+ }
- {
- char buf[20];
- sprintf (buf, "%d", i);
- o = variable_buffer_output (o, buf, strlen (buf));
- }
+ free ((char *) words);
+ return o;
+}
- free (text);
- break;
+static char *
+func_wildcard(o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
- case function_wordlist:
- /* Get two comma-separated arguments and expand each one. */
- count = 0;
- for (p = text; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("wordlist");
- text = expand_argument (text, p);
-
- /* Check the first argument. */
- for (p2 = text; *p2 != '\0'; ++p2)
- if (*p2 < '0' || *p2 > '9')
- fatal (reading_file,
- "non-numeric first argument to `wordlist' function");
- i = (unsigned int)atoi(text);
- free (text);
-
- /* Check the next argument */
- for (p2 = p + 1; isblank(*p2); ++p2)
- {}
- count = 0;
- for (p = p2; p < end; ++p)
+#ifdef _AMIGA
+ o = wildcard_expansion (argv[0], o);
+#else
+ char *p = string_glob (argv[0]);
+ o = variable_buffer_output (o, p, strlen (p));
+#endif
+ return o;
+}
+
+/*
+ \r is replaced on UNIX as well. Is this desirable?
+ */
+void
+fold_newlines (buffer, length)
+ char *buffer;
+ int *length;
+{
+ char *dst = buffer;
+ char *src = buffer;
+ char *last_nonnl = buffer -1;
+ src[*length] = 0;
+ for (; *src != '\0'; ++src)
+ {
+ if (src[0] == '\r' && src[1] == '\n')
+ continue;
+ if (*src == '\n')
{
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
+ *dst++ = ' ';
}
- if (p == end)
- BADARGS ("wordlist");
- text = expand_argument (p2, p);
-
- for (p2 = text; *p2 != '\0'; ++p2)
- if (*p2 < '0' || *p2 > '9')
- fatal (reading_file,
- "non-numeric second argument to `wordlist' function");
- j = (unsigned int)atoi(text);
- free (text);
-
- if (j > i)
- j -= i;
else
{
- unsigned int k;
- k = j;
- j = i - j;
- i = k;
+ last_nonnl = dst;
+ *dst++ = *src;
}
- ++j;
+ }
+ *(++last_nonnl) = '\0';
+ *length = last_nonnl - buffer;
+}
- /* Extract the requested words */
- text = expand_argument (p + 1, end);
- p2 = text;
- while (((p = find_next_token (&p2, &len)) != 0) && --i)
- {}
- if (p)
- {
- while (--j && (find_next_token (&p2, &len) != 0))
- {}
- o = variable_buffer_output (o, p, p2 - p);
- }
- free (text);
- break;
+int shell_function_pid = 0, shell_function_completed;
- case function_findstring:
- /* Get two comma-separated arguments and expand each one. */
- count = 0;
- for (p = text; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS ("findstring");
- text = expand_argument (text, p);
- p = expand_argument (p + 1, end);
+#ifdef WINDOWS32
+/*untested*/
- /* Find the first occurrence of the first string in the second. */
- i = strlen (text);
- if (sindex (p, 0, text, i) != 0)
- o = variable_buffer_output (o, text, i);
+#include <windows.h>
+#include <io.h>
+#include "sub_proc.h"
- free (p);
- free (text);
- break;
- case function_addsuffix:
- case function_addprefix:
- /* Get two comma-separated arguments and expand each one. */
- count = 0;
- for (p = text; p < end; ++p)
- {
- if (*p == startparen)
- ++count;
- else if (*p == endparen)
- --count;
- else if (*p == ',' && count <= 0)
- break;
- }
- if (p == end)
- BADARGS (function == function_addsuffix ? "addsuffix" : "addprefix");
- text = expand_argument (text, p);
- i = strlen (text);
+void
+windows32_openpipe (int *pipedes, int *pid_p, char **command_argv, char **envp)
+{
+ SECURITY_ATTRIBUTES saAttr;
+ HANDLE hIn;
+ HANDLE hErr;
+ HANDLE hChildOutRd;
+ HANDLE hChildOutWr;
+ HANDLE hProcess;
+
+
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_INPUT_HANDLE),
+ GetCurrentProcess(),
+ &hIn,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fatal (NILF, "create_child_process: DuplicateHandle(In) failed (e=%d)\n",
+ GetLastError());
+
+ }
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_ERROR_HANDLE),
+ GetCurrentProcess(),
+ &hErr,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fatal (NILF, "create_child_process: DuplicateHandle(Err) failed (e=%d)\n",
+ GetLastError());
+ }
+
+ if (!CreatePipe(&hChildOutRd, &hChildOutWr, &saAttr, 0))
+ fatal (NILF, "CreatePipe() failed (e=%d)\n", GetLastError());
+
+
+
+ hProcess = process_init_fd(hIn, hChildOutWr, hErr);
+
+ if (!hProcess)
+ fatal (NILF, "windows32_openpipe (): process_init_fd() failed\n");
- p2 = expand_argument (p + 1, end);
+ else
+ process_register(hProcess);
- p3 = p2;
- while ((p = find_next_token (&p3, &len)) != 0)
- {
- if (function == function_addprefix)
- o = variable_buffer_output (o, text, i);
- o = variable_buffer_output (o, p, len);
- if (function == function_addsuffix)
- o = variable_buffer_output (o, text, i);
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
- if (doneany)
- /* Kill last space. */
- --o;
+ /* make sure that CreateProcess() has Path it needs */
+ sync_Path_environment();
- free (p2);
- free (text);
- break;
+ if (!process_begin(hProcess, command_argv, envp, command_argv[0], NULL))
+ *pid_p = (int) hProcess;
+ else
+ fatal (NILF, "windows32_openpipe (): unable to launch process (e=%d)\n",
+ process_last_err(hProcess));
- case function_dir:
- case function_basename:
- /* Expand the argument. */
- text = expand_argument (text, end);
+ /* set up to read data from child */
+ pipedes[0] = _open_osfhandle((long) hChildOutRd, O_RDONLY);
- p3 = text;
- while ((p2 = find_next_token (&p3, &len)) != 0)
- {
- p = p2 + len;
-#ifdef VMS
- while (p >= p2 && *p != ']'
- && (function != function_basename || *p != '.'))
-#else
-# ifdef __MSDOS__
- while (p >= p2 && *p != '/' && *p != '\\'
- && (function != function_basename || *p != '.'))
-# else
- while (p >= p2 && *p != '/'
- && (function != function_basename || *p != '.'))
-# endif
+ /* this will be closed almost right away */
+ pipedes[1] = _open_osfhandle((long) hChildOutWr, O_APPEND);
+}
#endif
- --p;
- if (p >= p2 && (function == function_dir))
- o = variable_buffer_output (o, p2, ++p - p2);
- else if (p >= p2 && (*p == '.'))
- o = variable_buffer_output (o, p2, p - p2);
-#if defined(WINDOWS32) || defined(__MSDOS__)
- /* Handle the "d:foobar" case */
- else if (p2[0] && p2[1] == ':' && function == function_dir)
- o = variable_buffer_output (o, p2, 2);
+
+
+#ifdef __MSDOS__
+/*
+ untested
+*/
+int
+msdos_openpipe (int* pipedes, int *pidp, char *text)
+{
+ FILE *fpipe=0;
+ /* MSDOS can't fork, but it has `popen'.
+ (Bwt, why isn't `popen' used in all the versions?) */
+ struct variable *sh = lookup_variable ("SHELL", 5);
+ int e;
+ extern int dos_command_running, dos_status;
+
+ /* Make sure not to bother processing an empty line. */
+ while (isblank (*text))
+ ++text;
+ if (*text == '\0')
+ return 0;
+
+ if (sh)
+ {
+ char buf[PATH_MAX + 7];
+ /* This makes sure $SHELL value is used by $(shell), even
+ though the target environment is not passed to it. */
+ sprintf (buf, "SHELL=%s", sh->value);
+ putenv (buf);
+ }
+
+ e = errno;
+ errno = 0;
+ dos_command_running = 1;
+ dos_status = 0;
+ fpipe = popen (text, "rt");
+ dos_command_running = 0;
+ if (!fpipe || dos_status)
+ {
+ pipedes[0] = -1;
+ *pidp = -1;
+ if (dos_status)
+ errno = EINTR;
+ else if (errno == 0)
+ errno = ENOMEM;
+ shell_function_completed = -1;
+ }
+ else
+ {
+ pipedes[0] = fileno (fpipe);
+ *pidp = 42; /* uh? The meaning of Life?*/
+ errno = e;
+ shell_function_completed = 1;
+ }
+ return fpipe;
+}
#endif
- else if (function == function_dir)
+
+/*
+ Do shell spawning, with the naughty bits for different OSes.
+ */
+
#ifdef VMS
- o = variable_buffer_output (o, "[]", 2);
+
+/* VMS can't do $(shell ...) */
+#define func_shell 0
+
#else
#ifndef _AMIGA
- o = variable_buffer_output (o, "./", 2);
-#else
- /* o = o */; /* Just a nop... */
-#endif /* AMIGA */
-#endif /* !VMS */
- else
- /* The entire name is the basename. */
- o = variable_buffer_output (o, p2, len);
+static char *
+func_shell (o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ char* batch_filename = NULL;
+ int i;
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
- if (doneany)
- /* Kill last space. */
- --o;
+#ifdef __MSDOS__
+ FILE *fpipe;
+#endif
+ char **command_argv;
+ char *error_prefix;
+ char **envp;
+ int pipedes[2];
+ int pid;
- free (text);
- break;
+#ifndef __MSDOS__
+ /* Construct the argument list. */
+ command_argv = construct_command_argv (argv[0],
+ (char **) NULL, (struct file *) 0,
+ &batch_filename);
+ if (command_argv == 0)
+ return o;
+#endif
- case function_notdir:
- case function_suffix:
- /* Expand the argument. */
- text = expand_argument (text, end);
+ /* Using a target environment for `shell' loses in cases like:
+ export var = $(shell echo foobie)
+ because target_environment hits a loop trying to expand $(var)
+ to put it in the environment. This is even more confusing when
+ var was not explicitly exported, but just appeared in the
+ calling environment. */
+
+ envp = environ;
+
+ /* For error messages. */
+ if (reading_file != 0)
+ {
+ error_prefix = (char *) alloca (strlen(reading_file->filenm)+100);
+
+ sprintf (error_prefix,
+ "%s:%lu: ", reading_file->filenm, reading_file->lineno);
+
+ }
+ else
+ error_prefix = "";
+
+#ifdef WINDOWS32
+ windows32_openpipe (pipedes, &pid, command_argv, envp);
+#else /* WINDOWS32 */
- p3 = text;
- while ((p2 = find_next_token (&p3, &len)) != 0)
- {
- p = p2 + len;
-#ifdef VMS
- while (p >= p2 && *p != ']'
- && (function != function_suffix || *p != '.'))
-#else
# ifdef __MSDOS__
- while (p >= p2 && *p != '/' && *p != '\\'
- && (function != function_suffix || *p != '.'))
+ fpipe = msdos_openpipe (pipedes, argv[0], command_argv, envp);
+ if (!fpipe || pipedes[0] < 0)
+ {
+ perror_with_name (error_prefix, "pipe");
+ return o;
+ }
# else
- while (p >= p2 && *p != '/'
- && (function != function_suffix || *p != '.'))
-# endif
+ if (pipe (pipedes) < 0)
+ {
+ perror_with_name (error_prefix, "pipe");
+ return o;
+ }
+
+ pid = vfork ();
+ if (pid < 0)
+ perror_with_name (error_prefix, "fork");
+ else if (pid == 0)
+ child_execute_job (0, pipedes[1], command_argv, envp);
+ else
+# endif /* ! __MSDOS__ */
+
+#endif /* WINDOWS32 */
+ {
+ /* We are the parent. */
+
+ char *buffer;
+ unsigned int maxlen;
+ int cc;
+
+ /* Record the PID for reap_children. */
+ shell_function_pid = pid;
+#ifndef __MSDOS__
+ shell_function_completed = 0;
+
+ /* Free the storage only the child needed. */
+ free (command_argv[0]);
+ free ((char *) command_argv);
+
+ /* Close the write side of the pipe. */
+ (void) close (pipedes[1]);
#endif
- --p;
- if (p >= p2)
- {
- if (function == function_notdir)
- ++p;
- else if (*p != '.')
- continue;
- o = variable_buffer_output (o, p, len - (p - p2));
- }
-#if defined(WINDOWS32) || defined(__MSDOS__)
- /* Handle the case of "d:foo/bar". */
- else if (function == function_notdir && p2[0] && p2[1] == ':')
+
+ /* Set up and read from the pipe. */
+
+ maxlen = 200;
+ buffer = (char *) xmalloc (maxlen + 1);
+
+ /* Read from the pipe until it gets EOF. */
+ i = 0;
+ do
+ {
+ if (i == maxlen)
{
- p = p2 + 2;
- o = variable_buffer_output (o, p, len - (p - p2));
+ maxlen += 512;
+ buffer = (char *) xrealloc (buffer, maxlen + 1);
}
+
+ errno = 0;
+ cc = read (pipedes[0], &buffer[i], maxlen - i);
+ if (cc > 0)
+ i += cc;
+ }
+#ifdef EINTR
+ while (cc > 0 || errno == EINTR);
+#else
+ while (cc > 0);
#endif
- else if (function == function_notdir)
- o = variable_buffer_output (o, p2, len);
- if (function == function_notdir || p >= p2)
- {
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
+ /* Close the read side of the pipe. */
+#ifdef __MSDOS__
+ if (fpipe)
+ (void) pclose (fpipe);
+#else
+ (void) close (pipedes[0]);
+#endif
+
+ /* Loop until child_handler sets shell_function_completed
+ to the status of our child shell. */
+ while (shell_function_completed == 0)
+ reap_children (1, 0);
+
+ if (batch_filename) {
+ if (debug_flag)
+ printf("Cleaning up temporary batch file %s\n", batch_filename);
+ remove(batch_filename);
+ free(batch_filename);
+ }
+ shell_function_pid = 0;
+
+ /* The child_handler function will set shell_function_completed
+ to 1 when the child dies normally, or to -1 if it
+ dies with status 127, which is most likely an exec fail. */
+
+ if (shell_function_completed == -1)
+ {
+ /* This most likely means that the execvp failed,
+ so we should just write out the error message
+ that came in over the pipe from the child. */
+ fputs (buffer, stderr);
+ fflush (stderr);
+ }
+ else
+ {
+ /* The child finished normally. Replace all
+ newlines in its output with spaces, and put
+ that in the variable output buffer. */
+ fold_newlines (buffer, &i);
+ o = variable_buffer_output (o, buffer, i);
}
- if (doneany)
- /* Kill last space. */
- --o;
- free (text);
- break;
+ free (buffer);
}
return o;
}
-
-/* Check for a function invocation in *STRINGP. *STRINGP points at the
- opening ( or { and is not null-terminated. If a function invocation
- is found, expand it into the buffer at *OP, updating *OP, incrementing
- *STRINGP past the reference and returning nonzero. If not, return zero. */
-int
-handle_function (op, stringp)
- char **op;
- char **stringp;
+#else /* _AMIGA */
+
+/* Do the Amiga version of func_shell. */
+static char *
+func_shell (char *o, char **argv, const char *funcname)
{
- register unsigned int code;
- unsigned int maxlen;
- char *beg = *stringp + 1;
- char *endref;
+ /* Amiga can't fork nor spawn, but I can start a program with
+ redirection of my choice. However, this means that we
+ don't have an opportunity to reopen stdout to trap it. Thus,
+ we save our own stdout onto a new descriptor and dup a temp
+ file's descriptor onto our stdout temporarily. After we
+ spawn the shell program, we dup our own stdout back to the
+ stdout descriptor. The buffer reading is the same as above,
+ except that we're now reading from a file. */
+
+#include <dos/dos.h>
+#include <proto/dos.h>
+
+ BPTR child_stdout;
+ char tmp_output[FILENAME_MAX];
+ unsigned int maxlen = 200;
+ int cc, i;
+ char * buffer, * ptr;
+ char ** aptr;
+ int len = 0;
+ char* batch_filename = NULL;
- endref = lindex (beg, beg + MAXFUNCTIONLEN, '\0');
- maxlen = endref != 0 ? endref - beg : MAXFUNCTIONLEN;
+ /* Construct the argument list. */
+ command_argv = construct_command_argv (argv[0], (char **) NULL,
+ (struct file *) 0, &batch_filename);
+ if (command_argv == 0)
+ return o;
- for (code = 0; function_table[code].name != 0; ++code)
+
+ strcpy (tmp_output, "t:MakeshXXXXXXXX");
+ mktemp (tmp_output);
+ child_stdout = Open (tmp_output, MODE_NEWFILE);
+
+ for (aptr=command_argv; *aptr; aptr++)
+ len += strlen (*aptr) + 1;
+
+ buffer = xmalloc (len + 1);
+ ptr = buffer;
+
+ for (aptr=command_argv; *aptr; aptr++)
{
- if (maxlen < function_table[code].len)
- continue;
- endref = beg + function_table[code].len;
- if (isblank (*endref)
- && !strncmp (function_table[code].name, beg,
- function_table[code].len))
- break;
+ strcpy (ptr, *aptr);
+ ptr += strlen (ptr) + 1;
+ *ptr ++ = ' ';
+ *ptr = 0;
}
- if (function_table[code].name != 0)
- {
- /* We have found a call to an expansion-time function.
- Find the end of the arguments, and do the function. */
- char openparen = beg[-1], closeparen = openparen == '(' ? ')' : '}';
- int count = 0;
- char *argbeg;
- register char *p;
+ ptr[-1] = '\n';
+
+ Execute (buffer, NULL, child_stdout);
+ free (buffer);
- /* Space after function name isn't part of the args. */
- p = next_token (endref);
- argbeg = p;
+ Close (child_stdout);
- /* Count nested use of whichever kind of parens we use,
- so that nested calls and variable refs work. */
+ child_stdout = Open (tmp_output, MODE_OLDFILE);
- for (; *p != '\0'; ++p)
+ buffer = xmalloc (maxlen);
+ i = 0;
+ do
+ {
+ if (i == maxlen)
{
- if (*p == openparen)
- ++count;
- else if (*p == closeparen && --count < 0)
- break;
+ maxlen += 512;
+ buffer = (char *) xrealloc (buffer, maxlen + 1);
}
- if (count >= 0)
- fatal (reading_file,
- "unterminated call to function `%s': missing `%c'",
- function_table[code].name, closeparen);
+ cc = Read (child_stdout, &buffer[i], maxlen - i);
+ if (cc > 0)
+ i += cc;
+ } while (cc > 0);
- /* We found the end; expand the function call. */
+ Close (child_stdout);
- *op = expand_function (*op, function_table[code].function, argbeg, p);
- *stringp = p;
- return 1;
- }
+ fold_newlines (buffer, &i);
+ o = variable_buffer_output (o, buffer, i);
+ free (buffer);
+ return o;
+}
+#endif /* _AMIGA */
+#endif /* !VMS */
+
+#ifdef EXPERIMENTAL
- return 0;
+/*
+ equality. Return is string-boolean, ie, the empty string is false.
+ */
+static char *
+func_eq (char* o, char **argv, char *funcname)
+{
+ int result = ! strcmp (argv[0], argv[1]);
+ o = variable_buffer_output (o, result ? "1" : "", result);
+ return o;
}
-
-/* Glob-expand LINE. The returned pointer is
- only good until the next call to string_glob. */
+
+/*
+ string-boolean not operator.
+ */
static char *
-string_glob (line)
- char *line;
+func_not (char* o, char **argv, char *funcname)
{
- static char *result = 0;
- static unsigned int length;
- register struct nameseq *chain;
- register unsigned int idx;
+ char * s = argv[0];
+ int result = 0;
+ while (isspace (*s))
+ s++;
+ result = ! (*s);
+ o = variable_buffer_output (o, result ? "1" : "", result);
+ return o;
+}
- chain = multi_glob (parse_file_seq
- (&line, '\0', sizeof (struct nameseq),
- /* We do not want parse_file_seq to strip `./'s.
- That would break examples like:
- $(patsubst ./%.c,obj/%.o,$(wildcard ./*.c)). */
- 0),
- sizeof (struct nameseq));
- if (result == 0)
- {
- length = 100;
- result = (char *) xmalloc (100);
- }
- idx = 0;
- while (chain != 0)
+/*
+ This is an experimental conditional function.
+
+ Syntax:
+
+ $(if condition, true-part, false-part)
+
+ This is fully not consistent with make's syntax, but more in line
+ with `normal' programming languages.
+
+ Semantics:
+
+ - CONDITION is false iff it evaluates to an empty string. White
+ space before and after condition are stripped before evaluation.
+
+ - If CONDITION is true, then TRUE-PART is evaluated, otherwise
+ FALSE-PART is evaluated. Because only one of the two PARTs is
+ evaluated, you can use $(if ) to create side-effects with the
+ $(shell ) function
+
+ */
+static char *
+func_if (char* o, char **argv, char *funcname)
+{
+ char *begp = argv[0];
+ char *endp = argv[1]-2;
+ char *expansion =0;
+ int result = 0;
+
+ strip_whitespace (&begp, &endp);
+ if(begp <= endp)
+ expansion = expand_argument (begp, endp + 1);
+
+ result = expansion
+ ? strlen (expansion)
+ : 0;
+
+ result = !result;
+ free (expansion);
+
+ expansion = expand_argument (argv[1 + result], argv[2+result] -1);
+ o = variable_buffer_output (o, expansion, strlen (expansion));
+
+ return o;
+}
+#endif
+
+/* This might not be very useful, but the code was simple to
+ implement, I just had to do it.
+
+ Here goes anyway
+
+ Apply & User defined functions.
+
+ SYNTAX
+
+ $(apply funcname, arg1, arg2, .. )
+
+ SEMANTICS
+
+ You can specify a builtin function, for funcname, eg
+
+ f = addprefix
+ $(apply addprefix,a, b c d)
+
+ This will result in
+
+ ab ac ad
+
+ You can specify your own functions, eg
+
+ funcname=BODY
+
+ BODY contains $(1) .. $(N) as argument markers.
+ upon expansions the strings ARG1 .. ARGN are substituted for $(1) .. $(N)
+ into BODY
+
+ Because the funcname is computed as well you can combine this do some
+ funky things, eg
+
+ map=$(foreach a, $(2), $(apply $(1), $(a)))
+
+
+ LIMITATIONS.
+
+ Make has no support for nested lists (or tuples), so you can't do
+ stuff like (Haskell notation):
+
+ f :: (a,b) -> c -- type of F
+ map :: (a->b) -> [a] -> b -- type of MAP
+
+ map f [(1,2), (2,3)] -- map F over list containing (1,2) and (2,3)
+
+ to get
+
+ [f (1, 2), f (2, 3)]
+
+
+ If only we had nested lists and quotes, we could duplicate LISP in make by
+ transforming
+
+ $(a, b, c) <-> (a b c)
+ $(quote $(a, b, c)) <-> '(a b c)
+
+ (or something alike ;-) (We could have automatic integration of
+ GUILE and make :-)
+
+ [Actually -- why should this be a joke? If we could somehow integrate the
+ rules and targets into a functional model make could be a lot cleaner in
+ concept. ]
+
+*/
+char *
+func_apply (o, argv, funcname)
+ char *o;
+ char **argv;
+ const char *funcname;
+{
+ char *userfunc_name;
+ int func_len;
+ char *body = 0;
+ char *expanded_body = 0;
+ int i;
+ const struct function_table_entry *entry_p;
+
+ userfunc_name = argv[0];
+ while (isspace (*userfunc_name))
+ ++userfunc_name;
+
+ entry_p = lookup_function (function_table, userfunc_name);
+
+ /* builtin function? */
+ if (entry_p)
{
- register char *name = chain->name;
- unsigned int len = strlen (name);
+ for (i=0; argv[i+1]; i++)
+ ;
- struct nameseq *next = chain->next;
- free ((char *) chain);
- chain = next;
+ o = expand_builtin_function (o, i, argv + 1, entry_p);
+ return o;
+ }
- /* multi_glob will pass names without globbing metacharacters
- through as is, but we want only files that actually exist. */
- if (file_exists_p (name))
- {
- if (idx + len + 1 > length)
- {
- length += (len + 1) * 2;
- result = (char *) xrealloc (result, length);
- }
- bcopy (name, &result[idx], len);
- idx += len;
- result[idx++] = ' ';
- }
+ func_len = strlen (userfunc_name);
+ body = xmalloc (func_len + 4);
+ strcpy (body + 2, userfunc_name);
+ body [func_len+2]=')';
+ body [func_len+3]= 0;
+ body [1]='(';
+ body [0]='$';
- free (name);
+ push_new_variable_scope ();
+
+ /* set up arguments $(1) .. $(N) */
+ for (i=0; argv[i]; i++)
+ {
+ char num[10];
+ struct variable* var;
+ sprintf (num, "%d", i);
+ var = define_variable (num, strlen (num), argv[i], o_automatic, 0);
}
- /* Kill the last space and terminate the string. */
- if (idx == 0)
- result[0] = '\0';
- else
- result[idx - 1] = '\0';
+ expanded_body = allocated_variable_expand (body);
+ o = variable_buffer_output (o, expanded_body, strlen (expanded_body));
+ free (expanded_body);
+ pop_variable_scope ();
- return result;
+ free (body);
+ return o;
}
+
+
+#define STRING_SIZE_TUPLE(s) (s), (sizeof(s)-1)
+
+/* Lookup table for builtin functions.
+
+ This doesn't have to be sorted; we use a straight lookup. We might gain
+ some efficiency by moving most often used functions to the start of the
+ table.
+
+ REQUIRED_ARGUMENTS is the minimum number of arguments. A function
+ can have more, but they will be ignored.
+
+ EXPAND_ALL_ARGUMENTS means that all arguments should be expanded
+ before invocation. Functions that do namespace tricks (foreach)
+ don't automatically expand. */
+
+static struct function_table_entry function_table[] =
+{
+ /* Name/size */ /* ARG EXP? Function */
+ { STRING_SIZE_TUPLE("addprefix"), 2, 1, func_addsuffix_addprefix},
+ { STRING_SIZE_TUPLE("addsuffix"), 2, 1, func_addsuffix_addprefix},
+ { STRING_SIZE_TUPLE("basename"), 1, 1, func_basename_dir},
+ { STRING_SIZE_TUPLE("dir"), 1, 1, func_basename_dir},
+ { STRING_SIZE_TUPLE("notdir"), 1, 1, func_notdir_suffix},
+ { STRING_SIZE_TUPLE("subst"), 3, 1, func_subst},
+ { STRING_SIZE_TUPLE("suffix"), 1, 1, func_notdir_suffix},
+ { STRING_SIZE_TUPLE("filter"), 2, 1, func_filter_filterout},
+ { STRING_SIZE_TUPLE("filter-out"), 2, 1, func_filter_filterout},
+ { STRING_SIZE_TUPLE("findstring"), 2, 1, func_findstring},
+ { STRING_SIZE_TUPLE("firstword"), 1, 1, func_firstword},
+ { STRING_SIZE_TUPLE("join"), 2, 1, func_join},
+ { STRING_SIZE_TUPLE("patsubst"), 3, 1, func_patsubst},
+ { STRING_SIZE_TUPLE("shell"), 1, 1, func_shell},
+ { STRING_SIZE_TUPLE("sort"), 1, 1, func_sort},
+ { STRING_SIZE_TUPLE("strip"), 1, 1, func_strip},
+ { STRING_SIZE_TUPLE("wildcard"), 1, 1, func_wildcard},
+ { STRING_SIZE_TUPLE("word"), 2, 1, func_word},
+ { STRING_SIZE_TUPLE("wordlist"), 3, 1, func_wordlist},
+ { STRING_SIZE_TUPLE("words"), 1, 1, func_words},
+ { STRING_SIZE_TUPLE("origin"), 1, 1, func_origin},
+ { STRING_SIZE_TUPLE("error"), 1, 1, func_error},
+ { STRING_SIZE_TUPLE("warning"), 1, 1, func_error},
+ { STRING_SIZE_TUPLE("foreach"), 3, 0, func_foreach},
+#ifdef EXPERIMENTAL
+ { STRING_SIZE_TUPLE("apply"), 1, 1, func_apply},
+ { STRING_SIZE_TUPLE("eq"), 2, 1, func_eq},
+ { STRING_SIZE_TUPLE("if"), 3, 0, func_if},
+ { STRING_SIZE_TUPLE("not"), 1, 1, func_not},
+#endif
+ { 0 }
+};
diff --git a/job.c b/job.c
index 87f22a2..5999558 100644
--- a/job.c
+++ b/job.c
@@ -2030,8 +2030,35 @@ construct_command_argv_internal (line, restp, shell, ifs, batch_filename_ptr)
}
}
else if (p[1] != '\0')
- /* Copy and skip the following char. */
- *ap++ = *++p;
+ {
+#if defined(__MSDOS__) || defined(WINDOWS32)
+ /* Only remove backslashes before characters special
+ to Unixy shells. All other backslashes are copied
+ verbatim, since they are probably DOS-style
+ directory separators. This still leaves a small
+ window for problems, but at least it should work
+ for the vast majority of naive users. */
+
+#ifdef __MSDOS__
+ /* A dot is only special as part of the "..."
+ wildcard. */
+ if (strncmp (p + 1, ".\\.\\.", 5) == 0)
+ {
+ *ap++ = '.';
+ *ap++ = '.';
+ p += 4;
+ }
+ else
+#endif
+ if (p[1] != '\\' && p[1] != '\'' && !isspace (p[1])
+ && (index (sh_chars_sh, p[1]) == 0))
+ /* back up one notch, to copy the backslash */
+ --p;
+
+#endif /* __MSDOS__ || WINDOWS32 */
+ /* Copy and skip the following char. */
+ *ap++ = *++p;
+ }
break;
case '\'':
diff --git a/make.texinfo b/make.texinfo
index 246707c..632ba48 100644
--- a/make.texinfo
+++ b/make.texinfo
@@ -8,10 +8,10 @@
@c FSF publishers: format makebook.texi instead of using this file directly.
@set RCSID $Id$
-@set EDITION 0.52
-@set VERSION 3.77
-@set UPDATED 20 May 1998
-@set UPDATE-MONTH May 1998
+@set EDITION 0.53
+@set VERSION 3.78
+@set UPDATED 22 March 1999
+@set UPDATE-MONTH March 1999
@comment The ISBN number might need to change on next publication.
@set ISBN 1-882114-80-9 @c CHANGE THIS BEFORE PRINTING AGAIN! --psmith 16jul98
@@ -37,7 +37,7 @@ and issues the commands to recompile them.
This is Edition @value{EDITION}, last updated @value{UPDATED},
of @cite{The GNU Make Manual}, for @code{make}, Version @value{VERSION}.
-Copyright (C) 1988, '89, '90, '91, '92, '93, '94, '95, '96, '97, '98
+Copyright (C) 1988, '89, '90, '91, '92, '93, '94, '95, '96, '97, '98, '99
Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of
@@ -5166,6 +5166,7 @@ call, just as a variable might be substituted.
* Foreach Function:: Repeat some text with controlled variation.
* Origin Function:: Find where a variable got its value.
* Shell Function:: Substitute the output of a shell command.
+* Make Control Functions:: Functions that control how make runs.
@end menu
@node Syntax of Functions, Text Functions, , Functions
@@ -5907,7 +5908,7 @@ Here the redefinition takes place if @samp{$(origin bletch)} returns either
@samp{environment} or @samp{environment override}.
@xref{Text Functions, , Functions for String Substitution and Analysis}.
-@node Shell Function, , Origin Function, Functions
+@node Shell Function, Make Control Functions, Origin Function, Functions
@section The @code{shell} Function
@findex shell
@cindex commands, expansion
@@ -5953,6 +5954,60 @@ sets @code{files} to the expansion of @samp{*.c}. Unless @code{make} is
using a very strange shell, this has the same result as
@w{@samp{$(wildcard *.c)}}.@refill
+@node Make Control Functions, , Shell Function, Functions
+@section Functions That Control Make
+@cindex functions, for controlling make
+@cindex controlling make
+
+These functions control the way make runs. Generally, they are used to
+provide information to the user of the makefile or to cause make to stop
+if some sort of environmental error is detected.
+
+@table @code
+@item $(error @var{text}@dots{})
+@findex error
+@cindex error, stopping on
+@cindex stopping make
+Generates a fatal error where the message is @var{text}. Note that the
+error is generated whenever this function is evaluated. So, if you put
+it inside a command script or on the right side of a recursive variable
+assignment, it won't be evaluated until later. The @var{text} will be
+expanded before the error is generated.
+
+For example,
+
+@example
+ifdef ERROR1
+$(error error is $(ERROR1))
+endif
+@end example
+
+@noindent
+will generate a fatal error during the read of the makefile if the
+@code{make} variable @code{ERROR1} is defined. Or,
+
+@example
+ERR = $(error found an error!)
+
+.PHONY: err
+err: ; $(ERR)
+@end example
+
+@noindent
+will generate a fatal error while @code{make} is running, if the
+@code{err} target is invoked.
+
+@item $(warning @var{text}@dots{})
+@findex warning
+@cindex warnings, printing
+@cindex printing user warnings
+This function works similarly to the @code{error} function, above,
+except that @code{make} doesn't exit. Instead, @var{text} is expanded
+and the resulting message is displayed, but processing of the makefile
+continues.
+
+The result of the expansion of this function is the empty string.
+
@node Running, Implicit Rules, Functions, Top
@chapter How to Run @code{make}