📄 function.c
字号:
/* 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.GNU Make is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU Make; see the file COPYING. If not, write tothe Free Software Foundation, Inc., 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include "make.h"#include "filedef.h"#include "variable.h"#include "dep.h"#include "job.h"#include "commands.h"#ifdef _AMIGA#include "amiga.h"#endifstruct function_table_entry { const char *name; int len; int required_args; int expand_args; char *(*func_ptr) PARAMS((char *output, char **argv, const char*funcname)); };/* 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;{ unsigned int pattern_prepercent_len, pattern_postpercent_len; unsigned int replace_prepercent_len, replace_postpercent_len = 0; 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] || !strneq (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] || !strneq (&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;}/* 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...*/static const struct function_table_entry *lookup_function (table, s) const struct function_table_entry *table; const char *s;{ int len = strlen(s); 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; return NULL;}/* Return 1 if PATTERN matches STR, 0 if not. */intpattern_matches (pattern, percent, str) register char *pattern, *percent, *str;{ unsigned int sfxlen, strlength; if (percent == 0) { unsigned int len = strlen (pattern) + 1; 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); } sfxlen = strlen (percent + 1); strlength = strlen (str); if (strlength < (percent - pattern) + sfxlen || !strneq (pattern, str, percent - pattern)) return 0; return !strcmp (percent + 1, str + (strlength - sfxlen));}/* 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; else if (*ptr == endparen) { --count; if (count < 0) return NULL; } else if (*ptr == ',' && !count) return (char *)ptr; /* We didn't find anything. */ return NULL;}/* 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;}/* 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;{ int doneany = 0; /* 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 { 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); 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) { 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#if defined(__MSDOS__) || defined(WINDOWS32)#define IS_PATHSEP(c) ((c) == '/' || (c) == '\\')#else#define IS_PATHSEP(c) ((c) == '/')#endif#endifstatic 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; while (p >= p2 && (!is_suffix || *p != '.')) { if (IS_PATHSEP (*p)) break; --p; } if (p >= p2) { if (is_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 (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 (is_notdir || p >= p2) { o = variable_buffer_output (o, " ", 1); doneany = 1; } } if (doneany) /* Kill last space. */ --o; return o;}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) { p = p2 + len; while (p >= p2 && (!is_basename || *p != '.')) { if (IS_PATHSEP(*p)) break; --p; } 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 /* 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; return o;}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; int doneany =0; char *p=0; int len =0; 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -