/* To be done:
   archive members as targets or dependencies.
   deletion of targets on interrupts.  */

#include <stdio.h>
#include <sys/param.h>
#include <stat.h>
#include <sys/file.h>


#define max(a, b) ((a) > (b) ? (a) : (b))

/* A `struct linebuffer' is a structure which holds a line of text.
 `readline' reads a line from a stream into a linebuffer
 and works regardless of the length of the line.  */

struct linebuffer
  {
    long size;
    char *buffer;
  };

struct linebuffer lb1;

/* Structure that represents the info on one file
   that the makefile says how to make.
   All of these are chained together through `next'.  */

struct file
  {
    struct file *next;
    char *name;
    struct dep *deps;
    struct cmd *cmds;
    int double_colon;		/* Nonzero for double-colon entry */
    int is_target;		/* Nonzero if file is described as target */
    char *prefix;		/* common prefix, if implicit rule has been used */
  };

/* Chain of files the makefile knows how to make.  */

struct file *files;

/* Structure representing one dependency of a file.
   Each struct file's `deps' points to a chain of these,
   chained through the `next'.

   Note that the first two words of this match a struct nameseq.  */

struct dep
  {
    struct dep *next;
    char *name;
    struct file *file;
    int changed;
  };

/* Structure representing one command line for remaking a file.
   Each struct file's `cmds' points to a chain of these,
   chained through the `next'.  */

struct cmd
  {
    struct cmd *next;
    char *line;
  };

/* Structure used in chains of names, for parsing and globbing */

struct nameseq
  {
    struct nameseq *next;
    char *name;
  };

/* Structure that represents one macro definition.
   All these are changed together through `next'.  */

struct macro
  {
    struct macro *next;
    char *name;
    char *value;
    int user;			/* Nonzero if specified by command-line arg */
  };

/* Chain of all macro definitions.  */

struct macro *macros;

/* Pointers to struct macro's for the 4 built-in macros,
   $*, $@, $? and $<.  */

struct macro *star_macro;
struct macro *at_macro;
struct macro *qmark_macro;
struct macro *less_macro;

/* First file defined in the makefile whose name does not
   start with `.'.  This is the default to remake if the
   command line does not specify.  */

struct file *default_goal_file;

/* Pointer to structure for the file .SUFFIXES
   whose dependencies are the suffixes to be searched.  */

struct file *suffix_file;

/* Pointer to structure for the file .DEFAULT
   whose commands are used for any file that has none of its own.
   This is zero if the makefiles do not define .DEFAULT.  */

struct file *default_file;

/* Nonzero means do not print commands to be executed (-s).  */

int silent_flag;

/* Nonzero means just touch the files
   that would appear to need remaking (-t)  */

int touch_flag;

/* Nonzero means just print what commands would need to be executed,
   don't actually execute them (-n).  */

int just_print_flag;

/* Print debugging trace info (-d).  */

int debug_flag;

/* Nonzero means ignore status codes returned by commands
   executed to remake files.  Just treat them all as successful (-i).  */

int ignore_errors_flag;

/* Nonzero means don't remake anything, just print the data base
   that results from reading the makefile (-p).  */

int print_data_base_flag;

/* Nonzero means don't remake anything; just return a nonzero status
   if the specified targets are not up to date (-q).  */

int question_flag;

/* Nonzero means do not use any of the builtin rules (-r).  */

int no_builtin_rules_flag;

/* Nonzero means keep going even if remaking some file fails (-k).  */

int keep_going_flag;

/* The next two describe the macro output buffer.
   This buffer is used to hold the macro-expansion of a line of the makefile.
   It is made bigger with realloc whenever it is too small.
   macro_buffer_length is the size currently allocated.
   macro_buffer is the address of the buffer.  */

int macro_buffer_length;
char *macro_buffer;

/* Our working directory */

char wdir[MAXPATHLEN];

/* Forward declarations */

struct macro *define_macro ();
struct macro *lookup_macro ();
char *macro_expand ();
struct file *enter_file ();
struct file *lookup_file ();
struct cmd *store_command_line ();
char *savestring ();
void initbuffer ();
void freebuffer ();
long readline ();
char *concat ();
struct nameseq *parse_file_seq ();
struct nameseq *multi_glob ();

/* Data base of implicit rules */

char *default_suffixes = ".o .c .e .r .f .p .y .yr .ye .l .s";

char *implicit_rules[] =
  {
    ".c.o",
    "$(CC) -c $(CFLAGS) $<",
    ".p.o",
    "$(PC) -c $(PFLAGS) $<",
    ".e.o",
    "$(FC) -c $(RFLAGS) $(EFLAGS) $(FFLAGS) $<",
    ".r.o",
    "$(FC) -c $(RFLAGS) $(EFLAGS) $(FFLAGS) $<",
    ".f.o",
    "$(FC) -c $(RFLAGS) $(EFLAGS) $(FFLAGS) $<",
    ".s.o",
    "$(AS) -o $@ $<",

    ".yr.r",
    "$(YACCR) $(YFLAGS) $< ; mv y.tab.r $@",
    ".ye.e",
    "$(YACCE) $(YFLAGS) $< ; mv y.tab.e $@",
    ".y.c",
    "$(YACC) $(YFLAGS) $< ; mv y.tab.c $@",

    ".y.o",
    "$(YACC) $(YFLAGS) $< ; $(CC) $(CFLAGS) -c y.tab.c ;\
     rm y.tab.c ; mv y.tab.o $@",
    ".ye.o",
    "$(YACCE) $(YFLAGS) $< ; $(EC) $(EFLAGS) -c y.tab.e ;\
     rm y.tab.e ; mv y.tab.o $@",
    ".yr.o",
    "$(YACCR) $(YFLAGS) $< ; $(RC) $(RFLAGS) -c y.tab.r ;\
     rm y.tab.r ; mv y.tab.o $@",

    ".l.c",
    "$(LEX) $(LFLAGS) $< ; mv lex.yy.c $@",
    ".l.o",
    "$(LEX) $(LFLAGS) $< ; $(CC) $(CFLAGS) -c lex.yy.c; \
     rm lex.yy.c; mv lex.yy.o $@",
    0,
  };

char *default_macros[] =
  {
    "AS = as",
    "CC = cc",
    "FC = fc",
    "RC = fc",
    "EC = fc",
    "PC = pc",
    "LEX = lex",
    "YACC = yacc",
    "YACCR = yacc -r",
    "YACCE = yacc -e",
    0,
  };

main (argc, argv, envp)
     int argc;
     char **argv;
     char **envp;
{
  register struct file *f;
  register int i;
  int num_remade = 0;
  int status = 0;
  int this_status;
  register char **p;
  char *ptr;

  files = 0;
  macros = 0;
  macro_buffer = 0;
  getwd (wdir);

  /* Search for command line arguments that define macros,
     and do the definitions.  */

  for (i = 1; i < argc; i++)
    {
      /* Don't even try an arg that is a switch.  */
      if (!strcmp (argv[i], "-f"))
	i++;
      else if (argv[i][0] != '-')
	{
	  /* If the arg is a macro definition,
	     replace it with "-"
	     so that it will not be taken as a file to remake.  */
	  if (try_macro_definition (argv[i], 1))
	    argv[i] = "-";
	}
    }

  /* Decode the switches now */

  decode_switches (argc, argv);

  /* Define the initial list of suffixes */

  suffix_file = enter_file (".SUFFIXES");
  if (!no_builtin_rules_flag)
    {
      ptr = default_suffixes;
      suffix_file->deps = (struct dep *) parse_file_seq (&ptr, 0,
							 sizeof (struct dep));

      for (p = implicit_rules; *p;)
	{
	  f = enter_file (*p++);
	  f->cmds = (struct cmd *) xmalloc (sizeof (struct cmd));
	  f->cmds->line = *p++;
	}

      for (p = default_macros; *p;)
	try_macro_definition (*p++);
    }

  /* Read all the specified or default makefiles */

  read_all_makefiles (argc, argv);

  if (lookup_file (".IGNORE"))
    ignore_errors_flag = 1;

  if (lookup_file (".SILENT"))
    silent_flag = 1;

  default_file = lookup_file (".DEFAULT");

  /* Make each struct dep point at the struct file for the file depended on.  */

  snap_deps ();

  define_automatic_macros ();

  /* Search command line for files to remake.  */

  for (i = 1; i < argc; i++)
    {
      /* Anything not a switch or an argument of a switch
	 is a file to be remade.
	 Note that macro definition args were replaced with
	 dummies that look like switches.  */
      if (!strcmp (argv[i], "-f"))
	i++;
      else if (argv[i][0] != '-')
	{
	  f = enter_file (argv[i]);
	  num_remade++;
	  this_status = update_file (f, 1);
	  if (this_status)
	    {
	      status = this_status;
	      if (!keep_going_flag)
		break;
	    }
	}
    }

  /* Command line did not specify any file to remake => use default.  */

  if (!num_remade && default_goal_file)
    status = update_file (default_goal_file, 1);

  if (print_data_base_flag)
    print_data_base ();

  return status;
}

decode_switches (argc, argv)
     int argc;
     char **argv;
{
  register int i;
  register char *sw;

  debug_flag = 0;
  ignore_errors_flag = 0;
  keep_going_flag = 0;
  just_print_flag = 0;
  print_data_base_flag = 0;
  question_flag = 0;
  no_builtin_rules_flag = 0;
  silent_flag = 0;
  touch_flag = 0;

  for (i = 1; i < argc; i++)
    {
      sw = argv[i];
      if (strcmp (sw, "-f") && *sw++ == '-')
	while (*sw)
	  switch (*sw++)
	    {
	    case 'd':
	      debug_flag = 1;
	      break;
	    case 'i':
	      ignore_errors_flag = 1;
	      break;
	    case 'k':
	      keep_going_flag = 1;
	      break;
	    case 'n':
	      just_print_flag = 1;
	      break;
	    case 'p':
	      print_data_base_flag = 1;
	      break;
	    case 'q':
	      question_flag = 1;
	      break;
	    case 'r':
	      no_builtin_rules_flag = 1;
	      break;
	    case 's':
	      silent_flag = 1;
	      break;
	    case 't':
	      touch_flag = 1;
	      break;
	    default:
	      fatal ("unknown switch: %c", sw[-1]);
	    }
    }
}

/* Implement macros.  */

/* Define macro named NAME with value VALUE.
   LENGTH is the length of NAME, which does not
   need to be null-terminated.
   USER nonzero means this is a definition specified by the
   user in the command line; it should not be overridden
   by a later definition in a makefile.  */

struct macro *
define_macro (name, length, value, user)
     char *name, *value;
     int length;
     int user;
{
  register struct macro *m;
  register char *s;

  m = lookup_macro (name, length);
  if (m)
    {
      if (user || !m->user)
	{
	  m->value = concat (value, "", "");
	  m->user = user;
	}
      return m;
    }

  s = (char *) xmalloc (length + 1);
  bcopy (name, s, length);
  s[length] = 0;
  m = (struct macro *) xmalloc (sizeof (struct macro));
  m->name = s;
  m->value = concat (value, "", "");
  m->user = user;
  m->next = macros;
  return macros = m;
}

struct macro *
lookup_macro (name, length)
     char *name;
     int length;
{
  register struct macro *m;

  for (m = macros; m; m = m->next)
    if (!strncmp (m->name, name, length) && m->name[length] == 0)
      return m;
  return 0;
}

/* Define the automatic macros, and record the addresses
   of their structures so we can redefine them quickly.  */

define_automatic_macros ()
{
  register char *mflags;

  star_macro = define_macro ("*", 1, savestring (0, 0), 1);
  at_macro = define_macro ("@", 1, savestring (0, 0), 1);
  qmark_macro = define_macro ("?", 1, savestring (0, 0), 1);
  less_macro = define_macro ("<", 1, savestring (0, 0), 1);

  /* Define the built-in macro MFLAGS to contain the command line switches.
     We can ignore here those switches that cause commands from the
     makefile not to be run.  */
  mflags = (char *) xmalloc (20);
  strcpy (mflags, "-");
  if (debug_flag) strcat (mflags, "d");
  if (ignore_errors_flag) strcat (mflags, "i");
  if (keep_going_flag) strcat (mflags, "k");
  if (no_builtin_rules_flag) strcat (mflags, "r");
  if (silent_flag) strcat (mflags, "s");
  if (mflags[1] == 0) mflags = "";
  define_macro ("MFLAGS", 6, mflags, 0);
}

/* Try to interpret LINE (a null-terminated string)
   as a macro definition.  If it is one, define the macro and return 1.
   Otherwise return 0.

   USER nonzero means this is a definition specified by the
   user in the command line; it should not be overridden
   by a later definition in a makefile.  */

int
try_macro_definition (line, user)
     char *line;
     int user;
{
  register int c;
  register char *p = line;
  register char *beg;
  register char *end;

  while (1)
    {
      c = *p++;
      if (c == 0 || c == '\t' || c == ':' || c == '#')
	return 0;
      if (c == '=')
	break;
    }

  beg = line;
  while (*beg == ' ') beg++;

  end = p - 1;
  while (end[-1] == ' ') end--;

  while (*p == ' ' || *p == '\t') p++;

  macro_expand (p);
  define_macro (beg, end - beg, macro_buffer, user);

  return 1;
}

char *
macro_buffer_output (ptr, string, length)
     char *ptr, *string;
     int length;
{
  register int newlen = length + (ptr - macro_buffer);
  register char *new;

  if (newlen > macro_buffer_length)
    {
      macro_buffer_length = max (2 * macro_buffer_length, newlen + 100);
      new = (char *) xrealloc (macro_buffer, macro_buffer_length);
      ptr += new - macro_buffer;
      macro_buffer = new;
    }

  bcopy (string, ptr, length);
  return ptr + length;
}

/* Scan LINE for macro calls.  Build in macro_buffer
   the result of replacing all macro calls in LINE with the macro values.
   Return the address of the resulting string, which is null-terminated
   and is only valid until the next time this function is called.  */

char *
macro_expand (line)
     char *line;
{
  register char *p, *o, *p1;
  char *p0;
  register struct macro *m;

  if (!macro_buffer)
    {
      macro_buffer_length = 500;
      macro_buffer = (char *) xmalloc (macro_buffer_length);
    }

  p = line;
  o = macro_buffer;

  while (1)
    {
      p1 = (char *) index (p, '$');
      o = macro_buffer_output (o, p, p1 ? p1 - p : strlen (p) + 1);

      if (p1 == 0)
	break;
      p = p1;

      p++;
      if (*p == '$')
	{
	  o = macro_buffer_output (o, p, 1);
	  p += 2;
	}
      else if (*p == '(' || *p == '{')
	{
	  p++;
	  p0 = p;
	  for (p1 = p; *p1 && *p1 != ')' && *p1 != '}'; p1++)
	    ;
	  p = p1 + 1;
	}
      else
	{
	  p0 = p;
	  p1 = ++p;
	}
      /* p0 is start of macro name, p1 is first character after end of name.
	 p is advanced past the macro reference.  */
      m = lookup_macro (p0, p1 - p0);
      if (m)
	o = macro_buffer_output (o, m->value, strlen (m->value));
    }
  return macro_buffer;
}

/* Access the list of all file records.
   lookup_file  given a name, return the struct file * for that name,
           or 0 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 = files;

  for (; f; f = f->next)
    {
      if (!strcmp (f->name, name))
	return f;
    }
  return 0;
}

struct file *
enter_file (name)
     char *name;
{
  register struct file *f = lookup_file (name);
  if (f) return f;

  f = (struct file *) xmalloc (sizeof (struct file));
  f->name = name;
  f->cmds = 0;
  f->deps = 0;
  f->double_colon = 0;
  f->prefix = 0;
  f->is_target = 0;
  f->next = files;
  files = f;
  return f;
}

/* Print the contents of the files and macros data bases */

print_data_base ()
{
  register struct macro *m;
  register struct file *f;
  register struct dep *d;
  register struct cmd *cmd;

  printf ("Macros\n\n");

  for (m = macros; m; m = m->next)
    {
      printf ("Macro %s, value is %s\n",
	      m->name, m->value);
    }

  printf ("\nFiles\n\n");

  for (f = files; f; f = f->next)
    {
      printf ("File %s:", f->name);
      if (f->double_colon)
	printf (":");
      if (f->prefix)
	printf ("   (implicit rule prefix %s)", f->prefix);
      printf ("\n");
      for (d = f->deps; d; d = d->next)
	printf (" depends on %s\n", d->name);
      if (f->cmds)
	printf (" commands to execute:\n");
      for (cmd = f->cmds; cmd; cmd = cmd->next)
	printf ("  %s\n", cmd->line);
    }

  printf ("\nDone\n");
}

read_all_makefiles (argc, argv)
     int argc;
     char **argv;
{
  register int i;
  int num_makefiles = 0;

  for (i = 1; i < argc; i++)
    {
      if (!strcmp (argv[i], "-f"))
	{
	  read_makefile (argv[++i]);
	  num_makefiles++;
	}
    }

  if (num_makefiles == 0)
    {
      if (file_mtime ("makefile") >= 0)
	read_makefile ("makefile");
      else if (file_mtime ("Makefile") >= 0)
	read_makefile ("Makefile");
      else
	fatal ("no arguments or description file");
    }
}

read_makefile (filename)
     char *filename;
{
  register FILE *infile;
  struct linebuffer lb;
  register char *p;
  char *p2;
  struct cmd *commands = 0;
  /* Last struct cmd attached to current file,
     or 0 if none so far.  Used for adding new ones to end of chain.  */
  struct cmd *lastc;
  struct nameseq *filenames = 0;
  struct dep *deps = 0;
  int lineno = 0;
  int two_colon;

  /* First, get a stream to read */

  if (!strcmp (filename, "-"))
    infile = stdin;
  else
    infile = fopen (filename, "r");

  if (!infile)
    pfatal_with_name (filename);

  /* Loop over lines in the file */

  initbuffer (&lb);
  while (!feof (infile))
    {
      readline (&lb, infile);
      lineno++;

      /* Look for a #-comment and discard it if found */
      discard_line_comment (lb.buffer);

      p = lb.buffer;
      while (*p == ' ') p++;

      if (try_macro_definition (p, 0))
	continue;
      else if (*p == 0)
	continue;
      else if (*p == '\t')
	{
	  /* This line is a shell command */
	  while (*p && (*p == ' ' || *p == '\t')) p++;
	  if (!filenames)
	    fatal ("description file \"%s\" has command before any target file", filename);

	  lastc = store_command_line (p, &commands, lastc);
	}
      else
	{
	  /* This line describes some target files */
	  record_files (filenames, deps, commands, two_colon);
	  lastc = 0;

	  p2 = macro_expand (p);
	  filenames = multi_glob (parse_file_seq (&p2, ':',
						  sizeof (struct nameseq)),
				  wdir,
				  sizeof (struct nameseq));
	  if (!*p2++)
	    fatal ("no separator in file \"%s\", line %d", filename, lineno);

	  /* Is this a one-colon or two-colon entry?  */
	  two_colon = *p2 == ':';
	  if (two_colon) p2++;

	  /* Parse the dependencies.  */
	  deps = (struct dep *)
	    multi_glob (parse_file_seq (&p2, ';', sizeof (struct dep)),
			wdir,
			sizeof (struct dep));

	  commands = 0;
	  /* Did we stop at end of line, or at a semicolon?  */
	  if (*p2 == ';')
	    {
	      /* Semicolon means rest of line is a command */
	      lastc = store_command_line (p2 + 1, &commands, 0);
	    }
	}
    }
  record_files (filenames, deps, commands, two_colon);
  freebuffer (&lb);
  if (infile != stdin)
    fclose (infile);
}

/* Record a description line for files FILENAMES,
   with dependencies DEPS, commands to execute COMMANDS.
   TWO_COLON is nonzero if a double colon was used.

   The links of FILENAMES are freed, and so are any names in it
   that are not incorporated into other data structures.  */

record_files (filenames, deps, commands, two_colon)
     struct nameseq *filenames;
     struct dep *deps;
     struct cmd *commands;
     int two_colon;
{
  register struct file *f;
  register char *name;
  register struct dep *d;
  struct nameseq *nextf;

  for (; filenames; filenames = nextf)
    {
      nextf = filenames->next;
      name = filenames->name;
      free (filenames);
      if (!two_colon)
	{
	  /* Single-colon.  Combine these dependencies
	     with any others in file's existing record, if any.  */
	  f = enter_file (name);
	  if (f->double_colon)
	    fatal ("target file \"%s\" has both : and :: entries", f->name);
	  if (commands && f->cmds)
	    fatal ("target file \"%s\" has commands specified twice", f->name);
	  f->cmds = commands;
	  /* Defining .SUFFIXES with no dependencies
	     clears out the list of suffixes.  */
	  if (f == suffix_file && deps == 0)
	    f->deps = 0;
	  else if (f->deps)
	    {
	      for (d = f->deps; d->next; d = d->next);
	      d->next = deps;
	    }
	  else
	    f->deps = deps;
	}
      else
	{
	  /* Double-colon.  Make a new record even if file has one.  */
	  f = lookup_file (name);
	  if (f && !f->double_colon)
	    fatal ("target file \"%s\" has both : and :: entries", f->name);
	  f = (struct file *) xmalloc (sizeof (struct file));
	  f->deps = deps;
	  f->cmds = commands;
	  f->name = name;
	  f->next = files;
	  f->double_colon = 1;
	  f->prefix = 0;
	  f->is_target = 0;
	  files = f;
	}

      /* Free name if not needed further */
      if (name != f->name)
	free (name);

      /* See if this is first file seen whose name
	 does not start with a `.'.  */
      if (!default_goal_file && *name != '.')
	default_goal_file = f;
    }
}

/* Scan the null-terminated string LINE for a # not quoted.
   If one is found, replace it with a null character.  */

discard_line_comment (line)
     char *line;
{
  register char *p = line;
  register int c;
  register int instring = 0;

  while (c = *p++)
    {
      if (c == '\\')
	{
	  if (!*p++)
	    break;
	}
      else if (c == '"')
	instring = ~instring;
      else if (instring)
	continue;
      else if (c == '#')
	{
	  p[-1] = 0;
	  break;
	}
    }
}

struct cmd *
store_command_line (string, cmds, lastc)
     char *string;
     struct cmd **cmds;
     struct cmd *lastc;
{
  register struct cmd *c;

  c = (struct cmd *) xmalloc (sizeof (struct cmd));
  c->line = savestring (string, strlen (string));
  c->next = 0;
  if (lastc)
    lastc->next = c;
  else
    *cmds = c;
  return c;
}

/* Parse a string into a sequence of filenames
   represented as a chain of struct nameseq's in reverse order.
   Return that chain.

   The string is passed as STRINGP, the address of a string pointer.
   The string pointer is updated to point at the first character
   not parsed, which either is a null char or equals STOPCHAR.

   SIZE is how big to construct chain elements.
   This is useful if we want them actually to be other structures
   that have room for additional info.  */

struct nameseq *
parse_file_seq (stringp, stopchar, size)
     char **stringp;
     int stopchar;
     int size;
{
  register struct nameseq *new = 0;
  register struct nameseq *new1;
  register char *p = *stringp;
  char *q;
  char *name;
  register int c;
  struct nameseq *nexto;
  
  while (1)
    {
      /* Skip whitespace; see if any more names are left.  */
      while (*p == ' ' || *p == '\t') p++;
      if (*p == 0 || *p == stopchar)
	break;
      /* Yes, find end of next name.  */
      q = p;
      while (1)
	{
	  c = *p++;
	  if (c == 0 || c == ' ' || c == '\t' || c == stopchar)
	    break;
	}
      p--;

      /* Extract the filename just found, and skip it.  */
      name = savestring (q, p - q);

      /* Add it to the front of the chain.  */
      new1 = (struct nameseq *) xmalloc (size);
      new1->name = name;
      new1->next = new;
      new = new1;
    }

  *stringp = p;
  return new;
}

alpha_compare (s1, s2)
     char **s1, **s2;
{
  return strcmp (*s1, *s2);
}

/* Given a chain of struct nameseq's describing a sequence of filenames,
   in reverse of the intended order,
   return a new chain describing the result of globbing the filenames.
   The new chain is in forward order.
   The links of the old chain are freed or used in the new chain.
   Likewise for the names in the old chain.
   Globbing is done in directory DIR.

   SIZE is how big to construct chain elements.
   This is useful if we want them actually to be other structures
   that have room for additional info.  */

struct nameseq *
multi_glob (chain, dir, size)
     struct nameseq *chain;
     char *dir;
     int size;
{
  register struct nameseq *new = 0;
  register struct nameseq *tem;
  register struct nameseq *old;
  register char **vector;
  register int i;
  int length;
  extern char **glob_vector ();
  struct nameseq *nexto;

  for (old = chain; old; old = nexto)
    {
      nexto = old->next;

      if (glob_pattern_p (old->name))
	{
	  vector = glob_vector (old->name, dir);
	  free (old->name);
	  for (i = 0; vector[i]; i++);
	  length = i;
	  qsort (vector, length, sizeof (char *), alpha_compare);
	  for (i = length - 1; i >= 0; i--)
	    {
	      tem = (struct nameseq *) xmalloc (size);
	      tem->name = vector[i];
	      tem->next = new;
	      new = tem;
	    }
	  free (vector);
	  free (old);
	}
      else
	{
	  old->next = new;
	  new = old;
	}
    }
  return new;
}

/* For each dependency of each file, make it point at the
   file that it depends on.  */

snap_deps ()
{
  register struct file *f;
  register struct dep *d;

  for (f = files; f; f = f->next)
    for (d = f->deps; d; d = d->next)
      d->file = enter_file (d->name);
}

/* If FILE is not up to date, execute the commands for it.
   Return 0 if successful, 1 if unsuccessful;
   but with some flag settings, just call `exit' if unsuccessful.

   TOPLEVEL is nonzero if this file is a top level goal;
   in that case, if nothing needs to be done, say so.  */

int
update_file (file, toplevel)
     struct file *file;
     int toplevel;
{
  register struct dep *d;
  register int thisdate;
  register int must_redo;
  int dep_status = 0;
  int status;
  int noerror;
  int noprint;
  register char *line;
  int dep_size = 0;
  register struct cmd *cmd;
  
  if (debug_flag)
    printf ("Considering target file \"%s\".\n", file->name);

  if (file->cmds == 0 && file->is_target)
    if (!try_implicit_rule (file))
      {
	if (default_file)
	  file->cmds = default_file->cmds;
	else
	  fatal ("no specification for making file \"%s\"", file->name);
      }

  /* Update all the files we depend on, if necessary.  */
  for (d = file->deps; d; d = d->next)
    {
      dep_status |= update_file (d->file, 0);
      if (dep_status && !keep_going_flag)
	{
	  if (debug_flag)
	    printf ("Giving up on target file \"%s\".\n", file->name);
	  return dep_status;
	}
    }

  if (dep_status || print_data_base_flag)
    {
      if (debug_flag)
	printf ("Giving up on target file \"%s\".\n", file->name);
      return dep_status;
    }
  
  if (debug_flag)
    printf ("Finished dependencies of target file \"%s\".\n", file->name);

  thisdate = file_mtime (file->name);

  /* Update if any file depended on is more recent than this file,
     or if this file does not exist.
     Must in any case check all of the dependencies
     so we can create $@.  */

  must_redo = thisdate < 0;
  for (d = file->deps; d; d = d->next)
    {
      d->changed = thisdate < 0 || thisdate < file_mtime (d->name);
  
      if (debug_flag)
	printf ("File \"%s\" is %s than file \"%s\".\n",
		file->name, d->changed ? "older" : "newer", d->name);

      if (d->changed)
	{
	  must_redo = 1;
	  dep_size += strlen (d->name) + 1;
	}
    }
  
  if (debug_flag && must_redo)
    printf ("Must remake target file \"%s\".\n", file->name);

  /* If we find the file needs to be remade,
     execute its commands one by one.  */

  if (question_flag)
    return must_redo;

  if (must_redo && touch_flag)
    {
      /* Should set file's modification date and do nothing else.  */
      printf ("touch(%s)\n", file->name);
      close (open (file->name, O_APPEND + O_CREAT, 0666));
    }
  else if (must_redo)
    {
      /* First set the automatic macros according to this file */

      at_macro->value = file->name;
      star_macro->value = file->prefix ? file->prefix : "";
      less_macro->value = file->prefix ? file->deps->name : "";

      line = (char *) xmalloc (dep_size + 1);
      line[0] = 0;

      for (d = file->deps; d; d = d->next)
	{
	  if (d->changed)
	    {
	      strcat (line, d->name);
	      strcat (line, " ");
	    }
	}
      line[strlen (line) - 1] = 0;
      qmark_macro->value = line;

      /* Now execute the command lines */

      for (cmd = file->cmds; cmd; cmd = cmd->next)
	{
	  macro_expand (cmd->line);
	  line = macro_buffer;
	  noprint = 0;
	  noerror = 0;

	  /* Print each line that does not start with `@',
	     unless -s was specified.  */
	  while (*line)
	    {
	      if (*line == '@')
		noprint = 1;
	      else if (*line == '-')
		noerror = 1;
	      else if (*line == ' ' || *line == '\t')
		;
	      else
		break;
	      line++;
	    }

	  if (!silent_flag && (!noprint || just_print_flag))
	    printf ("%s\n", line);

	  /* If -n was specified, don't really execute.  */
	  if (just_print_flag)
	    continue;

	  status = system (line);
	  if (status && !ignore_errors_flag && !noerror)
	    return status;
	}

      /* Now free the macro values made above */
      free (qmark_macro->value);
      qmark_macro->value = "";
    }
  else if (toplevel && !silent_flag)
    printf ("File '%s' is up to date.\n", file->name);

  return 0;
}

/* For a FILE which has no commands specified, try to figure out some
   from the implicit rules and suffix list.
   Returns 1 if a suitable implicit rule was found,
    after modifying FILE to contain the appropriate commands and deps,
   or returns 0 if no implicit rule was found.  */

int
try_implicit_rule (file)
     struct file *file;
{
  register struct dep *d;
  register int flen = strlen (file->name);
  register int len;
  char *rulename;
  struct file *rulefile;
  char *prefix;
  char *depname;

  /* Try each defined suffix, looking for one this file has.  */
  for (d = suffix_file->deps; d; d = d->next)
    {
      len = strlen (d->name);
      if (!strcmp (file->name + flen - len, d->name))
	break;
    }

  if (!d) return 0;

  /* Found one; now extract the name sans suffix.  */
  prefix = savestring (file->name, flen - len);

  /* Now try the remaining suffixes, looking for one for which
     there is an existing file and a rule for making this one from it.  */

  for (d = d->next; d; d = d->next)
    {
      depname = concat (star_macro->value, d->name, "");
      if (file_mtime (less_macro->value) >= 0)
	{
	  rulename = concat (d->name, file->name + flen - len, "");
	  rulefile = lookup_file (rulename);
	  free (rulename);
	  if (rulefile)
	    break;
	}
      free (depname);
    }

  if (!rulefile)
    {
      free (prefix);
      return 0;
    }

  /* We found the implicit rule.  Stick it into FILE.  */

  file->cmds = rulefile->cmds;
  d = (struct dep *) xmalloc (sizeof (struct dep));
  d->name = depname;
  d->file = enter_file (depname);
  d->next = file->deps;
  file->deps = d;
  file->prefix = prefix;

  return 1;
}

/* Initialize a linebuffer for use */

void
initbuffer (linebuffer)
     struct linebuffer *linebuffer;
{
  linebuffer->size = 200;
  linebuffer->buffer = (char *) xmalloc (200);
}

void
freebuffer (linebuffer)
     struct linebuffer *linebuffer;
{
  free (linebuffer->buffer);
}

/* Read a line of text from `stream' into `linebuffer'.
 Return the length of the line.
 Combine continuation lines into one line.  */

long
readline (linebuffer, stream)
     struct linebuffer *linebuffer;
     FILE *stream;
{
  char *buffer = linebuffer->buffer;
  register char *p = linebuffer->buffer;
  register char *end = p + linebuffer->size;
  register char *p1;
  register int c;

  while (1)
    {
      c = getc (stream);
      if (p == end)
	{
	  buffer = (char *) xrealloc (buffer, linebuffer->size *= 2);
	  p += buffer - linebuffer->buffer;
	  end += buffer - linebuffer->buffer;
	  linebuffer->buffer = buffer;
	}
      if (c < 0)
	break;
      if (c == '\n')
	{
	  p1 = p;
	  while (p1 != buffer && p1[-1] == '\\') p1--;
	  if ((p1 - p) & 1)
	    {
	      /* Line ends with odd number of backslashes -- continuation!  */
	      /* Discard the backslash that means continuation.  */
	      p--;
	      /* Discard whitespace at start of next line.  */
	      while ((c = getc (stream)) == ' ' || c == '\t');
	      ungetc (c, stream);
	      /* Replace it all with one space.  */
	      c = ' ';
	    }
	  else
	    /* Line not continued.  Return what we have so far.  */
	    break;
	}
      *p++ = c;
    }

  *p = 0;
  return p - buffer;
}

int
file_mtime (name)
     char *name;
{
  struct stat st;

  if (stat (name, &st) < 0)
    return -1;
  return st.st_mtime;
}

/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */

char *
concat (s1, s2, s3)
     char *s1, *s2, *s3;
{
  int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  char *result = (char *) xmalloc (len1 + len2 + len3 + 1);

  strcpy (result, s1);
  strcpy (result + len1, s2);
  strcpy (result + len1 + len2, s3);
  *(result + len1 + len2 + len3) = 0;

  return result;
}

/* Print error message and exit.  */

fatal (s1, s2, s3)
     char *s1, *s2, *s3;
{
  printf ("make: ");
  printf (s1, s2, s3);
  printf (".  Stop.\n");
  exit (1);
}

/* Print error message.  `s1' is printf control string, `s2' is arg for it. */

error (s1, s2)
     char *s1, *s2;
{
  printf ("make: ");
  printf (s1, s2);
  printf ("\n");
}

pfatal_with_name (name)
     char *name;
{
  extern int errno, sys_nerr;
  extern char *sys_errlist[];
  char *s;

  if (errno < sys_nerr)
    s = concat ("", sys_errlist[errno], " for %s");
  else
    s = "cannot open %s";
  fatal (s, name);
}

/* Like malloc but get fatal error if memory is exhausted.  */

int
xmalloc (size)
     int size;
{
  int result = malloc (size);
  if (!result)
    fatal ("virtual memory exhausted", 0);
  return result;
}


int
xrealloc (ptr, size)
     char *ptr;
     int size;
{
  int result = realloc (ptr, size);
  if (!result)
    fatal ("virtual memory exhausted");
  return result;
}

char *
savestring (string, length)
     char *string;
     int length;
{
  register char *out = (char *) xmalloc (length + 1);
  bcopy (string, out, length);
  out[length] = 0;
  return out;
}
