| /* Variable function expansion for GNU Make. |
| Copyright (C) 1988, 89, 91, 92, 93, 94, 95 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 2, 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. */ |
| |
| #include "make.h" |
| #include "variable.h" |
| #include "dep.h" |
| #include "commands.h" |
| #include "job.h" |
| |
| #ifdef __MSDOS__ |
| #include <process.h> |
| #include <fcntl.h> |
| #endif |
| |
| static char *string_glob (); |
| |
| /* 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 occurrence 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; |
| } |
| |
| do |
| { |
| if ((by_word | suffix_only) && slen == 0) |
| /* When matching by words, the empty string should match |
| the end of each word, rather than the end of the whole text. */ |
| p = end_of_token (next_token (t)); |
| else |
| { |
| p = sindex (t, 0, subst, slen); |
| if (p == 0) |
| { |
| /* No more matches. Output everything left on the end. */ |
| o = variable_buffer_output (o, t, strlen (t)); |
| return o; |
| } |
| } |
| |
| /* Output everything before this occurrence 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 && !isblank (p[-1])) |
| || (p[slen] != '\0' && !isblank (p[slen])))) |
| || (suffix_only |
| && (p[slen] != '\0' && !isblank (p[slen])))) |
| /* 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; |
| } while (*t != '\0'); |
| |
| 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)) != 0) |
| { |
| 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 sfxlen, wordlen; |
| |
| 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); |
| } |
| |
| sfxlen = strlen (percent + 1); |
| wordlen = strlen (word); |
| |
| if (wordlen < (percent - pattern) + sfxlen |
| || strncmp (pattern, word, percent - pattern)) |
| return 0; |
| |
| return !strcmp (percent + 1, word + (wordlen - 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. */ |
| |
| /* Note this absorbs a semicolon and is safe to use in conditionals. */ |
| #define BADARGS(func) \ |
| if (reading_filename != 0) \ |
| makefile_fatal (reading_filename, *reading_lineno_ptr, \ |
| "insufficient arguments to function `%s'", \ |
| 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; |
| { |
| 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 **argv, **envp; |
| char *error_prefix; |
| int pipedes[2]; |
| int pid; |
| |
| /* Expand the command line. */ |
| text = expand_argument (text, end); |
| |
| /* Construct the argument list. */ |
| argv = construct_command_argv (text, (char *) NULL, (struct file *) 0); |
| if (argv == 0) |
| break; |
| |
| /* 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 |
| |
| /* For error messages. */ |
| if (reading_filename != 0) |
| { |
| error_prefix = (char *) alloca (strlen (reading_filename) + 100); |
| sprintf (error_prefix, |
| "%s:%u: ", reading_filename, *reading_lineno_ptr); |
| } |
| else |
| error_prefix = ""; |
| |
| #ifndef __MSDOS__ |
| if (pipe (pipedes) < 0) |
| { |
| perror_with_name (error_prefix, "pipe"); |
| break; |
| } |
| |
| pid = vfork (); |
| if (pid < 0) |
| perror_with_name (error_prefix, "fork"); |
| else if (pid == 0) |
| child_execute_job (0, pipedes[1], argv, envp); |
| else |
| { |
| /* We are the parent. */ |
| |
| char *buffer; |
| unsigned int maxlen; |
| int cc; |
| |
| /* Free the storage only the child needed. */ |
| free (argv[0]); |
| free ((char *) argv); |
| #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; |
| shell_function_completed = 0; |
| |
| |
| /* Set up and read from the pipe. */ |
| |
| maxlen = 200; |
| buffer = (char *) xmalloc (maxlen + 1); |
| |
| /* 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) |
| reap_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'; |
| else |
| buffer[i] = '\0'; |
| p = buffer; |
| while ((p = index (p, '\n')) != 0) |
| *p++ = ' '; |
| o = variable_buffer_output (o, buffer, i); |
| } |
| } |
| |
| free (buffer); |
| } |
| #else /* MSDOS. */ |
| { |
| /* MS-DOS can't do fork, but it can do spawn. 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. */ |
| |
| int save_stdout; |
| int child_stdout; |
| char tmp_output[FILENAME_MAX]; |
| FILE *child_stream; |
| unsigned int maxlen = 200; |
| int cc; |
| char *buffer; |
| |
| strcpy (tmp_output, "shXXXXXX"); |
| mktemp (tmp_output); |
| child_stdout = open (tmp_output, |
| O_WRONLY|O_CREAT|O_TRUNC|O_TEXT, 0644); |
| save_stdout = dup (1); |
| dup2 (child_stdout, 1); |
| spawnvp (P_WAIT, argv[0], argv); |
| dup2 (save_stdout, 1); |
| close (child_stdout); |
| close (save_stdout); |
| |
| child_stdout = open (tmp_output, O_RDONLY|O_TEXT, 0644); |
| |
| 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); |
| unlink (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 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)) != 0) |
| { |
| if (wordi >= nwords - 1) |
| { |
| nwords *= 2; |
| 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: |
| { |
| struct word |
| { |
| struct word *next; |
| char *word; |
| 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); |
| |
| text = expand_argument (p + 1, end); |
| |
| /* 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 word *w = (struct word *) alloca (sizeof (struct word)); |
| if (words == 0) |
| words = w; |
| else |
| wordtail->next = w; |
| wordtail = w; |
| |
| if (*p3 != '\0') |
| ++p3; |
| p[len] = '\0'; |
| w->word = 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->word) |
| : pattern_matches (p, percent, wp->word)); |
| |
| 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->word, strlen (wp->word)); |
| o = variable_buffer_output (o, " ", 1); |
| doneany = 1; |
| } |
| if (doneany) |
| /* Kill the last space. */ |
| --o; |
| } |
| |
| 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) |
| makefile_fatal (reading_filename, *reading_lineno_ptr, |
| "non-numeric first argument to `word' function"); |
| else |
| fatal ("non-numeric first argument to `word' function"); |
| } |
| |
| i = (unsigned int) atoi (text); |
| if (i == 0) |
| { |
| if (reading_filename != 0) |
| makefile_fatal (reading_filename, *reading_lineno_ptr, |
| "the `word' function takes a one-origin \ |
| index argument"); |
| 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 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); |
| |
| 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) |
| { |
| if (function == function_notdir) |
| ++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 (isblank (*endref) |
| && !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; |
| } |
| |
| if (count >= 0) |
| { |
| static const char errmsg[] |
| = "unterminated call to function `%s': missing `%c'"; |
| if (reading_filename == 0) |
| fatal (errmsg, function_table[code].name, closeparen); |
| else |
| makefile_fatal (reading_filename, *reading_lineno_ptr, errmsg, |
| function_table[code].name, closeparen); |
| } |
| |
| /* 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), |
| /* 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) |
| { |
| 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; |
| } |