home *** CD-ROM | disk | FTP | other *** search
- /* Variable function expansion for GNU Make.
- Copyright (C) 1988, 1989 Free Software Foundation, Inc.
- This file is part of GNU Make.
-
- GNU Make is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- GNU Make is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU Make; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /*
- * MS-DOS port (c) 1990 by Thorsten Ohl <ohl@gnu.ai.mit.edu>
- *
- * To this port, the same copying conditions apply as to the
- * original release.
- *
- * IMPORTANT:
- * This file is not identical to the original GNU release!
- * You should have received this code as patch to the official
- * GNU release.
- *
- * MORE IMPORTANT:
- * This port comes with ABSOLUTELY NO WARRANTY.
- *
- * $Header: e:/gnu/make/RCS/function.c'v 3.58.0.3 90/07/19 01:01:52 tho Exp $
- */
-
- #include "make.h"
- #include "variable.h"
- #include "dep.h"
- #include "commands.h"
- #include "job.h"
- #include <errno.h>
-
- #ifdef MSDOS
- #include <fcntl.h>
- #include <io.h>
- #include <time.h>
- #include <process.h>
- #include <swaplib.h>
-
- extern int last_child_pid;
- extern int last_child_status;
- #endif /* MSDOS */
-
- #ifndef MSDOS
- extern int errno;
- #endif /* not MSDOS */
-
- #ifdef MSDOS
- static char *expand_function (char *o, enum function function, char *text, char *end);
- static char *string_glob (char *line);
- #else /* not MSDOS */
- static char *string_glob ();
- #endif /* not MSDOS */
-
-
- /* 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
- the length of SUBST and RLEN is the length of REPLACE. If BY_WORD is
- nonzero, substitutions are done only on matches which are complete
- whitespace-delimited words. If SUFFIX_ONLY is nonzero, substitutions are
- done only at the ends of whitespace-delimited words. */
-
- char *
- subst_expand (o, text, subst, replace, slen, rlen, by_word, suffix_only)
- char *o;
- char *text;
- char *subst, *replace;
- unsigned int slen, rlen;
- int by_word, suffix_only;
- {
- register char *t = text;
- register char *p;
-
- if (slen == 0 && !by_word && !suffix_only)
- {
- /* The first occurence of "" in any string is its end. */
- o = variable_buffer_output (o, t, strlen (t));
- if (rlen > 0)
- o = variable_buffer_output (o, replace, rlen);
- return o;
- }
-
- while ((p = sindex (t, 0, subst, slen)) != 0)
- {
- /* Output everything before this occurence of the string to replace. */
- if (p > t)
- o = variable_buffer_output (o, t, p - t);
-
- /* If we're substituting only by fully matched words,
- or only at the ends of words, check that this case qualifies. */
- if ((by_word
- && ((p > t && p[-1] != ' ' && p[-1] != '\t')
- || (p[slen] != '\0' && p[slen] != ' ' && p[slen] != '\t'))
- || (suffix_only
- && (p[slen] != '\0' && p[slen] != ' ' && p[slen] != '\t'))))
- /* Struck out. Output the rest of the string that is
- no longer to be replaced. */
- o = variable_buffer_output (o, subst, slen);
- else if (rlen > 0)
- /* Output the replacement string. */
- o = variable_buffer_output (o, replace, rlen);
-
- /* Advance T past the string to be replaced. */
- t = p + slen;
- }
-
- /* Output everything left on the end. */
- if (*t != '\0')
- o = variable_buffer_output (o, t, strlen (t));
-
- return o;
- }
-
-
- /* Store into VARIABLE_BUFFER at O the result of scanning TEXT
- and replacing strings matching PATTERN with REPLACE.
- If PATTERN_PERCENT is not nil, PATTERN has already been
- run through find_percent, and PATTERN_PERCENT is the result.
- If REPLACE_PERCENT is not nil, REPLACE has already been
- run through find_percent, and REPLACE_PERCENT is the result. */
-
- char *
- patsubst_expand (o, text, pattern, replace, pattern_percent, replace_percent)
- char *o;
- char *text;
- register char *pattern, *replace;
- register char *pattern_percent, *replace_percent;
- {
- register int pattern_prepercent_len, pattern_postpercent_len;
- register int replace_prepercent_len, replace_postpercent_len;
- register char *t;
- unsigned int len;
- int doneany = 0;
-
- /* We call find_percent on REPLACE before checking PATTERN so that REPLACE
- will be collapsed before we call subst_expand if PATTERN has no %. */
- if (replace_percent == 0)
- replace_percent = find_percent (replace);
- if (replace_percent != 0)
- {
- /* Record the length of REPLACE before and after the % so
- we don't have to compute these lengths more than once. */
- replace_prepercent_len = replace_percent - replace;
- replace_postpercent_len = strlen (replace_percent + 1);
- }
- else
- /* We store the length of the replacement
- so we only need to compute it once. */
- replace_prepercent_len = strlen (replace);
-
- if (pattern_percent == 0)
- pattern_percent = find_percent (pattern);
- if (pattern_percent == 0)
- /* With no % in the pattern, this is just a simple substitution. */
- return subst_expand (o, text, pattern, replace,
- strlen (pattern), strlen (replace), 1, 0);
-
- /* Record the length of PATTERN before and after the %
- so we don't have to compute it more than once. */
- pattern_prepercent_len = pattern_percent - pattern;
- pattern_postpercent_len = strlen (pattern_percent + 1);
-
- while (t = find_next_token (&text, &len))
- {
- int fail = 0;
-
- /* Is it big enough to match? */
- if (len < pattern_prepercent_len + pattern_postpercent_len)
- fail = 1;
-
- /* Does the prefix match? */
- if (!fail && pattern_prepercent_len > 0
- && (*t != *pattern
- || t[pattern_prepercent_len - 1] != pattern_percent[-1]
- || strncmp (t + 1, pattern + 1, pattern_prepercent_len - 1)))
- fail = 1;
-
- /* Does the suffix match? */
- if (!fail && pattern_postpercent_len > 0
- && (t[len - 1] != pattern_percent[pattern_postpercent_len]
- || t[len - pattern_postpercent_len] != pattern_percent[1]
- || strncmp (&t[len - pattern_postpercent_len],
- &pattern_percent[1], pattern_postpercent_len - 1)))
- fail = 1;
-
- if (fail)
- /* It didn't match. Output the string. */
- o = variable_buffer_output (o, t, len);
- else
- {
- /* It matched. Output the replacement. */
-
- /* Output the part of the replacement before the %. */
- o = variable_buffer_output (o, replace, replace_prepercent_len);
-
- if (replace_percent != 0)
- {
- /* Output the part of the matched string that
- matched the % in the pattern. */
- o = variable_buffer_output (o, t + pattern_prepercent_len,
- len - (pattern_prepercent_len
- + pattern_postpercent_len));
- /* Output the part of the replacement after the %. */
- o = variable_buffer_output (o, replace_percent + 1,
- replace_postpercent_len);
- }
- }
-
- /* Output a space, but not if the replacement is "". */
- if (fail || replace_prepercent_len > 0
- || (replace_percent != 0 && len + replace_postpercent_len > 0))
- {
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
- }
- if (doneany)
- /* Kill the last space. */
- --o;
-
- 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_findstring,
- function_strip,
- function_join,
- function_patsubst,
- function_filter,
- function_filter_out,
- function_foreach,
- function_sort,
- function_origin,
- function_shell,
- function_invalid
- };
-
- /* Greater than the length of any function name. */
- #define MAXFUNCTIONLEN 11
-
- /* The function names and lengths of names, for looking them up. */
-
- 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 },
- { "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 1 if PATTERN matches WORD, 0 if not. */
-
- int
- pattern_matches (pattern, percent, word)
- register char *pattern, *percent, *word;
- {
- unsigned int len;
-
- if (percent == 0)
- {
- unsigned int len = strlen (pattern) + 1;
- char *new = (char *) alloca (len);
- bcopy (pattern, new, len);
- pattern = new;
- percent = find_percent (pattern);
- if (percent == 0)
- return streq (pattern, word);
- }
-
- len = strlen (percent + 1);
-
- if (strlen (word) < (percent - pattern) + len
- || strncmp (pattern, word, percent - pattern))
- return 0;
-
- return !strcmp (percent + 1, word + (strlen (word) - len));
- }
-
- 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. */
-
- /* Note this absorbs a semicolon and is safe to use in conditionals. */
- #define BADARGS(func) \
- if (reading_filename != 0) \
- fatal ("%s:%u: Insufficient arguments to function `%s'", \
- reading_filename, *reading_lineno_ptr, func); \
- else fatal ("insufficient arguments to function `%s'", func)
-
- static char *
- expand_function (o, function, text, end)
- char *o;
- enum function function;
- char *text;
- char *end;
- {
- #ifdef MSDOS
- extern char **construct_command_argv ();
- char **argv;
- int save_stdout;
- char *pipe_file = swap_mktmpname ("pi");
- int pipe_fds;
- char *buffer = (char *) xmalloc (201);
- unsigned int maxlen = 200;
- int cc;
- #endif /* MSDOS */
- char *p, *p2, *p3;
- unsigned int i, len;
- int doneany = 0;
- int count;
- char endparen = *end, startparen = *end == ')' ? '(' : '{';
-
- switch (function)
- {
- default:
- abort ();
- break;
-
- case function_shell:
- {
- char buf[100];
- int pipedes[2];
- int pid;
-
- /* Expand the command line. */
- text = expand_argument (text, end);
-
- /* For error messages. */
- if (reading_filename != 0)
- sprintf (buf, "%s:%u: ", reading_filename, *reading_lineno_ptr);
- else
- buf[0] = '\0';
-
- #ifdef MSDOS
-
- argv = construct_command_argv (text, (struct file *) 0);
-
- #if 0
- {
- int ac = 0;
- char **av = argv;
- printf ( "expand_function(): spawning process");
- while (*av)
- printf( " $%d=`%s'", ac++, *av++);
- printf( "\n");
- }
- #endif /* NEVER */
-
- save_stdout = dup (1); /* redirect stdout */
- pipe_fds = open (pipe_file, O_CREAT|O_RDWR|O_TEXT, S_IREAD|S_IWRITE);
- if (dup2 (pipe_fds, 1) == -1)
- pfatal_with_name ("Can't redirect /dev/stdout");
-
- last_child_pid = pid = abs ((int) clock()); /* reasonably random */
-
- last_child_status = swap_spawnvpe (argv[0], argv, environ);
-
- dup2 (save_stdout, 1); /* reset stdout */
- if (lseek (pipe_fds, 0L, SEEK_SET) == -1L)
- pfatal_with_name ("Can't rewind intermediate file for pipe");
-
- /* Record the PID for child_handler. */
- shell_function_pid = pid;
- shell_function_completed = 0;
-
- /* Loop until child_handler sets shell_function_completed
- to the status of our child shell. */
- while (shell_function_completed == 0)
- wait_for_children (1, 0);
-
- /* Read from the pipe until it gets EOF. */
-
- i = 0;
- do {
- if (i == maxlen)
- {
- maxlen += 512;
- buffer = (char *) xrealloc (buffer, maxlen + 1);
- }
-
- cc = read (pipe_fds, &buffer[i], maxlen - i);
- if (cc > 0)
- i += cc;
-
- } while (cc > 0);
-
- unlink(pipe_file);
-
- /* The child finished normally. Replace all
- newlines in its output with spaces, and put
- that in the variable output buffer. */
- for (p = buffer; p < buffer + i; ++p)
- if (*p == '\n')
- *p = ' ';
-
- o = variable_buffer_output (o, buffer, i);
-
- free (buffer);
-
- #else
- if (pipe (pipedes) < 0)
- {
- perror_with_name (buf, "pipe");
- break;
- }
-
- block_children ();
-
- pid = fork ();
- if (pid < 0)
- perror_with_name (buf, "fork");
- else if (pid == 0)
- child_execute_job (0, pipedes[1], (struct file *) 0,
- construct_command_argv (text, (struct file *) 0),
- environ);
- else
- {
- /* We are the parent. Set up and read from the pipe. */
- char *buffer = (char *) xmalloc (201);
- unsigned int maxlen = 200;
- int cc;
-
- /* Record the PID for child_handler. */
- shell_function_pid = pid;
- shell_function_completed = 0;
-
- unblock_children ();
-
- /* Close the write side of the pipe. */
- (void) close (pipedes[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
-
- /* Close the read side of the pipe. */
- (void) close (pipedes[0]);
-
- /* Loop until child_handler sets shell_function_completed
- to the status of our child shell. */
- while (shell_function_completed == 0)
- wait_for_children (1, 0);
-
- 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')
- buffer[--i] = '\0';
- p = buffer;
- while ((p = index (p, '\n')) != 0)
- *p++ = ' ';
- o = variable_buffer_output (o, buffer, i);
- }
- }
-
- free (buffer);
- }
- #endif /* MSDOS */
-
- free (text);
- break;
- }
-
- case function_origin:
- /* Expand the argument. */
- text = expand_argument (text, end);
-
- {
- 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;
-
- case function_sort:
- /* Expand the argument. */
- text = expand_argument (text, end);
-
- {
- 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))
- {
- if (wordi >= nwords - 1)
- {
- nwords += 5;
- words = (char **) xrealloc ((char *) words,
- nwords * sizeof (char *));
- }
- words[wordi++] = savestring (p, len);
- }
-
- 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);
- }
-
- free (text);
- break;
-
- case function_foreach:
- {
- /* Get three comma-separated arguments but
- expand only the first two. */
- char *var, *list;
- register struct variable *v;
-
- 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);
-
- ++p;
- text = savestring (p, end - p);
-
- push_new_variable_scope ();
- v = define_variable (var, strlen (var), "", o_automatic, 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;
-
- pop_variable_scope ();
-
- free (var);
- free (list);
- free (text);
- }
- break;
-
- case function_filter:
- case function_filter_out:
- {
- char **words;
- unsigned int nwords;
- register unsigned int wordi;
-
- /* 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);
-
- text = expand_argument (p + 1, end);
-
- /* Chop TEXT up into words and then run each pattern through. */
- nwords = 10;
- words = (char **) xmalloc (10 * sizeof (char *));
- wordi = 0;
- p3 = text;
- while ((p = find_next_token (&p3, &len)) != 0)
- {
- if (wordi == nwords - 1)
- {
- nwords += 10;
- words = (char **) xrealloc ((char *) words,
- nwords * sizeof (char *));
- }
- if (*p3 != '\0')
- ++p3;
- p[len] = '\0';
- words[wordi++] = p;
- }
-
- /* 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 (i = 0; i < wordi; ++i)
- if (words[i] != 0 &&
- (percent == 0 ? streq (p, words[i])
- : pattern_matches (p, percent, words[i]))
- == (function == function_filter_out))
- words[i] = 0;
- p[len] = save;
- }
-
- /* Output the words that remain. */
- for (i = 0; i < wordi; ++i)
- if (words[i] != 0)
- {
- o = variable_buffer_output (o, words[i], strlen (words[i]));
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
- if (doneany)
- /* Kill the last space. */
- --o;
-
- free ((char *) words);
- free (p2);
- free (text);
- }
- 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");
-
- 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");
-
- text = expand_argument (text, p2);
- p3 = expand_argument (p2 + 1, p);
- p2 = expand_argument (p + 1, end);
-
- o = patsubst_expand (o, p2, text, p3, (char *) 0, (char *) 0);
-
- free (text);
- free (p3);
- free (p2);
- break;
-
- 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);
-
- {
- /* 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;
-
- tp = find_next_token (&p2, &tlen);
- if (tp != 0)
- o = variable_buffer_output (o, tp, tlen);
-
- pp = find_next_token (&p3, &plen);
- if (pp != 0)
- o = variable_buffer_output (o, pp, plen);
-
- 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;
- }
-
- free (text);
- free (p);
- break;
-
- case function_strip:
- /* Expand the argument. */
- text = expand_argument (text, end);
-
- p2 = text;
- while ((p = find_next_token (&p2, &i)) != 0)
- {
- o = variable_buffer_output (o, p, i);
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
- if (doneany)
- /* Kill the last space. */
- --o;
-
- free (text);
- break;
-
- case function_wildcard:
- text = expand_argument (text, end);
-
- p = string_glob (text);
- o = variable_buffer_output (o, p, strlen (p));
-
- free (text);
- break;
-
- 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");
-
- 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");
-
- text = expand_argument (text, p2);
- p3 = expand_argument (p2 + 1, p);
- p2 = expand_argument (p + 1, end);
-
- o = subst_expand (o, p2, text, p3, strlen (text), strlen (p3), 0, 0);
-
- free (text);
- free (p3);
- free (p2);
- break;
-
- case function_firstword:
- /* Expand the argument. */
- text = expand_argument (text, end);
-
- /* Find the first word in TEXT. */
- p2 = text;
- p = find_next_token (&p2, &i);
- if (p != 0)
- o = variable_buffer_output (o, p, i);
-
- free (text);
- break;
-
- case function_word:
- /* 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 ("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')
- {
- if (reading_filename != 0)
- fatal ("%s:%u: non-numeric first argument to `word' function",
- reading_filename, *reading_lineno_ptr);
- else
- fatal ("non-numeric first argument to `word' function");
- }
-
- i = (unsigned int) atoi (text);
- if (i == 0)
- {
- if (reading_filename != 0)
- fatal ("%s:%u: the `word' function takes a one-origin \
- index argument",
- reading_filename, *reading_lineno_ptr);
- else
- fatal ("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;
-
- case function_words:
- /* Expand the argument. */
- text = expand_argument (text, end);
-
- i = 0;
- p2 = text;
- while (find_next_token (&p2, (unsigned int *) 0) != 0)
- ++i;
-
- {
- char buf[20];
- sprintf (buf, "%d", i);
- o = variable_buffer_output (o, buf, strlen (buf));
- }
-
- free (text);
- break;
-
- 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);
-
- /* Find the first occurence of the first string in the second. */
- i = strlen (text);
- if (sindex (p, 0, text, i) != 0)
- o = variable_buffer_output (o, text, i);
-
- 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);
-
- p2 = expand_argument (p + 1, end);
-
- 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;
-
- free (p2);
- free (text);
- break;
-
- case function_dir:
- case function_basename:
- /* Expand the argument. */
- text = expand_argument (text, end);
-
- p3 = text;
- while ((p2 = find_next_token (&p3, &len)) != 0)
- {
- p = p2 + len;
- while (p >= p2 && *p != (function == function_dir ? '/' : '.'))
- --p;
- if (p >= p2)
- {
- if (function == function_dir)
- ++p;
- o = variable_buffer_output (o, p2, p - p2);
- }
- else if (function == function_dir)
- o = variable_buffer_output (o, "./", 2);
- else
- /* The entire name is the basename. */
- o = variable_buffer_output (o, p2, len);
-
- o = variable_buffer_output (o, " ", 1);
- doneany = 1;
- }
- if (doneany)
- /* Kill last space. */
- --o;
-
- free (text);
- break;
-
- case function_notdir:
- case function_suffix:
- /* Expand the argument. */
- text = expand_argument (text, end);
-
- p3 = text;
- while ((p2 = find_next_token (&p3, &len)) != 0)
- {
- p = p2 + len;
- while (p >= p2 && *p != (function == function_notdir ? '/' : '.'))
- --p;
- if (p >= p2)
- {
- ++p;
- o = variable_buffer_output (o, p, len - (p - p2));
- }
- 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;
- }
- if (doneany)
- /* Kill last space. */
- --o;
-
- free (text);
- break;
- }
-
- 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;
-
- {
- register unsigned int code;
- unsigned int maxlen;
- char *beg = *stringp + 1;
- char *endref;
-
- endref = lindex (beg, beg + MAXFUNCTIONLEN, '\0');
- maxlen = endref != 0 ? endref - beg : MAXFUNCTIONLEN;
-
- for (code = 0; function_table[code].name != 0; ++code)
- {
- if (maxlen < function_table[code].len)
- continue;
- endref = beg + function_table[code].len;
- if ((*endref == ' ' || *endref == '\t')
- && !strncmp (function_table[code].name, beg,
- function_table[code].len))
- break;
- }
- 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;
-
- /* Space after function name isn't part of the args. */
- p = next_token (endref);
- argbeg = p;
-
- /* Count nested use of whichever kind of parens we use,
- so that nested calls and variable refs work. */
-
- for (; *p != '\0'; ++p)
- {
- if (*p == openparen)
- ++count;
- else if (*p == closeparen && --count < 0)
- break;
- }
-
- /* We found the end; expand the function call. */
-
- *op = expand_function (*op, function_table[code].function, argbeg, p);
- *stringp = p;
- return 1;
- }
-
- return 0;
- }
-
- /* 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)),
- 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);
- }
-
- result[idx] = '\0';
- return result;
- }
-