summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--default.c327
-rw-r--r--file.c469
-rw-r--r--function.c1193
-rw-r--r--rule.c524
4 files changed, 2513 insertions, 0 deletions
diff --git a/default.c b/default.c
new file mode 100644
index 0000000..495603e
--- /dev/null
+++ b/default.c
@@ -0,0 +1,327 @@
+/* Data base of default implicit rules for GNU Make.
+Copyright (C) 1988, 1989, 1990, 1991, 1992 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 "rule.h"
+#include "dep.h"
+#include "file.h"
+#include "commands.h"
+#include "variable.h"
+
+
+/* This is the default list of suffixes for suffix rules.
+ `.s' must come last, so that a `.o' file will be made from
+ a `.c' or `.p' or ... file rather than from a .s file. */
+
+static char default_suffixes[]
+ = ".out .a .ln .o .c .cc .C .p .f .F .r .y .l .s .S \
+.mod .sym .def .h .info .dvi .tex .texinfo .texi .cweb .web .sh .elc .el";
+
+static struct pspec default_pattern_rules[] =
+ {
+ "(%)", "%",
+ "$(AR) $(ARFLAGS) $@ $<",
+
+ /* The X.out rules are only in BSD's default set because
+ BSD Make has no null-suffix rules, so `foo.out' and
+ `foo' are the same thing. */
+ "%.out", "%",
+ "@rm -f $@ \n cp $< $@",
+
+ 0, 0, 0
+ };
+
+static struct pspec default_terminal_rules[] =
+ {
+ /* RCS. */
+ "%", "%,v",
+ "test -f $@ || $(CO) $(COFLAGS) $< $@",
+ "%", "RCS/%,v",
+ "test -f $@ || $(CO) $(COFLAGS) $< $@",
+
+ /* SCCS. */
+ "%", "s.%",
+ "$(GET) $(GFLAGS) $<",
+ "%", "SCCS/s.%",
+ "$(GET) $(GFLAGS) $<",
+
+ 0, 0, 0
+ };
+
+static char *default_suffix_rules[] =
+ {
+ ".o",
+ "$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".s",
+ "$(LINK.s) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".S",
+ "$(LINK.S) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".c",
+ "$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".cc",
+ "$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".C",
+ "$(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".f",
+ "$(LINK.f) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".p",
+ "$(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".F",
+ "$(LINK.F) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".r",
+ "$(LINK.r) $^ $(LOADLIBES) $(LDLIBS) -o $@",
+ ".mod",
+ "$(COMPILE.mod) -o $@ -e $@ $^",
+
+ ".def.sym",
+ "$(COMPILE.def) -o $@ $<",
+
+ ".sh",
+ "cat $< >$@ \n chmod a+x $@",
+
+ ".s.o",
+#if !defined(M_XENIX) || defined(__GNUC__)
+ "$(COMPILE.s) -o $@ $<",
+#else /* Xenix. */
+ "$(COMPILE.s) -o$@ $<",
+#endif /* Not Xenix. */
+ ".S.o",
+#if !defined(M_XENIX) || defined(__GNUC__)
+ "$(COMPILE.S) -o $@ $<",
+#else /* Xenix. */
+ "$(COMPILE.S) -o$@ $<",
+#endif /* Not Xenix. */
+ ".c.o",
+ "$(COMPILE.c) $< $(OUTPUT_OPTION)",
+ ".cc.o",
+ "$(COMPILE.cc) $< $(OUTPUT_OPTION)",
+ ".C.o",
+ "$(COMPILE.C) $< $(OUTPUT_OPTION)",
+ ".f.o",
+ "$(COMPILE.f) $< $(OUTPUT_OPTION)",
+ ".p.o",
+ "$(COMPILE.p) $< $(OUTPUT_OPTION)",
+ ".F.o",
+ "$(COMPILE.F) $< $(OUTPUT_OPTION)",
+ ".r.o",
+ "$(COMPILE.r) $< $(OUTPUT_OPTION)",
+ ".mod.o",
+ "$(COMPILE.mod) -o $@ $<",
+
+ ".c.ln",
+ "$(LINT.c) -C$* $<",
+ ".y.ln",
+ "$(YACC.y) $< \n $(LINT.c) -C$* y.tab.c \n $(RM) y.tab.c",
+ ".l.ln",
+ "@$(RM) $*.c \n $(LEX.l) $< > $*.c \n\
+$(LINT.c) -i $*.c -o $@ \n $(RM) $*.c",
+
+ ".y.c",
+ "$(YACC.y) $< \n mv -f y.tab.c $@",
+ ".l.c",
+ "@$(RM) $@ \n $(LEX.l) $< > $@",
+
+ ".F.f",
+ "$(PREPROCESS.F) $< $(OUTPUT_OPTION)",
+ ".r.f",
+ "$(PREPROCESS.r) $< $(OUTPUT_OPTION)",
+
+ /* This might actually make lex.yy.c if there's no %R%
+ directive in $*.l, but in that case why were you
+ trying to make $*.r anyway? */
+ ".l.r",
+ "$(LEX.l) $< > $@ \n mv -f lex.yy.r $@",
+
+ ".S.s",
+ "$(PREPROCESS.S) $< > $@",
+
+ ".texinfo.info",
+ "$(MAKEINFO) $<",
+
+ ".texi.info",
+ "$(MAKEINFO) $<",
+
+ ".tex.dvi",
+ "$(TEX) $<",
+
+ ".texinfo.dvi",
+ "$(TEXI2DVI) $<",
+
+ ".texi.dvi",
+ "$(TEXI2DVI) $<",
+
+ ".cweb.c",
+ "$(CTANGLE) $<",
+
+ ".web.p",
+ "$(TANGLE) $<",
+
+ ".cweb.tex",
+ "$(CWEAVE) $<",
+
+ ".web.tex",
+ "$(WEAVE) $<",
+
+ 0}
+;
+
+static char *default_variables[] =
+ {
+ "AR", "ar",
+ "ARFLAGS", "rv",
+ "AS", "as",
+ "CC", "cc",
+ "C++", "g++",
+ "CO", "co",
+ "CPP", "$(CC) -E",
+#ifdef _IBMR2
+ "FC", "xlf",
+#else
+ "FC", "f77",
+#endif
+ /* System V uses these, so explicit rules using them should work.
+ However, there is no way to make implicit rules use them and FC. */
+ "F77", "$(FC)",
+ "F77FLAGS", "$(FFLAGS)",
+#ifdef USG
+ "GET", "get",
+#else
+ "GET", "/usr/sccs/get",
+#endif
+ "LD", "ld",
+ "LEX", "lex",
+ "LINT", "lint",
+ "M2C", "m2c",
+#ifdef pyr
+ "PC", "pascal",
+#else
+ "PC", "pc",
+#endif
+ "YACC", "yacc", /* Or "bison -y" */
+ "MAKEINFO", "makeinfo",
+ "TEX", "tex",
+ "TEXI2DVI", "texi2dvi",
+ "WEAVE", "weave",
+ "CWEAVE", "cweave",
+ "TANGLE", "tangle",
+ "CTANGLE", "ctangle",
+
+ "RM", "rm -f",
+
+ "LINK.o", "$(CC) $(LDFLAGS) $(TARGET_ARCH)",
+ "COMPILE.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
+ "LINK.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
+ "COMPILE.cc", "$(C++) $(C++FLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
+ "COMPILE.C", "$(COMPILE.cc)",
+ "LINK.cc", "$(C++) $(C++FLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
+ "LINK.C", "$(LINK.cc)",
+ "YACC.y", "$(YACC) $(YFLAGS)",
+ "LEX.l", "$(LEX) $(LFLAGS) -t",
+ "COMPILE.f", "$(FC) $(FFLAGS) $(TARGET_ARCH) -c",
+ "LINK.f", "$(FC) $(FFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
+ "COMPILE.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
+ "LINK.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
+ "COMPILE.r", "$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -c",
+ "LINK.r", "$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
+ "COMPILE.def", "$(M2C) $(M2FLAGS) $(DEFFLAGS) $(TARGET_ARCH)",
+ "COMPILE.mod", "$(M2C) $(M2FLAGS) $(MODFLAGS) $(TARGET_ARCH)",
+ "COMPILE.p", "$(PC) $(PFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
+ "LINK.p", "$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
+ "LINK.s", "$(CC) $(ASFLAGS) $(LDFLAGS) $(TARGET_MACH)",
+ "COMPILE.s", "$(AS) $(ASFLAGS) $(TARGET_MACH)",
+ "LINK.S", "$(CC) $(ASFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_MACH)",
+ "COMPILE.S", "$(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c",
+#if !defined(M_XENIX) || defined(__GNUC__)
+ "PREPROCESS.S", "$(CC) -E $(CPPFLAGS)",
+#else /* Xenix. */
+ "PREPROCESS.S", "$(CC) -EP $(CPPFLAGS)",
+#endif /* Not Xenix. */
+ "PREPROCESS.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -F",
+ "PREPROCESS.r", "$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -F",
+ "LINT.c", "$(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH)",
+
+#ifndef NO_MINUS_C_MINUS_O
+#if !defined(M_XENIX) || defined(__GNUC__)
+ "OUTPUT_OPTION", "-o $@",
+#else /* Xenix. */
+ "OUTPUT_OPTION", "-Fo$@",
+#endif /* Not Xenix. */
+#endif
+
+ 0, 0
+ };
+
+/* Set up the default .SUFFIXES list. */
+
+void
+set_default_suffixes ()
+{
+ suffix_file = enter_file (".SUFFIXES");
+
+ if (no_builtin_rules_flag)
+ (void) define_variable ("SUFFIXES", 8, "", o_default, 0);
+ else
+ {
+ char *p = default_suffixes;
+ suffix_file->deps = (struct dep *)
+ multi_glob (parse_file_seq (&p, '\0', sizeof (struct dep), 1),
+ sizeof (struct dep));
+ (void) define_variable ("SUFFIXES", 8, default_suffixes, o_default, 0);
+ }
+}
+
+/* Install the default pattern rules and enter
+ the default suffix rules as file rules. */
+
+void
+install_default_implicit_rules ()
+{
+ register struct pspec *p;
+ register char **s;
+
+ if (no_builtin_rules_flag)
+ return;
+
+ for (p = default_pattern_rules; p->target != 0; ++p)
+ install_pattern_rule (p, 0);
+
+ for (p = default_terminal_rules; p->target != 0; ++p)
+ install_pattern_rule (p, 1);
+
+ for (s = default_suffix_rules; *s != 0; s += 2)
+ {
+ register struct file *f = enter_file (s[0]);
+ /* Don't clobber cmds given in a makefile if there were any. */
+ if (f->cmds == 0)
+ {
+ f->cmds = (struct commands *) xmalloc (sizeof (struct commands));
+ f->cmds->filename = 0;
+ f->cmds->commands = s[1];
+ f->cmds->command_lines = 0;
+ }
+ }
+}
+
+void
+define_default_variables ()
+{
+ register char **s;
+
+ for (s = default_variables; *s != 0; s += 2)
+ (void) define_variable (s[0], strlen (s[0]), s[1], o_default, 1);
+}
diff --git a/file.c b/file.c
new file mode 100644
index 0000000..95c836e
--- /dev/null
+++ b/file.c
@@ -0,0 +1,469 @@
+/* Copyright (C) 1988, 1989, 1990, 1991 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 "commands.h"
+#include "dep.h"
+#include "file.h"
+#include "variable.h"
+#include <errno.h>
+
+
+extern int errno;
+
+
+/* Hash table of files the makefile knows how to make. */
+
+#ifndef FILE_BUCKETS
+#define FILE_BUCKETS 1007
+#endif
+static struct file *files[FILE_BUCKETS];
+
+/* Number of files with the `intermediate' flag set. */
+
+unsigned int num_intermediates = 0;
+
+
+/* Access the hash table of all file records.
+ lookup_file given a name, return the struct file * for that name,
+ or nil if there is none.
+ enter_file similar, but create one if there is none. */
+
+struct file *
+lookup_file (name)
+ char *name;
+{
+ register struct file *f;
+ register char *n;
+ register unsigned int hashval;
+
+ if (*name == '\0')
+ abort ();
+
+ while (name[0] == '.' && name[1] == '/' && name[2] != '\0')
+ name += 2;
+
+ hashval = 0;
+ for (n = name; *n != '\0'; ++n)
+ HASH (hashval, *n);
+ hashval %= FILE_BUCKETS;
+
+ for (f = files[hashval]; f != 0; f = f->next)
+ if (streq (f->name, name))
+ return f;
+ return 0;
+}
+
+struct file *
+enter_file (name)
+ char *name;
+{
+ register struct file *f, *new;
+ register char *n;
+ register unsigned int hashval;
+
+ if (*name == '\0')
+ abort ();
+
+ hashval = 0;
+ for (n = name; *n != '\0'; ++n)
+ HASH (hashval, *n);
+ hashval %= FILE_BUCKETS;
+
+ for (f = files[hashval]; f != 0; f = f->next)
+ if (streq (f->name, name))
+ break;
+
+ if (f != 0 && !f->double_colon)
+ return f;
+
+ new = (struct file *) xmalloc (sizeof (struct file));
+ bzero ((char *) new, sizeof (struct file));
+ new->name = name;
+ new->update_status = -1;
+
+ if (f == 0)
+ {
+ /* This is a completely new file. */
+ new->next = files[hashval];
+ files[hashval] = new;
+ }
+ else
+ {
+ /* There is already a double-colon entry for this file. */
+ while (f->prev != 0)
+ f = f->prev;
+ f->prev = new;
+ }
+
+ return new;
+}
+
+/* Rename FILE to NAME. This is not as simple as resetting
+ the `name' member, since it must be put in a new hash bucket,
+ and possibly merged with an existing file called NAME. */
+
+void
+rename_file (file, name)
+ register struct file *file;
+ char *name;
+{
+ char *oldname = file->name;
+ register unsigned int oldhash, newhash;
+ register char *n;
+ register struct file *f;
+ struct file *oldfile;
+
+ /* Find the hash values of the old and new names. */
+
+ oldhash = 0;
+ for (n = oldname; *n != '\0'; ++n)
+ HASH (oldhash, *n);
+ oldhash %= FILE_BUCKETS;
+
+ newhash = 0;
+ for (n = name; *n != '\0'; ++n)
+ HASH (newhash, *n);
+ newhash %= FILE_BUCKETS;
+
+ /* Look for an existing file under the new name. */
+
+ for (oldfile = files[newhash]; oldfile != 0; oldfile = oldfile->next)
+ if (streq (oldfile->name, name))
+ break;
+
+ if (newhash != oldhash || oldfile != 0)
+ {
+ /* Remove FILE from its hash bucket. */
+
+ struct file *lastf = 0;
+
+ for (f = files[oldhash]; f != file; f = f->next)
+ lastf = f;
+
+ if (lastf == 0)
+ files[oldhash] = f->next;
+ else
+ lastf->next = f->next;
+ }
+
+ /* Give FILE its new name. */
+
+ for (f = file; f != 0; f = f->prev)
+ f->name = name;
+
+ if (oldfile == 0)
+ {
+ /* There is no existing file with the new name. */
+
+ if (newhash != oldhash)
+ {
+ /* Put FILE in its new hash bucket. */
+ file->next = files[newhash];
+ files[newhash] = file;
+ }
+ }
+ else
+ {
+ /* There is an existing file with the new name.
+ We must merge FILE into the existing file. */
+
+ register struct dep *d;
+
+ if (file->cmds != 0)
+ {
+ if (oldfile->cmds == 0)
+ oldfile->cmds = file->cmds;
+ else if (file->cmds != oldfile->cmds)
+ {
+ /* We have two sets of commands. We will go with the
+ one given in the rule explicitly mentioning this name,
+ but give a message to let the user know what's going on. */
+ makefile_error (file->cmds->filename, file->cmds->lineno,
+ "Commands were specified for file `%s' at %s:%u,",
+ oldname, oldfile->cmds->filename, oldfile->cmds->lineno);
+ makefile_error (file->cmds->filename, file->cmds->lineno,
+ "but `%s' is now considered the same file \
+as `%s'.",
+ oldname, name);
+ makefile_error (file->cmds->filename, file->cmds->lineno,
+ "Commands for `%s' will be ignored \
+in favor of those for `%s'.",
+ name, oldname);
+ }
+ }
+
+ /* Merge the dependencies of the two files. */
+
+ d = oldfile->deps;
+ if (d == 0)
+ oldfile->deps = file->deps;
+ else
+ {
+ while (d->next != 0)
+ d = d->next;
+ d->next = file->deps;
+ uniquize_deps (oldfile->deps);
+ }
+
+ merge_variable_set_lists (&oldfile->variables, file->variables);
+
+ if (oldfile->double_colon && !file->double_colon)
+ fatal ("can't rename single-colon `%s' to double-colon `%s'",
+ oldname, name);
+ if (!oldfile->double_colon && file->double_colon)
+ fatal ("can't rename double-colon `%s' to single-colon `%s'",
+ oldname, name);
+
+ if (file->last_mtime > oldfile->last_mtime)
+ /* %%% Kludge so -W wins on a file that gets vpathized. */
+ oldfile->last_mtime = file->last_mtime;
+
+#define MERGE(field) oldfile->field |= file->field
+ MERGE (precious);
+ MERGE (tried_implicit);
+ MERGE (updating);
+ MERGE (updated);
+ MERGE (is_target);
+ MERGE (cmd_target);
+ MERGE (phony);
+#undef MERGE
+
+ file->renamed = oldfile;
+ }
+}
+
+/* Remove all nonprecious intermediate files.
+ If SIG is nonzero, this was caused by a fatal signal,
+ meaning that a different message will be printed, and
+ the message will go to stderr rather than stdout. */
+
+void
+remove_intermediates (sig)
+ int sig;
+{
+ register int i;
+ register struct file *f;
+ char doneany;
+
+ if (!sig && just_print_flag)
+ return;
+
+ doneany = 0;
+ for (i = 0; i < FILE_BUCKETS; ++i)
+ for (f = files[i]; f != 0; f = f->next)
+ if (f->intermediate && (f->dontcare || !f->precious))
+ {
+ int status;
+ if (just_print_flag)
+ status = 0;
+ else
+ {
+ status = unlink (f->name);
+ if (status < 0 && errno == ENOENT)
+ continue;
+ }
+ if (!f->dontcare)
+ {
+ if (sig)
+ error ("*** Deleting file `%s'", f->name);
+ else if (!silent_flag && !just_print_flag)
+ {
+ if (!doneany)
+ {
+ fputs ("rm ", stdout);
+ doneany = 1;
+ }
+ putchar (' ');
+ fputs (f->name, stdout);
+ fflush (stdout);
+ }
+ if (status < 0)
+ perror_with_name ("unlink: ", f->name);
+ }
+ }
+
+ if (doneany && !sig)
+ {
+ putchar ('\n');
+ fflush (stdout);
+ }
+}
+
+/* For each dependency of each file, make the `struct dep' point
+ at the appropriate `struct file' (which may have to be created).
+
+ Also mark the files depended on by .PRECIOUS and .PHONY. */
+
+void
+snap_deps ()
+{
+ register struct file *f, *f2;
+ register struct dep *d;
+ register int i;
+
+ /* Enter each dependency name as a file. */
+ for (i = 0; i < FILE_BUCKETS; ++i)
+ for (f = files[i]; f != 0; f = f->next)
+ for (f2 = f; f2 != 0; f2 = f2->prev)
+ for (d = f2->deps; d != 0; d = d->next)
+ if (d->name != 0)
+ {
+ d->file = lookup_file (d->name);
+ if (d->file == 0)
+ d->file = enter_file (d->name);
+ else
+ free (d->name);
+ d->name = 0;
+ }
+
+ for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev)
+ for (d = f->deps; d != 0; d = d->next)
+ for (f2 = d->file; f2 != 0; f2 = f2->prev)
+ f2->precious = 1;
+
+ for (f = lookup_file (".PHONY"); f != 0; f = f->prev)
+ for (d = f->deps; d != 0; d = d->next)
+ for (f2 = d->file; f2 != 0; f2 = f2->prev)
+ {
+ /* Mark this file as phony and nonexistent. */
+ f2->phony = 1;
+ f2->last_mtime = (time_t) -1;
+ }
+}
+
+/* Print the data base of files. */
+
+void
+print_file_data_base ()
+{
+ register unsigned int i, nfiles, per_bucket;
+ register struct file *file;
+ register struct dep *d;
+
+ puts ("\n# Files");
+
+ per_bucket = nfiles = 0;
+ for (i = 0; i < FILE_BUCKETS; ++i)
+ {
+ register unsigned int this_bucket = 0;
+
+ for (file = files[i]; file != 0; file = file->next)
+ {
+ register struct file *f;
+
+ ++this_bucket;
+
+ for (f = file; f != 0; f = f->prev)
+ {
+ putchar ('\n');
+ if (!f->is_target)
+ puts ("# Not a target:");
+ printf ("%s:%s", f->name, f->double_colon ? ":" : "");
+
+ for (d = f->deps; d != 0; d = d->next)
+ printf (" %s", dep_name (d));
+ putchar ('\n');
+
+ if (f->precious)
+ puts ("# Precious file (dependency of .PRECIOUS).");
+ if (f->phony)
+ puts ("# Phony target (dependency of .PHONY).");
+ if (f->cmd_target)
+ puts ("# Command-line target.");
+ if (f->dontcare)
+ puts ("# A default or MAKEFILES makefile.");
+ printf ("# Implicit rule search has%s been done.\n",
+ f->tried_implicit ? "" : " not");
+ if (f->stem != 0)
+ printf ("# Implicit/static pattern stem: `%s'\n", f->stem);
+ if (f->intermediate)
+ puts ("# File is an intermediate dependency.");
+ if (f->also_make != 0)
+ {
+ fputs ("# Also makes:", stdout);
+ for (d = f->also_make; d != 0; d = d->next)
+ printf (" %s", dep_name (d));
+ putchar ('\n');
+ }
+ if (f->last_mtime == (time_t) 0)
+ puts ("# Modification time never checked.");
+ else if (f->last_mtime == (time_t) -1)
+ puts ("# File does not exist.");
+ else
+ printf ("# Last modified %.24s (%ld)\n",
+ ctime (&f->last_mtime), (long int) f->last_mtime);
+ printf ("# File has%s been updated.\n",
+ f->updated ? "" : " not");
+ switch (f->command_state)
+ {
+ case cs_running:
+ puts ("# Commands currently running (?!).");
+ break;
+ case cs_deps_running:
+ puts ("# Dependencies currently being made (?!).");
+ break;
+ case cs_not_started:
+ case cs_finished:
+ switch (f->update_status)
+ {
+ case -1:
+ break;
+ case 0:
+ puts ("# Successfully updated.");
+ break;
+ case 1:
+ puts ("# Failed to be updated.");
+ break;
+ default:
+ puts ("# Invalid value in `update_status' member!");
+ fflush (stdout);
+ fflush (stderr);
+ abort ();
+ }
+ break;
+ default:
+ puts ("# Invalid value in `command_state' member!");
+ fflush (stdout);
+ fflush (stderr);
+ abort ();
+ }
+
+ if (f->variables != 0)
+ print_file_variables (file);
+
+ if (f->cmds != 0)
+ print_commands (f->cmds);
+ }
+ }
+
+ nfiles += this_bucket;
+ if (this_bucket > per_bucket)
+ per_bucket = this_bucket;
+ }
+
+ if (nfiles == 0)
+ puts ("\n# No files.");
+ else
+ {
+ printf ("\n# %u files in %u hash buckets.\n", nfiles, FILE_BUCKETS);
+#ifndef NO_FLOAT
+ printf ("# average %.1f files per bucket, max %u files in one bucket.\n",
+ ((double) FILE_BUCKETS) / ((double) nfiles) * 100.0, per_bucket);
+#endif
+ }
+}
diff --git a/function.c b/function.c
new file mode 100644
index 0000000..1bc66e6
--- /dev/null
+++ b/function.c
@@ -0,0 +1,1193 @@
+/* Variable function expansion for GNU Make.
+Copyright (C) 1988, 1989, 1991 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"
+#include <errno.h>
+
+extern int errno;
+
+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;
+ }
+
+ while ((p = sindex (t, 0, subst, slen)) != 0)
+ {
+ /* 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;
+ }
+
+ /* 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) \
+ 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:
+ {
+ extern int fork ();
+ extern int pipe ();
+ char **argv;
+ 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;
+
+ /* 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 = "";
+
+ if (pipe (pipedes) < 0)
+ {
+ perror_with_name (error_prefix, "pipe");
+ break;
+ }
+
+ push_signals_blocked_p (1);
+
+ pid = fork ();
+ if (pid < 0)
+ perror_with_name (error_prefix, "fork");
+ else if (pid == 0)
+ child_execute_job (0, pipedes[1], argv, 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;
+
+ /* 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 (argv[0]);
+ free ((char *) argv);
+ free (buffer);
+ }
+
+ pop_signals_blocked_p ();
+
+ 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:
+ {
+ 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;
+ }
+
+ /* 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), 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;
+}
diff --git a/rule.c b/rule.c
new file mode 100644
index 0000000..02302b0
--- /dev/null
+++ b/rule.c
@@ -0,0 +1,524 @@
+/* Pattern and suffix rule internals for GNU Make.
+Copyright (C) 1988, 1989, 1990, 1991 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 "commands.h"
+#include "dep.h"
+#include "file.h"
+#include "variable.h"
+#include "rule.h"
+
+static void freerule ();
+static int new_pattern_rule ();
+
+/* Chain of all pattern rules. */
+
+struct rule *pattern_rules;
+
+/* Pointer to last rule in the chain, so we can add onto the end. */
+
+struct rule *last_pattern_rule;
+
+/* Number of rules in the chain. */
+
+unsigned int num_pattern_rules;
+
+/* Maximum number of dependencies of any pattern rule. */
+
+unsigned int max_pattern_deps;
+
+/* Maximum length of the name of a dependencies of any pattern rule. */
+
+unsigned int max_pattern_dep_length;
+
+/* Pointer to structure for the file .SUFFIXES
+ whose dependencies are the suffixes to be searched. */
+
+struct file *suffix_file;
+
+/* Maximum length of a suffix. */
+
+unsigned int maxsuffix;
+
+/* Compute the maximum dependency length and maximum number of
+ dependencies of all implicit rules. Also sets the subdir
+ flag for a rule when appropriate, possibly removing the rule
+ completely when appropriate. */
+
+void
+count_implicit_rule_limits ()
+{
+ char *name;
+ unsigned int namelen;
+ register struct rule *rule, *lastrule;
+
+ num_pattern_rules = 0;
+
+ name = 0;
+ namelen = 0;
+ rule = lastrule = pattern_rules;
+ while (rule != 0)
+ {
+ unsigned int ndeps = 0;
+ register struct dep *dep;
+
+ ++num_pattern_rules;
+
+ for (dep = rule->deps; dep != 0; dep = dep->next)
+ {
+ unsigned int len = strlen (dep->name);
+ char *p = rindex (dep->name, '/');
+ char *p2 = p != 0 ? index (dep->name, '%') : 0;
+
+ ndeps++;
+
+ if (len > max_pattern_dep_length)
+ max_pattern_dep_length = len;
+
+ if (p != 0 && p2 > p)
+ {
+ if (p == dep->name)
+ ++p;
+ if (p - dep->name > namelen)
+ {
+ if (name != 0)
+ free (name);
+ namelen = p - dep->name;
+ name = (char *) xmalloc (namelen + 1);
+ }
+ bcopy (dep->name, name, p - dep->name);
+ name[p - dep->name] = '\0';
+
+ if (!dir_file_exists_p (name, "."))
+ {
+ if (*name == '/')
+ {
+ freerule (rule, lastrule);
+ rule = lastrule;
+ goto end_main_loop;
+ }
+ else
+ rule->subdir = 1;
+ }
+ }
+ }
+
+ if (ndeps > max_pattern_deps)
+ max_pattern_deps = ndeps;
+
+ end_main_loop:;
+ lastrule = rule;
+ rule = rule->next;
+ }
+
+ if (name != 0)
+ free (name);
+}
+
+/* Convert old-style suffix rules to pattern rules.
+ All rules for the suffixes on the .SUFFIXES list
+ are converted and added to the chain of pattern rules. */
+
+void
+convert_to_pattern ()
+{
+ register struct dep *d, *d2, *newd;
+ register struct file *f;
+ register char *rulename;
+ register unsigned int slen, s2len;
+ register char *name, **names;
+
+ /* Compute maximum length of all the suffixes. */
+
+ maxsuffix = 0;
+ for (d = suffix_file->deps; d != 0; d = d->next)
+ {
+ register unsigned int namelen = strlen (dep_name (d));
+ if (namelen > maxsuffix)
+ maxsuffix = namelen;
+ }
+
+ rulename = (char *) alloca ((maxsuffix * 2) + 1);
+
+ for (d = suffix_file->deps; d != 0; d = d->next)
+ {
+ /* Make a rule that is just the suffix, with no deps or commands.
+ This rule exists solely to disqualify match-anything rules. */
+ slen = strlen (dep_name (d));
+ name = (char *) xmalloc (1 + slen + 1);
+ name[0] = '%';
+ bcopy (dep_name (d), name + 1, slen + 1);
+ names = (char **) xmalloc (2 * sizeof (char *));
+ names[0] = name;
+ names[1] = 0;
+ create_pattern_rule (names, (char **) 0, 0, (struct dep *) 0,
+ (struct commands *) 0, 0);
+
+ f = d->file;
+ if (f->cmds != 0)
+ {
+ /* Record a pattern for this suffix's null-suffix rule. */
+ newd = (struct dep *) xmalloc (sizeof (struct dep));
+ /* Construct this again rather than using the contents
+ of NAME (above), since that may have been freed by
+ create_pattern_rule. */
+ newd->name = (char *) xmalloc (1 + slen + 1);
+ newd->name[0] = '%';
+ bcopy (dep_name (d), newd->name + 1, slen + 1);
+ newd->next = 0;
+ names = (char **) xmalloc (2 * sizeof (char *));
+ names[0] = savestring ("%", 1);
+ names[1] = 0;
+ create_pattern_rule (names, (char **) 0, 0, newd, f->cmds, 0);
+ }
+
+ /* Record a pattern for each of this suffix's two-suffix rules. */
+ bcopy (dep_name (d), rulename, slen);
+ for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next)
+ {
+ s2len = strlen (dep_name (d2));
+
+ if (slen == s2len && streq (dep_name (d), dep_name (d2)))
+ continue;
+
+ bcopy (dep_name (d2), rulename + slen, s2len + 1);
+ f = lookup_file (rulename);
+ if (f == 0 || f->cmds == 0)
+ continue;
+
+ if (s2len == 2 && rulename[slen] == '.' && rulename[slen + 1] == 'a')
+ /* The suffix rule `.X.a:' is converted
+ to the pattern rule `(%.o): %.X'. */
+ name = savestring ("(%.o)", 5);
+ else
+ {
+ /* The suffix rule `.X.Y:' is converted
+ to the pattern rule `%.Y: %.X'. */
+ name = (char *) xmalloc (1 + s2len + 1);
+ name[0] = '%';
+ bcopy (dep_name (d2), name + 1, s2len + 1);
+ }
+ names = (char **) xmalloc (2 * sizeof (char *));
+ names[0] = name;
+ names[1] = 0;
+ newd = (struct dep *) xmalloc (sizeof (struct dep));
+ newd->next = 0;
+ /* Construct this again (see comment above). */
+ newd->name = (char *) xmalloc (1 + slen + 1);
+ newd->name[0] = '%';
+ bcopy (dep_name (d), newd->name + 1, slen + 1);
+ create_pattern_rule (names, (char **) 0, 0, newd, f->cmds, 0);
+ }
+ }
+}
+
+
+/* Install the pattern rule RULE (whose fields have been filled in)
+ at the end of the list (so that any rules previously defined
+ will take precedence). If this rule duplicates a previous one
+ (identical target and dependents), the old one is replaced
+ if OVERRIDE is nonzero, otherwise this new one is thrown out.
+ When an old rule is replaced, the new one is put at the end of the
+ list. Return nonzero if RULE is used; zero if not. */
+
+static int
+new_pattern_rule (rule, override)
+ register struct rule *rule;
+ int override;
+{
+ register struct rule *r, *lastrule;
+ register unsigned int i, j;
+
+ rule->subdir = 0;
+ rule->in_use = 0;
+ rule->terminal = 0;
+
+ rule->next = 0;
+
+ /* Search for an identical rule. */
+ lastrule = pattern_rules;
+ for (r = pattern_rules; r != 0; lastrule = r, r = r->next)
+ for (i = 0; rule->targets[i] != 0; ++i)
+ for (j = 0; r->targets[j] != 0; ++j)
+ if (streq (rule->targets[i], r->targets[j]))
+ {
+ register struct dep *d, *d2;
+ for (d = rule->deps, d2 = r->deps;
+ d != 0 && d2 != 0; d = d->next, d2 = d2->next)
+ if (!streq (dep_name (d), dep_name (d2)))
+ break;
+ if (d == 0 && d2 == 0)
+ /* All the dependencies matched. */
+ if (override)
+ {
+ /* Remove the old rule. */
+ freerule (r, lastrule);
+ /* Install the new one. */
+ if (pattern_rules == 0)
+ pattern_rules = rule;
+ else
+ last_pattern_rule->next = rule;
+ last_pattern_rule = rule;
+
+ /* We got one. Stop looking. */
+ goto matched;
+ }
+ else
+ {
+ /* The old rule stays intact. Destroy the new one. */
+ freerule (rule, (struct rule *) 0);
+ return 0;
+ }
+ }
+
+ matched:;
+
+ if (r == 0)
+ {
+ /* There was no rule to replace. */
+ if (pattern_rules == 0)
+ pattern_rules = rule;
+ else
+ last_pattern_rule->next = rule;
+ last_pattern_rule = rule;
+ }
+
+ return 1;
+}
+
+
+/* Install an implicit pattern rule based on the three text strings
+ in the structure P points to. These strings come from one of
+ the arrays of default implicit pattern rules.
+ TERMINAL specifies what the `terminal' field of the rule should be. */
+
+void
+install_pattern_rule (p, terminal)
+ struct pspec *p;
+ int terminal;
+{
+ register struct rule *r;
+ char *ptr;
+
+ r = (struct rule *) xmalloc (sizeof (struct rule));
+
+ r->targets = (char **) xmalloc (2 * sizeof (char *));
+ r->suffixes = (char **) xmalloc (2 * sizeof (char *));
+ r->lens = (unsigned int *) xmalloc (2 * sizeof (unsigned int));
+
+ r->targets[1] = 0;
+ r->suffixes[1] = 0;
+ r->lens[1] = 0;
+
+ r->lens[0] = strlen (p->target);
+ /* These will all be string literals, but we malloc space for
+ them anyway because somebody might want to free them later on. */
+ r->targets[0] = savestring (p->target, r->lens[0]);
+ r->suffixes[0] = find_percent (r->targets[0]);
+ if (r->suffixes[0] == 0)
+ /* Programmer-out-to-lunch error. */
+ abort ();
+ else
+ ++r->suffixes[0];
+
+ ptr = p->dep;
+ r->deps = (struct dep *) multi_glob (parse_file_seq (&ptr, '\0',
+ sizeof (struct dep), 1),
+ sizeof (struct dep));
+
+ if (new_pattern_rule (r, 0))
+ {
+ r->terminal = terminal;
+ r->cmds = (struct commands *) xmalloc (sizeof (struct commands));
+ r->cmds->filename = 0;
+ r->cmds->lineno = 0;
+ /* These will all be string literals, but we malloc space for them
+ anyway because somebody might want to free them later. */
+ r->cmds->commands = savestring (p->commands, strlen (p->commands));
+ r->cmds->command_lines = 0;
+ }
+}
+
+
+/* Free all the storage used in RULE and take it out of the
+ pattern_rules chain. LASTRULE is the rule whose next pointer
+ points to RULE. */
+
+static void
+freerule (rule, lastrule)
+ register struct rule *rule, *lastrule;
+{
+ struct rule *next = rule->next;
+ register unsigned int i;
+
+ for (i = 0; rule->targets[i] != 0; ++i)
+ free (rule->targets[i]);
+
+ free ((char *) rule->targets);
+ free ((char *) rule->suffixes);
+ free ((char *) rule->lens);
+
+ /* We can't free the storage for the commands because there
+ are ways that they could be in more than one place:
+ * If the commands came from a suffix rule, they could also be in
+ the `struct file's for other suffix rules or plain targets given
+ on the same makefile line.
+ * If two suffixes that together make a two-suffix rule were each
+ given twice in the .SUFFIXES list, and in the proper order, two
+ identical pattern rules would be created and the second one would
+ be discarded here, but both would contain the same `struct commands'
+ pointer from the `struct file' for the suffix rule. */
+
+ free ((char *) rule);
+
+ if (lastrule == 0)
+ return;
+
+ if (pattern_rules == rule)
+ if (lastrule != pattern_rules)
+ abort ();
+ else
+ pattern_rules = next;
+ else
+ lastrule->next = next;
+ if (last_pattern_rule == rule)
+ last_pattern_rule = lastrule;
+}
+
+/* Create a new pattern rule with the targets in the nil-terminated
+ array TARGETS. If TARGET_PERCENTS is not nil, it is an array of
+ pointers into the elements of TARGETS, where the `%'s are.
+ The new rule has dependencies DEPS and commands from COMMANDS.
+ It is a terminal rule if TERMINAL is nonzero. This rule overrides
+ identical rules with different commands if OVERRIDE is nonzero.
+
+ The storage for TARGETS and its elements is used and must not be freed
+ until the rule is destroyed. The storage for TARGET_PERCENTS is not used;
+ it may be freed. */
+
+void
+create_pattern_rule (targets, target_percents,
+ terminal, deps, commands, override)
+ char **targets, **target_percents;
+ int terminal;
+ struct dep *deps;
+ struct commands *commands;
+ int override;
+{
+ register struct rule *r = (struct rule *) xmalloc (sizeof (struct rule));
+ register unsigned int max_targets, i;
+
+ r->cmds = commands;
+ r->deps = deps;
+ r->targets = targets;
+
+ max_targets = 2;
+ r->lens = (unsigned int *) xmalloc (2 * sizeof (unsigned int));
+ r->suffixes = (char **) xmalloc (2 * sizeof (char *));
+ for (i = 0; targets[i] != 0; ++i)
+ {
+ if (i == max_targets - 1)
+ {
+ max_targets += 5;
+ r->lens = (unsigned int *)
+ xrealloc ((char *) r->lens, max_targets * sizeof (unsigned int));
+ r->suffixes = (char **)
+ xrealloc ((char *) r->suffixes, max_targets * sizeof (char *));
+ }
+ r->lens[i] = strlen (targets[i]);
+ r->suffixes[i] = (target_percents == 0 ? find_percent (targets[i])
+ : target_percents[i]) + 1;
+ if (r->suffixes[i] == 0)
+ abort ();
+ }
+
+ if (i < max_targets - 1)
+ {
+ r->lens = (unsigned int *) xrealloc ((char *) r->lens,
+ (i + 1) * sizeof (unsigned int));
+ r->suffixes = (char **) xrealloc ((char *) r->suffixes,
+ (i + 1) * sizeof (char *));
+ }
+
+ if (new_pattern_rule (r, override))
+ r->terminal = terminal;
+}
+
+/* Print the data base of rules. */
+
+void
+print_rule_data_base ()
+{
+ register unsigned int rules, terminal, subdir;
+ register struct rule *r;
+ register struct dep *d;
+ register unsigned int i;
+
+ puts ("\n# Implicit Rules");
+
+ rules = terminal = subdir = 0;
+ for (r = pattern_rules; r != 0; r = r->next)
+ {
+ ++rules;
+
+ putchar ('\n');
+ for (i = 0; r->targets[i] != 0; ++i)
+ {
+ fputs (r->targets[i], stdout);
+ if (r->targets[i + 1] != 0)
+ putchar (' ');
+ else
+ putchar (':');
+ }
+ if (r->terminal)
+ {
+ ++terminal;
+ putchar (':');
+ }
+
+ for (d = r->deps; d != 0; d = d->next)
+ printf (" %s", dep_name (d));
+ putchar ('\n');
+
+ if (r->subdir)
+ {
+ ++subdir;
+ puts ("# references nonexistent subdirectory.");
+ }
+
+ if (r->cmds != 0)
+ print_commands (r->cmds);
+ }
+
+ if (rules == 0)
+ puts ("\n# No implicit rules.");
+ else
+ {
+ printf ("\n# %u implicit rules, %u", rules, terminal);
+#ifndef NO_FLOAT
+ printf (" (%.1f%%)", (double) terminal / (double) rules * 100.0);
+#endif
+ puts (" terminal.");
+
+ printf ("# %u", subdir);
+#ifndef NO_FLOAT
+ printf (" (%.1f%%)", (double) subdir / (double) rules * 100.0);
+#endif
+ puts (" reference nonexistent subdirectories.");
+ }
+}