/* `make' recompile files as needed.
   Copyright (C) 1985 Richard M. Stallman

		       NO WARRANTY

  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.

 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.

		GENERAL PUBLIC LICENSE TO COPY

  1. You may copy and distribute verbatim copies of this source file
as you receive it, in any medium, provided that you conspicuously
and appropriately publish on each copy a valid copyright notice
"Copyright (C) 1986 Martin Minow, Richard M. Stallman"; and include
following the copyright notice a verbatim copy of the above disclaimer
of warranty and of this License.

  2. You may modify your copy or copies of this source file or
any portion of it, and copy and distribute such modifications under
the terms of Paragraph 1 above, provided that you also do the following:

    a) cause the modified files to carry prominent notices stating
    that you changed the files and the date of any change; and

    b) cause the whole of any work that you distribute or publish,
    that in whole or in part contains or is a derivative of this
    program or any part thereof, to be freely distributed
    and licensed to all third parties on terms identical to those
    contained in this License Agreement (except that you may choose
    to grant more extensive warranty protection to third parties,
    at your option).

  3. You may copy and distribute this program or any portion of it in
compiled, executable or object code form under the terms of Paragraphs
1 and 2 above provided that you do the following:

    a) cause each such copy to be accompanied by the
    corresponding machine-readable source code, which must
    be distributed under the terms of Paragraphs 1 and 2 above; or,

    b) cause each such copy to be accompanied by a
    written offer, with no time limit, to give any third party
    free (except for a nominal shipping charge) a machine readable
    copy of the corresponding source code, to be distributed
    under the terms of Paragraphs 1 and 2 above; or,

    c) in the case of a recipient of this program in compiled, executable
    or object code form (without the corresponding source code) you
    shall cause copies you distribute to be accompanied by a copy
    of the written offer of source code which you received along
    with the copy you received.

  4. You may not copy, sublicense, distribute or transfer this program
except as expressly provided under this License Agreement.  Any attempt
otherwise to copy, sublicense, distribute or transfer this program is void and
your rights to use the program under this License agreement shall be
automatically terminated.  However, parties who have received computer
software programs from you with this License Agreement will not have
their licenses terminated so long as such parties remain in full compliance.

 In other words, you are welcome to use, share and improve this program.
 You are forbidden to forbid anyone else to use, share and improve
 what you give them.   Help stamp out software-hoarding!  */


/* Non-berkeley features:
  A target that depends on .RECURSIVE has its commands run
  even if -t or -q or -n is specified.
*/

/* Missing Berkeley features:
  Names of the form a((b)) are not supported.
*/

#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/time.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 */
    int update_status;		/* Status of last attempt to update,
				   or -1 if none made.  */
    int updated;		/* Nonzero if this file has been remade.  */
    int last_mtime;		/* File's modtime, if already known.  */
    int tried_implicit;		/* Nonzero => have searched for implicit rule
				   for making this file; don't search again */
    struct file *prev;		/* Previous entry for same file name;
				   used when there are multiple double-colon
				   entries for the same file.  */
    int precious;		/* Non-0 means don't delete this file on quit */
    int recursive;		/* Non-0 means do file's commands even if -q or -t or -n.  */
  };

/* Incremented when any file might actually be changed on disk;
   invalidates all files' `update_status' fields.
   Note that we assume that any command might alter any file.
   An alternative would be to assume that remaking a target
   alters only that target, but I am not sure this is supposed to be true.  */

int tick;

/* Incremented when we find a file needs to be remade,
   even if it has no commands or if we are not really executing commands.  */

int files_remade;

/* 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 three words of this match a struct nameseq.  */

struct dep
  {
    struct dep *next;
    char *name;
    int quotedparen;
    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;
    int quotedparen;
  };

/* 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;

/* Pointer to struct macro for SHELL.  So we can look up the value fast.  */

struct macro *shell_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;

/* Maximum length of a suffix.  */

int maxsuffix;

/* 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;

/* Pointer to the structure for the file .PRECIOUS,
   being depended onby which means that a target should not be
   deleted if its commands are interrupted.  */

struct file *precious_file;

/* Pointer to the structure for the file .RECURSIVE,
   being depended on by which means that a target's commands
   should be executed even if -t.  */

struct file *recursive_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];


/* Nonzero => file to delete if fatal signal happens.
   While actually running shell commands, this is their target.  */

char *signal_delete_file;

/* But only delete the file if it has been changed;
   that is, its mtime is not the same as this.  */

int signal_delete_mtime;

/* Forward declarations */

struct macro *define_macro ();
struct macro *lookup_macro ();
char *macro_expand ();
int try_macro_definition ();
struct file *enter_file ();
struct file *lookup_file ();
struct cmd *store_command_line ();
int update_file ();
void initbuffer ();
void freebuffer ();
long readline ();
void print_spaces ();
int file_mtime (), name_mtime ();
char *savestring (),*concat ();
char *xmalloc (), *xrealloc ();
void fatal (), error (), perror_with_name (), pfatal_with_name ();
struct nameseq *parse_file_seq ();
struct nameseq *multi_glob ();
struct cmd *split_command_line ();

/* 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 $(EFLAGS) $<",
    ".r.o",
    "$(FC) -c $(RFLAGS) $<",
    ".f.o",
    "$(FC) -c $(FFLAGS) $<",
    ".s.o",
    "$(AS) -o $@ $<",

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

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

    ".l.c",
    "$(LEX) $(LFLAGS) $< \n mv lex.yy.c $@",
    ".l.o",
    "$(LEX) $(LFLAGS) $< \n $(CC) $(CFLAGS) -c lex.yy.c\n \
     rm lex.yy.c\n 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,
  };

/* Given a string of multiple lines,
   return a chain of struct cmd's, one for each line,
   with the lines in the same order as in the string.  */

struct cmd *
split_command_line (line)
     char *line;
{
  register struct cmd *c;
  register struct cmd *oc = 0;
  register char *p = line + strlen (line);
  register char *p1, *p2;

  while (p)
    {
      p1 = (char *) rindex (p, '\n');
      if (p1 == 0)
	p2 = line;
      else
	p2 = p1 + 1;

      while (*p2 == ' ' || *p2 == '\t') p2++;
      c = (struct cmd *) xmalloc (sizeof (struct cmd));
      c->line = savestring (p2, p - p2);
      c->next = oc;
      oc = c;
      p = p1;
    }
  return oc;
}

/* Handle bus errors, illegal instruction, etc. */
fatal_error_signal (sig)
     int sig;
{
  struct stat st;

  signal (sig, SIG_DFL);
  sigsetmask (0);

  /* Don't die until our children have finished dying.  */
  wait (0);

  /* If we have specified a file to delete for this moment,
     and it exists and is not a directory, delete it.  */

  if (signal_delete_file
      &&
      stat (signal_delete_file, &st) >= 0
      &&
      (st.st_mode & S_IFMT) != S_IFDIR
      &&
      st.st_mtime != signal_delete_mtime)
    {
      fprintf (stderr, "\nDeleting file %s\n", signal_delete_file);
      fflush (stderr);
      unlink (signal_delete_file);
    }

  /* Signal the same code; this time it will really be fatal.  */
  kill (getpid (), sig);
  pfatal_with_name ("kill");
}

int
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;
  void decode_switches (), define_automatic_macros (),
       print_data_base (), read_all_makefiles (), snap_deps ();

  files = 0;
  files_remade = 0;
  macros = 0;
  macro_buffer = 0;
  tick = 0;
  default_goal_file = 0;
  signal_delete_file = 0;
  signal_delete_mtime = 0;

  signal (SIGHUP, fatal_error_signal);
  signal (SIGQUIT, fatal_error_signal);
  signal (SIGINT, fatal_error_signal);
  signal (SIGILL, fatal_error_signal);
  signal (SIGTRAP, fatal_error_signal);
  signal (SIGIOT, fatal_error_signal);
  signal (SIGEMT, fatal_error_signal);
  signal (SIGFPE, fatal_error_signal);
  signal (SIGBUS, fatal_error_signal);
  signal (SIGSEGV, fatal_error_signal);
  signal (SIGSYS, fatal_error_signal);
  signal (SIGTERM, fatal_error_signal);
#ifdef SIGXCPU
  signal (SIGXCPU, fatal_error_signal);
#endif
#ifdef SIGXFSZ
  signal (SIGXFSZ, fatal_error_signal);
#endif SIGXFSZ

  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;
      /* Why do multi-glob here?  None of the suffixes contains *.
	 But multi-glob is easiest way of reverseing the chain.  */
      suffix_file->deps
	= (struct dep *) multi_glob (parse_file_seq (&ptr, 0,
						     sizeof (struct dep)),
				     "", sizeof (struct dep));

      for (p = implicit_rules; *p;)
	{
	  f = enter_file (*p++);
	  f->cmds = split_command_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, 0);
	  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, 0);

  if (print_data_base_flag)
    print_data_base ();

  return status;
}

void
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.  */

void
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);

  shell_macro = define_macro ("SHELL", 5, savestring ("sh", 2), 0);

  /* 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 (just_print_flag) strcat (mflags, "n");
  if (question_flag) strcat (mflags, "q");
  if (no_builtin_rules_flag) strcat (mflags, "r");
  if (silent_flag) strcat (mflags, "s");
  if (touch_flag) strcat (mflags, "t");
  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++;
	}
      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));
  bzero (f, sizeof *f);
  f->name = name;
  f->update_status = -1;
  f->next = files;
  files = f;
  return f;
}

/* Print the contents of the files and macros data bases */

void
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->is_target)
	printf (":");
      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");
  fflush (stdout);
}

void read_makefile (), record_files (), discard_line_comment ();

void
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 (name_mtime ("makefile") >= 0)
	read_makefile ("makefile");
      else if (name_mtime ("Makefile") >= 0)
	read_makefile ("Makefile");
      else
	fatal ("no arguments or description file");
    }
}

void
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;
  char *cmdleft;

  /* 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;

	  cmdleft = (char *) index (p, ';');
	  if (cmdleft)
	    {
	      p = savestring (p, cmdleft - p);
	      p2 = macro_expand (p);
	      free (p);
	    }
	  else
	    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 (cmdleft)
	    {
	      /* Semicolon means rest of line is a command */
	      lastc = store_command_line (cmdleft + 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.  */

void
record_files (filenames, deps, commands, two_colon)
     struct nameseq *filenames;
     struct dep *deps;
     struct cmd *commands;
     int two_colon;
{
  register struct file *f, *f1;
  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);
	  /* Check for two single-colon entries both with commands.
	     Check is_target so that we don't lose on files such as .c.o
	     whose commands were preinitialized.  */
	  if (commands && f->cmds && f->is_target)
	    fatal ("target file \"%s\" has commands specified twice", f->name);
	  f->is_target = 1;
	  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.  */
	  f1 = lookup_file (name);
	  if (f1 && !f1->double_colon)
	    fatal ("target file \"%s\" has both : and :: entries", f->name);
	  f = (struct file *) xmalloc (sizeof (struct file));
	  bzero (f, sizeof *f);
	  f->deps = deps;
	  f->cmds = commands;
	  f->name = name;
	  f->next = files;
	  f->double_colon = 1;
	  f->is_target = 1;
	  f->update_status = -1;
	  f->prev = f1;
	  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.  */

void
discard_line_comment (line)
     char *line;
{
  register char *p = line;
  register int c;
  /* -1 if not inside string; else desired terminator char.  */
  register int instring = -1;

  while (c = *p++)
    {
      if (c == '\\')
	{
	  if (!*p++)
	    break;
	}
      else if (c == instring)
	instring = -1;
      else if (c == '"' || c == '\'' || c == '`')
	instring = c;
      else if (instring < 0 && 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;
}

int
alpha_compare (s1, s2)
     char **s1, **s2;
{
  return strcmp (*s1, *s2);
}

/* Remove quoting backslashes from a string
   by compacting the string down into itself.
   Backslashes quoted by other backslashes are not removed.  */

void
dequote (string)
     char *string;
{
  register char *in, *out;
  register int c;

  in = string;
  out = string;

  while (c = *in++)
    {
      if (c != '\\')
	*out++ = c;
      else if (c = *in++)
	*out++ = c;
    }
}

/* 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;
	      tem->quotedparen = 1;
	      new = tem;
	    }
	  free (vector);
	  free (old);
	}
      else
	{
	  old->quotedparen = !strcmp (old->name + strlen (old->name) - 2, "\\)");
	  dequote (old->name);
	  old->next = new;
	  new = old;
	}
    }
  return new;
}


/* For each dependency of each file, make it point at the
   file that it depends on.

   Also mark the files depended on by .PRECIOUS and .RECURSIVE.  */

void
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);

  /* Compute maximum length of a suffix.  */
  maxsuffix = 0;
  for (d = suffix_file->deps; d; d = d->next)
    {
      maxsuffix = max (maxsuffix, strlen (d->name));
    }

  f = lookup_file (".PRECIOUS");
  if (f)
    for (d = f->deps; d; d = d->next)
      {
	for (f = d->file; f; f = f->prev)
	  f->precious = 1;
      }

  f = lookup_file (".RECURSIVE");
  if (f)
    for (d = f->deps; d; d = d->next)
      {
	for (f = d->file; f; f = f->prev)
	  f->recursive = 1;
      }
}

int update_file_1 (), execute_file_commands (), try_implicit_rule ();
int run_shell_command ();
int ar_touch (), ar_scan_1 ();
extern int ar_scan (), ar_member_touch ();

/* 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.

   DEPTH is the depth in recursions of this function.
   We increment it during the consideration of our dependencies,
   then decrement it again after finding out whether this file
   is out of date.

   If there are multiple entries for FILE, consider each in turn.  */

int
update_file (file, depth)
     struct file *file;
     int depth;
{
  register int status;
  register struct file *f = file;
  int ofiles_remade = files_remade;

  while (f)
    {
      status = update_file_1 (f, depth);
      if (status && !keep_going_flag)
	return status;
      f = f->prev;
    }

  /* For a top level target, if we have found nothing whatever to do for it,
     print a message saying nothing needs doing.  */

  if (ofiles_remade == files_remade
      && !print_data_base_flag
      && depth == 0 && !silent_flag && !question_flag)
    {
      printf ("File \"%s\" is up to date.\n", file->name);
      fflush (stdout);
    }

  return status;
}

/* Consider a single struct file, even for a file that has more than one.  */

#define DEBUGPR(msg) \
  if (debug_flag) \
    {  print_spaces (depth);  printf (msg, file->name); fflush (stdout);  }

int
update_file_1 (file, depth)
     struct file *file;
     int depth;
{
  register int thisdate;
  register int must_redo;
  int dep_status = 0;
  register struct dep *d;
  int fd;
  int status;
  struct timeval timevals[2];
  
  DEBUGPR ("Considering target file \"%s\".\n");
  depth++;

  if (file->updated)
    {
      if (file->update_status > 0)
	{
	  depth--;
	  DEBUGPR ("Recently tried and failed to update file \"%s\.\n");
	  return file->update_status;
	}
	
      DEBUGPR ("File \"%s\" was considered recently.\n");
      must_redo = file->update_status;
    }
  else
    {
      /* If file was specified as a target with no commands,
	 come up with some default commands */

      if (file->cmds == 0 && !file->tried_implicit)
	{
	  if (!try_implicit_rule (file, depth))
	    {
	      if (default_file)
		file->cmds = default_file->cmds;
	    }
	  file->tried_implicit = 1;
	}

      /* Update all the files we depend on, if necessary.  */
      for (d = file->deps; d; d = d->next)
	{
	  dep_status |= update_file (d->file, depth);
	  if (dep_status && !keep_going_flag)
	    break;
	}

      DEBUGPR ("Finished dependencies of target file \"%s\".\n");

      if (print_data_base_flag)
	{
	  depth--;
	  DEBUGPR ("Would now consider updating \"%s\".\n");
	  return 0;
	}

      if (dep_status)
	{
	  depth--;
	  if (depth == 0 && keep_going_flag)
	    {
	      printf ("File %s not remade because of errors.\n", file->name);
	      fflush (stdout);
	    }
	  DEBUGPR ("Giving up on target file \"%s\".\n");
	  return dep_status;
	}

      thisdate = file_mtime (file);

      /* 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;
      if (must_redo)
	DEBUGPR ("File \"%s\" does not exist.\n");

      for (d = file->deps; d; d = d->next)
	{
	  d->changed = thisdate < 0 || thisdate < file_mtime (d->file);

	  if (debug_flag)
	    {
	      print_spaces (depth);
	      printf ("File \"%s\" is %s than file \"%s\".\n",
		      file->name, d->changed ? "older" : "newer", d->name);
	      fflush (stdout);
	    }

	  if (d->changed)
	    must_redo = 1;
	}
    }  

  /* Here depth returns to the value it had when we were called */
  depth--;

  file->update_status = must_redo ? -1 : 0;
  file->updated = 1;

  if (!must_redo)
    {
      DEBUGPR ("No need to remake target file \"%s\".\n");
      return 0;
    }

  DEBUGPR ("Must remake target file \"%s\".\n");

  /* Now, take appropriate actions to remake the file.  */

  if (!file->is_target && !file->cmds)
    fatal ("no specification for making file \"%s\"", file->name);

  files_remade++;

  /* If file depends on .RECURSIVE, ignore -t and -q for this file.  */

  if (question_flag && !file->recursive)
    return 1;			/* Just answer "not up to date".  */

  if (file->cmds == 0)
    {
      /* The only effect of this if statement
	 is that files with no commands (not even implicit ones)
	 do not get touched with -t.  */
      status = 0;
    }
  else if (touch_flag && !file->recursive)
    {
      /* Should set file's modification date and do nothing else.  */
      if (!silent_flag)
	{
	  printf ("touch(%s)\n", file->name);
	  fflush (stdout);
	}

      if (index (file->name, '(')
	  && file->name[strlen (file->name) - 1] == ')')
	{
	  status = ar_touch (file->name);
	}
      else
	{
	  fd = open (file->name, O_RDWR + O_CREAT, 0666);
	  status = fd < 0;
	  if (status)
	    perror_with_name ("touch: ", file->name);
	  else
	    {
	      timevals[0].tv_sec = timevals[1].tv_sec = time (0);
	      timevals[0].tv_usec = timevals[1].tv_usec = 0;
	      if (utimes (file->name, timevals) < 0)
		{
		  /* utimes fails if we don't own the file.
		     in that case, must really do i/o.  */
		  char buf;
		  read (fd, &buf, 1);
		  lseek (fd, 0, 0);
		  write (fd, &buf, 1);
		}
	      close (fd);
	    }
	}
      /* We may have caused some other file to need updating */
      tick++;
    }
  else
    status = execute_file_commands (file);

  file->update_status = status;
  file->last_mtime = just_print_flag ? -1 : name_mtime (file->name);
  if (file->last_mtime < 0)
    file->last_mtime = time (0);

  return status;
}

/* Returns nonzero if some command got a nonignored error.
   Returns zero if have successfully executed the commands.  */

int
execute_file_commands (file)
     struct file *file;
{
  register char *line;
  register struct dep *d;
  register struct cmd *cmd;
  int noprint;
  int noerror;
  int status = 0;

  /* 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 : "";

  {
    register int dep_size = 0;

    for (d = file->deps; d; d = d->next)
      if (d->changed)
	dep_size += strlen (d->name) + 1;

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

    if (dep_size > 0)
      {
	for (d = file->deps; d; d = d->next)
	  if (d->changed)
	    {
	      strcat (line, d->name);
	      strcat (line, " ");
	    }
	/* remove final space */
	line[dep_size - 1] = 0;
      }
    qmark_macro->value = line;
  }

  /* Arrange for signal to delete this target,
     unless it depends on .PRECIOUS:  */

  if (!file->precious)
    {
      signal_delete_file = file->name;
      signal_delete_mtime = file->last_mtime;
    }

  /* 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);
	  fflush (stdout);
	}

      /* If -n was specified, don't really execute, unless file
	 is depended on by .RECURSIVE .
	 But do pretend we changed this files date.  */
      if (just_print_flag && !file->recursive)
	continue;

      status = run_shell_command (line);
      /* We may have caused some other file to need updating */
      tick++;

      if (status && !ignore_errors_flag && !noerror)
	{
	  fprintf (stderr, "*** Error %o\n", status);
	  fflush (stderr);
	  break;
	}
    }

  /* Now free the macro values made above */
  free (qmark_macro->value);
  qmark_macro->value = "";
  signal_delete_file = 0;
  signal_delete_mtime = 0;

  return status;
}

/* 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, depth)
     struct file *file;
     int depth;
{
  register struct dep *d;
  register int flen = strlen (file->name);
  register int len;
  struct file *rulefile = 0;
  char *prefix;
  char *depname;
  int dep_mtime = 0;
  char *rulename = (char *) alloca (2 * maxsuffix + 1);

  /* 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;

  DEBUGPR ("Looking for implicit rule for file \"%s\".\n");

  /* 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)
    {
      strcpy (rulename, d->name);
      strcat (rulename, file->name + flen - len);
      rulefile = lookup_file (rulename);
      if (rulefile == 0)
	continue;

      depname = concat (prefix, d->name, "");
      if (lookup_file (depname))
	break;
      dep_mtime = name_mtime (depname);
      if (dep_mtime >= 0)
	break;

      rulefile = 0;
      free (depname);
    }

  if (!rulefile)
    {
      DEBUGPR ("No implicit rule found for \"%s\".\n");
      free (prefix);
      return 0;
    }

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

  DEBUGPR ("An implicit rule found for \"%s\".\n");
  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;

  /* If the new dependency duplicates an old one, delete the old one.  */
  while (d)
    {
      if (d->next && !strcmp (d->next->name, depname))
	d->next = d->next->next;
      else
	d = d->next;
    }

  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)
	{
	  register int delta = linebuffer->size;
	  end += delta;
	  buffer = (char *) xrealloc (buffer, linebuffer->size += delta);
	  delta = buffer - linebuffer->buffer;
	  p += delta;
	  end += delta;
	  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
ar_scan_1 (name, function)
     char *name;
     int (*function) ();
{
  char *arname;
  char *memname;
  char *p;
  int val;

  /* This "file" is an archive member */
  p = (char *) index (name, '(');
  arname = savestring (name, p - name);
  memname = savestring (p + 1, strlen (p) - 2);

  val = ar_scan (arname, function, memname);
  if (val == -1)
    pfatal_with_name (arname);
  if (val == -2)
    fatal ("Invalid archive file %s", arname);
  free (arname);
  free (memname);
  return val;
}

int
ar_member_date_1 (desc, name, hdrpos, datapos, size, date, uid, gid, mode, mem)
     int desc;
     char *name;
     int hdrpos, datapos, size, date, uid, gid, mode;
     char *mem;
{
  if (!strcmp (name, mem))
    return date;
  else
    return 0;
}

int
ar_member_date (name)
     char *name;
{
  return ar_scan_1 (name, ar_member_date_1);
}

int
ar_touch (name)
     char *name;
{
  register char *p, *arname, *memname;
  register int val;

  p = (char *) index (name, '(');
  arname = savestring (name, p - name);
  memname = savestring (p + 1, strlen (p) - 2);

  val = ar_member_touch (arname, memname);
  if (val == -2)
    fatal ("Invalid archive %s", arname);
  if (val < 0)
    pfatal_with_name (arname);
  if (val > 0)
    error ("touch: No such archive member %s", name);

  free (arname);
  free (memname);

  return val;
}

/* Execute LINE as a command for an inferior shell.
   Wait for completion.
   Return the status that the shell returns.  */

int
run_shell_command (line)
     char *line;
{
  int pid;
  union wait status;
  char *new_argv[4];

  new_argv[0] = shell_macro->value;
  new_argv[1] = "-c";
  new_argv[2] = line;
  new_argv[3] = 0;

  pid = vfork();
  if (!pid)
    {
      execvp (new_argv[0], new_argv);
      _exit (127);
    }

  while (1)
    {
      wait (&status);
      if (WIFEXITED (status))
	{
	  return status.w_retcode;
	}
      else if (WIFSIGNALED (status))
	{
	  /* If subshell got a fatal signal,
	     give us (in effect) the same one now.
	     if we did't get it already.  */
	  fatal_error_signal (status.w_termsig);
	}
    }
}

/* Print N spaces.  */

void
print_spaces (n)
     register int n;
{
  while (n-- > 0)
    putchar (' ');
}

/* Return the mtime of a file, given a struct file.
   Caches the time in the struct file to avoid excess stat calls.
   Records current tick also, so we can tell when an old mtime
   cannot be trusted (since commands have been executed since).  */

int
file_mtime (file)
     register struct file *file;
{
  register int mtime;

  if (file->last_mtime != 0)
    return file->last_mtime;

  /* File's mtime is not known;
     must get it now from system.  */

  mtime = name_mtime (file->name);

  /* Store the mtime into all the entries for this file.  */

  while (file)
    {
      file->last_mtime = mtime;
      file = file->prev;
    }
  return mtime;
}

int
name_mtime (name)
     register char *name;
{
  struct stat st;

  if (index (name, '(') && name[strlen (name) - 1] == ')')
    return ar_member_date (name);

  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;
{
  register int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  register 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.  */

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

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

void
error (s1, s2)
     char *s1, *s2;
{
  fprintf (stderr, "make: ");
  fprintf (stderr, s1, s2);
  fprintf (stderr, "\n");
  fflush (stderr);
}

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

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

void
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.  */
extern char *malloc (), *realloc ();

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


char *
xrealloc (ptr, size)
     char *ptr;
     int size;
{
  char *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;
}
