]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Initial revision
authorRoland McGrath <roland@redhat.com>
Sat, 11 Jan 1992 11:37:36 +0000 (11:37 +0000)
committerRoland McGrath <roland@redhat.com>
Sat, 11 Jan 1992 11:37:36 +0000 (11:37 +0000)
default.c [new file with mode: 0644]
file.c [new file with mode: 0644]
function.c [new file with mode: 0644]
rule.c [new file with mode: 0644]

diff --git a/default.c b/default.c
new file mode 100644 (file)
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
+  };
+\f
+/* 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 (file)
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;
+}
+\f
+/* 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;
+    }
+}
+\f
+/* 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);
+    }
+}
+\f
+/* 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;
+       }
+}
+\f
+/* 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 (file)
index 0000000..1bc66e6
--- /dev/null
@@ -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 ();
+\f
+/* 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;
+}
+\f
+/* 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 }
+  };
+\f
+/* 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));
+}
+\f
+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;
+}
+\f
+/* 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;
+}
+\f
+/* 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 (file)
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 ();
+\f
+/* 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;
+\f
+/* 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);
+}
+\f
+/* 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;
+}
+\f
+/* 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;
+}
+\f
+/* 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.");
+    }
+}