#include "make.h"
#include <assert.h>
#include <glob.h>
#include "dep.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "rule.h"
#include "debug.h"
#ifndef WINDOWS32
#ifndef _AMIGA
#ifndef VMS
#include <pwd.h>
#else
struct passwd *getpwnam PARAMS ((char *name));
#endif
#endif
#endif
struct linebuffer
{
unsigned int size;
char *buffer;
};
#define initbuffer(lb) (lb)->buffer = (char *) xmalloc ((lb)->size = 200)
#define freebuffer(lb) free ((lb)->buffer)
enum make_word_type
{
w_bogus, w_eol, w_static, w_variable, w_colon, w_dcolon, w_semicolon,
w_comment, w_varassign
};
struct conditionals
{
unsigned int if_cmds;
unsigned int allocated;
char *ignoring;
char *seen_else;
};
static struct conditionals toplevel_conditionals;
static struct conditionals *conditionals = &toplevel_conditionals;
static char *default_include_directories[] =
{
#if defined(WINDOWS32) && !defined(INCLUDEDIR)
#define INCLUDEDIR "."
#endif
INCLUDEDIR,
#ifndef _AMIGA
"/usr/gnu/include",
"/usr/local/include",
"/usr/include",
#endif
0
};
static char **include_directories;
static unsigned int max_incl_len;
const struct floc *reading_file;
static struct dep *read_makefiles = 0;
static int read_makefile PARAMS ((char *filename, int flags));
static unsigned long readline PARAMS ((struct linebuffer *linebuffer,
FILE *stream, const struct floc *flocp));
static void do_define PARAMS ((char *name, unsigned int namelen,
enum variable_origin origin, FILE *infile,
struct floc *flocp));
static int conditional_line PARAMS ((char *line, const struct floc *flocp));
static void record_files PARAMS ((struct nameseq *filenames, char *pattern, char *pattern_percent,
struct dep *deps, unsigned int cmds_started, char *commands,
unsigned int commands_idx, int two_colon,
const struct floc *flocp, int set_default));
static void record_target_var PARAMS ((struct nameseq *filenames, char *defn,
int two_colon,
enum variable_origin origin,
const struct floc *flocp));
static enum make_word_type get_next_mword PARAMS ((char *buffer, char *delim,
char **startp, unsigned int *length));
struct dep *
read_all_makefiles (makefiles)
char **makefiles;
{
unsigned int num_makefiles = 0;
DB (DB_BASIC, (_("Reading makefiles...\n")));
{
char *value;
char *name, *p;
unsigned int length;
{
int save = warn_undefined_variables_flag;
warn_undefined_variables_flag = 0;
value = allocated_variable_expand ("$(MAKEFILES)");
warn_undefined_variables_flag = save;
}
p = value;
while ((name = find_next_token (&p, &length)) != 0)
{
if (*p != '\0')
*p++ = '\0';
name = xstrdup (name);
if (read_makefile (name,
RM_NO_DEFAULT_GOAL|RM_INCLUDED|RM_DONTCARE) < 2)
free (name);
}
free (value);
}
if (makefiles != 0)
while (*makefiles != 0)
{
struct dep *tail = read_makefiles;
register struct dep *d;
if (! read_makefile (*makefiles, 0))
perror_with_name ("", *makefiles);
d = read_makefiles;
while (d->next != tail)
d = d->next;
*makefiles = dep_name (d);
++num_makefiles;
++makefiles;
}
if (num_makefiles == 0)
{
static char *default_makefiles[] =
#ifdef VMS
{ "makefile.vms", "gnumakefile.", "makefile.", 0 };
#else
#ifdef _AMIGA
{ "GNUmakefile", "Makefile", "SMakefile", 0 };
#else
{ "GNUmakefile", "makefile", "Makefile", 0 };
#endif
#endif
register char **p = default_makefiles;
while (*p != 0 && !file_exists_p (*p))
++p;
if (*p != 0)
{
if (! read_makefile (*p, 0))
perror_with_name ("", *p);
}
else
{
struct dep *tail = read_makefiles;
while (tail != 0 && tail->next != 0)
tail = tail->next;
for (p = default_makefiles; *p != 0; ++p)
{
struct dep *d = (struct dep *) xmalloc (sizeof (struct dep));
d->name = 0;
d->file = enter_file (*p);
d->file->dontcare = 1;
d->changed = RM_DONTCARE;
if (tail == 0)
read_makefiles = d;
else
tail->next = d;
tail = d;
}
if (tail != 0)
tail->next = 0;
}
}
return read_makefiles;
}
static int
read_makefile (filename, flags)
char *filename;
int flags;
{
static char *collapsed = 0;
static unsigned int collapsed_length = 0;
register FILE *infile;
struct linebuffer lb;
unsigned int commands_len = 200;
char *commands;
unsigned int commands_idx = 0;
unsigned int cmds_started, tgts_started;
char *p;
char *p2;
int len, reading_target;
int ignoring = 0, in_ignored_define = 0;
int no_targets = 0;
int using_filename = 0;
struct floc fileinfo;
char *passed_filename = filename;
struct nameseq *filenames = 0;
struct dep *deps;
unsigned int nlines = 0;
int two_colon = 0;
char *pattern = 0, *pattern_percent;
int makefile_errno;
#if defined (WINDOWS32) || defined (__MSDOS__)
int check_again;
#endif
#define record_waiting_files() \
do \
{ \
if (filenames != 0) \
{ \
struct floc fi; \
fi.filenm = fileinfo.filenm; \
fi.lineno = tgts_started; \
record_files (filenames, pattern, pattern_percent, deps, \
cmds_started, commands, commands_idx, two_colon, \
&fi, !(flags & RM_NO_DEFAULT_GOAL)); \
using_filename |= commands_idx > 0; \
} \
filenames = 0; \
commands_idx = 0; \
if (pattern) { free(pattern); pattern = 0; } \
} while (0)
fileinfo.filenm = filename;
fileinfo.lineno = 1;
pattern_percent = 0;
cmds_started = tgts_started = fileinfo.lineno;
if (ISDB (DB_VERBOSE))
{
printf (_("Reading makefile `%s'"), fileinfo.filenm);
if (flags & RM_NO_DEFAULT_GOAL)
printf (_(" (no default goal)"));
if (flags & RM_INCLUDED)
printf (_(" (search path)"));
if (flags & RM_DONTCARE)
printf (_(" (don't care)"));
if (flags & RM_NO_TILDE)
printf (_(" (no ~ expansion)"));
puts ("...");
}
if (!(flags & RM_NO_TILDE) && filename[0] == '~')
{
char *expanded = tilde_expand (filename);
if (expanded != 0)
filename = expanded;
}
infile = fopen (filename, "r");
makefile_errno = errno;
if (infile == 0 && (flags & RM_INCLUDED) && *filename != '/')
{
register unsigned int i;
for (i = 0; include_directories[i] != 0; ++i)
{
char *name = concat (include_directories[i], "/", filename);
infile = fopen (name, "r");
if (infile == 0)
free (name);
else
{
filename = name;
break;
}
}
}
deps = (struct dep *) xmalloc (sizeof (struct dep));
deps->next = read_makefiles;
read_makefiles = deps;
deps->name = 0;
deps->file = lookup_file (filename);
if (deps->file == 0)
{
deps->file = enter_file (xstrdup (filename));
if (flags & RM_DONTCARE)
deps->file->dontcare = 1;
}
if (filename != passed_filename)
free (filename);
filename = deps->file->name;
deps->changed = flags;
deps = 0;
if (infile == 0)
{
errno = makefile_errno;
return 0;
}
reading_file = &fileinfo;
initbuffer (&lb);
commands = xmalloc (200);
while (!feof (infile))
{
fileinfo.lineno += nlines;
nlines = readline (&lb, infile, &fileinfo);
if (lb.buffer[0] == '\t')
{
unsigned int len;
if (no_targets)
continue;
if (filenames != 0)
{
if (ignoring)
continue;
p = lb.buffer;
if (commands_idx == 0)
cmds_started = fileinfo.lineno;
len = strlen (p);
if (len + 1 + commands_idx > commands_len)
{
commands_len = (len + 1 + commands_idx) * 2;
commands = (char *) xrealloc (commands, commands_len);
}
bcopy (p, &commands[commands_idx], len);
commands_idx += len;
commands[commands_idx++] = '\n';
continue;
}
}
if (collapsed_length < lb.size)
{
collapsed_length = lb.size;
if (collapsed != 0)
free (collapsed);
collapsed = (char *) xmalloc (collapsed_length);
}
strcpy (collapsed, lb.buffer);
collapse_continuations (collapsed);
remove_comments (collapsed);
#define word1eq(s, l) (len == l && strneq (s, p, l))
p = collapsed;
while (isspace ((unsigned char)*p))
++p;
if (*p == '\0')
continue;
for (p2 = p+1; *p2 != '\0' && !isspace ((unsigned char)*p2); ++p2)
{}
len = p2 - p;
reading_target = 0;
while (isspace ((unsigned char)*p2))
++p2;
if (*p2 == '\0')
p2 = NULL;
else if (p2[0] == ':' && p2[1] == '\0')
{
reading_target = 1;
goto skip_conditionals;
}
if (!in_ignored_define
&& (word1eq ("ifdef", 5) || word1eq ("ifndef", 6)
|| word1eq ("ifeq", 4) || word1eq ("ifneq", 5)
|| word1eq ("else", 4) || word1eq ("endif", 5)))
{
int i = conditional_line (p, &fileinfo);
if (i >= 0)
ignoring = i;
else
fatal (&fileinfo, _("invalid syntax in conditional"));
continue;
}
if (word1eq ("endef", 5))
{
if (in_ignored_define)
in_ignored_define = 0;
else
fatal (&fileinfo, _("extraneous `endef'"));
continue;
}
if (word1eq ("define", 6))
{
if (ignoring)
in_ignored_define = 1;
else
{
p2 = next_token (p + 6);
if (*p2 == '\0')
fatal (&fileinfo, _("empty variable name"));
p = strchr (p2, '\0');
while (isblank (p[-1]))
--p;
do_define (p2, p - p2, o_file, infile, &fileinfo);
}
continue;
}
if (word1eq ("override", 8))
{
p2 = next_token (p + 8);
if (*p2 == '\0')
error (&fileinfo, _("empty `override' directive"));
if (strneq (p2, "define", 6) && (isblank (p2[6]) || p2[6] == '\0'))
{
if (ignoring)
in_ignored_define = 1;
else
{
p2 = next_token (p2 + 6);
if (*p2 == '\0')
fatal (&fileinfo, _("empty variable name"));
p = strchr (p2, '\0');
while (isblank (p[-1]))
--p;
do_define (p2, p - p2, o_override, infile, &fileinfo);
}
}
else if (!ignoring
&& !try_variable_definition (&fileinfo, p2, o_override, 0))
error (&fileinfo, _("invalid `override' directive"));
continue;
}
skip_conditionals:
if (ignoring)
continue;
if (!reading_target && word1eq ("export", 6))
{
struct variable *v;
p2 = next_token (p + 6);
if (*p2 == '\0')
export_all_variables = 1;
v = try_variable_definition (&fileinfo, p2, o_file, 0);
if (v != 0)
v->export = v_export;
else
{
unsigned int len;
for (p = find_next_token (&p2, &len); p != 0;
p = find_next_token (&p2, &len))
{
v = lookup_variable (p, len);
if (v == 0)
v = define_variable_loc (p, len, "", o_file, 0, &fileinfo);
v->export = v_export;
}
}
}
else if (!reading_target && word1eq ("unexport", 8))
{
unsigned int len;
struct variable *v;
p2 = next_token (p + 8);
if (*p2 == '\0')
export_all_variables = 0;
for (p = find_next_token (&p2, &len); p != 0;
p = find_next_token (&p2, &len))
{
v = lookup_variable (p, len);
if (v == 0)
v = define_variable_loc (p, len, "", o_file, 0, &fileinfo);
v->export = v_noexport;
}
}
else if (word1eq ("vpath", 5))
{
char *pattern;
unsigned int len;
p2 = variable_expand (p + 5);
p = find_next_token (&p2, &len);
if (p != 0)
{
pattern = savestring (p, len);
p = find_next_token (&p2, &len);
}
else
pattern = 0;
construct_vpath_list (pattern, p);
if (pattern != 0)
free (pattern);
}
else if (word1eq ("include", 7) || word1eq ("-include", 8)
|| word1eq ("sinclude", 8))
{
struct conditionals *save, new_conditionals;
struct nameseq *files;
int noerror = p[0] != 'i';
p = allocated_variable_expand (next_token (p + (noerror ? 8 : 7)));
if (*p == '\0')
{
error (&fileinfo,
_("no file name for `%sinclude'"), noerror ? "-" : "");
continue;
}
p2 = p;
files = multi_glob (parse_file_seq (&p2, '\0',
sizeof (struct nameseq),
1),
sizeof (struct nameseq));
free (p);
save = conditionals;
bzero ((char *) &new_conditionals, sizeof new_conditionals);
conditionals = &new_conditionals;
record_waiting_files ();
while (files != 0)
{
struct nameseq *next = files->next;
char *name = files->name;
int r;
free ((char *)files);
files = next;
r = read_makefile (name, (RM_INCLUDED | RM_NO_TILDE
| (noerror ? RM_DONTCARE : 0)));
if (!r && !noerror)
error (&fileinfo, "%s: %s", name, strerror (errno));
if (r < 2)
free (name);
}
if (conditionals->ignoring)
free (conditionals->ignoring);
if (conditionals->seen_else)
free (conditionals->seen_else);
conditionals = save;
reading_file = &fileinfo;
}
#undef word1eq
else if (try_variable_definition (&fileinfo, p, o_file, 0))
;
else if (lb.buffer[0] == '\t')
{
p = collapsed;
while (isblank (*p))
++p;
if (*p == '\0')
continue;
fatal(&fileinfo, _("commands commence before first target"));
}
else
{
enum make_word_type wtype;
enum variable_origin v_origin;
char *cmdleft, *lb_next;
unsigned int len, plen = 0;
char *colonp;
record_waiting_files ();
tgts_started = fileinfo.lineno;
cmdleft = find_char_unquote (lb.buffer, ";#", 0);
if (cmdleft != 0 && *cmdleft == '#')
{
*cmdleft = '\0';
cmdleft = 0;
}
else if (cmdleft != 0)
*(cmdleft++) = '\0';
collapse_continuations (lb.buffer);
wtype = get_next_mword(lb.buffer, NULL, &lb_next, &len);
switch (wtype)
{
case w_eol:
if (cmdleft != 0)
fatal(&fileinfo, _("missing rule before commands"));
continue;
case w_colon:
case w_dcolon:
no_targets = 1;
continue;
default:
break;
}
p2 = variable_expand_string(NULL, lb_next, len);
while (1)
{
lb_next += len;
if (cmdleft == 0)
{
cmdleft = find_char_unquote (p2, ";", 0);
if (cmdleft != 0)
{
unsigned long p2_off = p2 - variable_buffer;
unsigned long cmd_off = cmdleft - variable_buffer;
char *pend = p2 + strlen(p2);
*cmdleft = '\0';
(void)variable_expand_string(pend, lb_next, (long)-1);
lb_next += strlen(lb_next);
p2 = variable_buffer + p2_off;
cmdleft = variable_buffer + cmd_off + 1;
}
}
colonp = find_char_unquote(p2, ":", 0);
#if defined(__MSDOS__) || defined(WINDOWS32)
while (colonp && (colonp[1] == '/' || colonp[1] == '\\') &&
colonp > p2 && isalpha ((unsigned char)colonp[-1]) &&
(colonp == p2 + 1 || strchr (" \t(", colonp[-2]) != 0))
colonp = find_char_unquote(colonp + 1, ":", 0);
#endif
if (colonp != 0)
break;
wtype = get_next_mword(lb_next, NULL, &lb_next, &len);
if (wtype == w_eol)
break;
p2 += strlen(p2);
*(p2++) = ' ';
p2 = variable_expand_string(p2, lb_next, len);
}
p2 = next_token (variable_buffer);
if (wtype == w_eol)
{
if (*p2 != '\0')
fatal (&fileinfo, _("missing separator%s"),
!strneq(lb.buffer, " ", 8) ? ""
: _(" (did you mean TAB instead of 8 spaces?)"));
continue;
}
*colonp = '\0';
filenames = multi_glob (parse_file_seq (&p2, '\0',
sizeof (struct nameseq),
1),
sizeof (struct nameseq));
*p2 = ':';
if (!filenames)
{
no_targets = 1;
continue;
}
assert (*p2 != '\0');
++p2;
two_colon = *p2 == ':';
if (two_colon)
p2++;
if (*lb_next != '\0')
{
unsigned int l = p2 - variable_buffer;
plen = strlen (p2);
(void) variable_buffer_output (p2+plen,
lb_next, strlen (lb_next)+1);
p2 = variable_buffer + l;
}
wtype = get_next_mword (p2, NULL, &p, &len);
v_origin = o_file;
if (wtype == w_static && (len == (sizeof ("override")-1)
&& strneq (p, "override", len)))
{
v_origin = o_override;
wtype = get_next_mword (p+len, NULL, &p, &len);
}
if (wtype != w_eol)
wtype = get_next_mword (p+len, NULL, NULL, NULL);
if (wtype == w_varassign)
{
record_target_var (filenames, p, two_colon, v_origin, &fileinfo);
filenames = 0;
continue;
}
find_char_unquote (lb_next, "=", 0);
no_targets = 0;
if (*lb_next != '\0')
{
unsigned int l = p2 - variable_buffer;
(void) variable_expand_string (p2 + plen, lb_next, (long)-1);
p2 = variable_buffer + l;
if (cmdleft == 0)
{
cmdleft = find_char_unquote (p2, ";", 0);
if (cmdleft != 0)
*(cmdleft++) = '\0';
}
}
p = strchr (p2, ':');
while (p != 0 && p[-1] == '\\')
{
register char *q = &p[-1];
register int backslash = 0;
while (*q-- == '\\')
backslash = !backslash;
if (backslash)
p = strchr (p + 1, ':');
else
break;
}
#ifdef _AMIGA
if (p && !(isspace ((unsigned char)p[1]) || !p[1]
|| isspace ((unsigned char)p[-1])))
p = 0;
#endif
#if defined (WINDOWS32) || defined (__MSDOS__)
do {
check_again = 0;
if (p != 0 && (p[1] == '\\' || p[1] == '/') &&
isalpha ((unsigned char)p[-1]) &&
(p == p2 + 1 || strchr (" \t:(", p[-2]) != 0)) {
p = strchr (p + 1, ':');
check_again = 1;
}
} while (check_again);
#endif
if (p != 0)
{
struct nameseq *target;
target = parse_file_seq (&p2, ':', sizeof (struct nameseq), 1);
++p2;
if (target == 0)
fatal (&fileinfo, _("missing target pattern"));
else if (target->next != 0)
fatal (&fileinfo, _("multiple target patterns"));
pattern = target->name;
pattern_percent = find_percent (pattern);
if (pattern_percent == 0)
fatal (&fileinfo, _("target pattern contains no `%%'"));
free((char *)target);
}
else
pattern = 0;
deps = (struct dep *)
multi_glob (parse_file_seq (&p2, '\0', sizeof (struct dep), 1),
sizeof (struct dep));
commands_idx = 0;
if (cmdleft != 0)
{
unsigned int len = strlen (cmdleft);
cmds_started = fileinfo.lineno;
if (len + 2 > commands_len)
{
commands_len = (len + 2) * 2;
commands = (char *) xrealloc (commands, commands_len);
}
bcopy (cmdleft, commands, len);
commands_idx += len;
commands[commands_idx++] = '\n';
}
continue;
}
record_waiting_files ();
no_targets = 0;
}
if (conditionals->if_cmds)
fatal (&fileinfo, _("missing `endif'"));
record_waiting_files ();
freebuffer (&lb);
free ((char *) commands);
fclose (infile);
reading_file = 0;
return 1+using_filename;
}
static void
do_define (name, namelen, origin, infile, flocp)
char *name;
unsigned int namelen;
enum variable_origin origin;
FILE *infile;
struct floc *flocp;
{
struct linebuffer lb;
unsigned int nlines = 0;
unsigned int length = 100;
char *definition = (char *) xmalloc (100);
register unsigned int idx = 0;
register char *p;
char *var = (char *) alloca (namelen + 1);
bcopy (name, var, namelen);
var[namelen] = '\0';
var = variable_expand (var);
initbuffer (&lb);
while (!feof (infile))
{
unsigned int len;
flocp->lineno += nlines;
nlines = readline (&lb, infile, flocp);
collapse_continuations (lb.buffer);
p = next_token (lb.buffer);
len = strlen (p);
if ((len == 5 || (len > 5 && isblank (p[5])))
&& strneq (p, "endef", 5))
{
p += 5;
remove_comments (p);
if (*next_token (p) != '\0')
error (flocp, _("Extraneous text after `endef' directive"));
if (idx == 0)
definition[0] = '\0';
else
definition[idx - 1] = '\0';
(void) define_variable_loc (var, strlen (var), definition, origin,
1, flocp);
free (definition);
freebuffer (&lb);
return;
}
else
{
len = strlen (lb.buffer);
if (idx + len + 1 > length)
{
length = (idx + len) * 2;
definition = (char *) xrealloc (definition, length + 1);
}
bcopy (lb.buffer, &definition[idx], len);
idx += len;
definition[idx++] = '\n';
}
}
fatal (flocp, _("missing `endef', unterminated `define'"));
return;
}
static int
conditional_line (line, flocp)
char *line;
const struct floc *flocp;
{
int notdef;
char *cmdname;
register unsigned int i;
if (*line == 'i')
{
notdef = line[2] == 'n';
if (notdef)
{
cmdname = line[3] == 'd' ? "ifndef" : "ifneq";
line += cmdname[3] == 'd' ? 7 : 6;
}
else
{
cmdname = line[2] == 'd' ? "ifdef" : "ifeq";
line += cmdname[2] == 'd' ? 6 : 5;
}
}
else
{
notdef = line[1] == 'n';
cmdname = notdef ? "endif" : "else";
line += notdef ? 5 : 4;
}
line = next_token (line);
if (*cmdname == 'e')
{
if (*line != '\0')
error (flocp, _("Extraneous text after `%s' directive"), cmdname);
if (conditionals->if_cmds == 0)
fatal (flocp, _("extraneous `%s'"), cmdname);
if (notdef)
--conditionals->if_cmds;
else if (conditionals->seen_else[conditionals->if_cmds - 1])
fatal (flocp, _("only one `else' per conditional"));
else
{
conditionals->ignoring[conditionals->if_cmds - 1]
= !conditionals->ignoring[conditionals->if_cmds - 1];
conditionals->seen_else[conditionals->if_cmds - 1] = 1;
}
for (i = 0; i < conditionals->if_cmds; ++i)
if (conditionals->ignoring[i])
return 1;
return 0;
}
if (conditionals->allocated == 0)
{
conditionals->allocated = 5;
conditionals->ignoring = (char *) xmalloc (conditionals->allocated);
conditionals->seen_else = (char *) xmalloc (conditionals->allocated);
}
++conditionals->if_cmds;
if (conditionals->if_cmds > conditionals->allocated)
{
conditionals->allocated += 5;
conditionals->ignoring = (char *)
xrealloc (conditionals->ignoring, conditionals->allocated);
conditionals->seen_else = (char *)
xrealloc (conditionals->seen_else, conditionals->allocated);
}
conditionals->seen_else[conditionals->if_cmds - 1] = 0;
for (i = 0; i < conditionals->if_cmds - 1; ++i)
if (conditionals->ignoring[i])
{
conditionals->ignoring[conditionals->if_cmds - 1] = 1;
return 1;
}
if (cmdname[notdef ? 3 : 2] == 'd')
{
struct variable *v;
register char *p = end_of_token (line);
i = p - line;
p = next_token (p);
if (*p != '\0')
return -1;
v = lookup_variable (line, i);
conditionals->ignoring[conditionals->if_cmds - 1]
= (v != 0 && *v->value != '\0') == notdef;
}
else
{
char *s1, *s2;
unsigned int len;
char termin = *line == '(' ? ',' : *line;
if (termin != ',' && termin != '"' && termin != '\'')
return -1;
s1 = ++line;
if (termin == ',')
{
register int count = 0;
for (; *line != '\0'; ++line)
if (*line == '(')
++count;
else if (*line == ')')
--count;
else if (*line == ',' && count <= 0)
break;
}
else
while (*line != '\0' && *line != termin)
++line;
if (*line == '\0')
return -1;
if (termin == ',')
{
char *p = line++;
while (isblank (p[-1]))
--p;
*p = '\0';
}
else
*line++ = '\0';
s2 = variable_expand (s1);
len = strlen (s2);
s1 = (char *) alloca (len + 1);
bcopy (s2, s1, len + 1);
if (termin != ',')
line = next_token (line);
termin = termin == ',' ? ')' : *line;
if (termin != ')' && termin != '"' && termin != '\'')
return -1;
if (termin == ')')
{
register int count = 0;
s2 = next_token (line);
for (line = s2; *line != '\0'; ++line)
{
if (*line == '(')
++count;
else if (*line == ')')
{
if (count <= 0)
break;
else
--count;
}
}
}
else
{
++line;
s2 = line;
while (*line != '\0' && *line != termin)
++line;
}
if (*line == '\0')
return -1;
*line = '\0';
line = next_token (++line);
if (*line != '\0')
error (flocp, _("Extraneous text after `%s' directive"), cmdname);
s2 = variable_expand (s2);
conditionals->ignoring[conditionals->if_cmds - 1]
= streq (s1, s2) == notdef;
}
for (i = 0; i < conditionals->if_cmds; ++i)
if (conditionals->ignoring[i])
return 1;
return 0;
}
void
uniquize_deps (chain)
struct dep *chain;
{
register struct dep *d;
for (d = chain; d != 0; d = d->next)
{
struct dep *last, *next;
last = d;
next = d->next;
while (next != 0)
if (streq (dep_name (d), dep_name (next)))
{
struct dep *n = next->next;
last->next = n;
if (next->name != 0 && next->name != d->name)
free (next->name);
if (next != d)
free ((char *) next);
next = n;
}
else
{
last = next;
next = next->next;
}
}
}
static void
record_target_var (filenames, defn, two_colon, origin, flocp)
struct nameseq *filenames;
char *defn;
int two_colon;
enum variable_origin origin;
const struct floc *flocp;
{
struct nameseq *nextf;
struct variable_set_list *global;
global = current_variable_set_list;
for (; filenames != 0; filenames = nextf)
{
struct variable *v;
register char *name = filenames->name;
struct variable_set_list *vlist;
char *fname;
char *percent;
nextf = filenames->next;
free ((char *) filenames);
percent = find_percent (name);
if (percent)
{
struct pattern_var *p;
p = create_pattern_var(name, percent);
vlist = p->vars;
fname = p->target;
}
else
{
struct file *f;
f = enter_file (name);
initialize_file_variables (f, 1);
vlist = f->variables;
fname = f->name;
}
current_variable_set_list = vlist;
v = try_variable_definition (flocp, defn, origin, 1);
if (!v)
error (flocp, _("Malformed per-target variable definition"));
v->per_target = 1;
if (origin != o_override)
{
struct variable *gv;
int len = strlen(v->name);
current_variable_set_list = global;
gv = lookup_variable (v->name, len);
if (gv && (gv->origin == o_env_override || gv->origin == o_command))
define_variable_in_set (v->name, len, gv->value, gv->origin,
gv->recursive, vlist->set, flocp);
}
if (name != fname && (name < fname || name > fname + strlen (fname)))
free (name);
}
current_variable_set_list = global;
}
static void
record_files (filenames, pattern, pattern_percent, deps, cmds_started,
commands, commands_idx, two_colon, flocp, set_default)
struct nameseq *filenames;
char *pattern, *pattern_percent;
struct dep *deps;
unsigned int cmds_started;
char *commands;
unsigned int commands_idx;
int two_colon;
const struct floc *flocp;
int set_default;
{
struct nameseq *nextf;
int implicit = 0;
unsigned int max_targets = 0, target_idx = 0;
char **targets = 0, **target_percents = 0;
struct commands *cmds;
if (commands_idx > 0)
{
cmds = (struct commands *) xmalloc (sizeof (struct commands));
cmds->fileinfo.filenm = flocp->filenm;
cmds->fileinfo.lineno = cmds_started;
cmds->commands = savestring (commands, commands_idx);
cmds->command_lines = 0;
}
else
cmds = 0;
for (; filenames != 0; filenames = nextf)
{
register char *name = filenames->name;
register struct file *f;
register struct dep *d;
struct dep *this;
char *implicit_percent;
nextf = filenames->next;
free (filenames);
implicit_percent = find_percent (name);
implicit |= implicit_percent != 0;
if (implicit && pattern != 0)
fatal (flocp, _("mixed implicit and static pattern rules"));
if (implicit && implicit_percent == 0)
fatal (flocp, _("mixed implicit and normal rules"));
if (implicit)
{
if (targets == 0)
{
max_targets = 5;
targets = (char **) xmalloc (5 * sizeof (char *));
target_percents = (char **) xmalloc (5 * sizeof (char *));
target_idx = 0;
}
else if (target_idx == max_targets - 1)
{
max_targets += 5;
targets = (char **) xrealloc ((char *) targets,
max_targets * sizeof (char *));
target_percents
= (char **) xrealloc ((char *) target_percents,
max_targets * sizeof (char *));
}
targets[target_idx] = name;
target_percents[target_idx] = implicit_percent;
++target_idx;
continue;
}
this = nextf != 0 ? copy_dep_chain (deps) : deps;
if (pattern != 0)
{
if (!pattern_matches (pattern, pattern_percent, name))
{
error (flocp,
_("target `%s' doesn't match the target pattern"), name);
this = 0;
}
else
{
char *buffer = variable_expand ("");
for (d = this; d != 0; d = d->next)
{
char *o;
char *percent = find_percent (d->name);
if (percent == 0)
continue;
o = patsubst_expand (buffer, name, pattern, d->name,
pattern_percent, percent);
if (o == buffer)
fatal (flocp,
_("target `%s' leaves prerequisite pattern empty"),
name);
free (d->name);
d->name = savestring (buffer, o - buffer);
}
}
}
if (!two_colon)
{
f = enter_file (name);
if (f->double_colon)
fatal (flocp,
_("target file `%s' has both : and :: entries"), f->name);
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
if (!(next_flag & NEXT_QUIET_FLAG))
#endif
if (cmds != 0 && cmds == f->cmds)
error (flocp,
_("target `%s' given more than once in the same rule."),
f->name);
else if (cmds != 0 && f->cmds != 0 && f->is_target)
{
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
if (!(next_flag & NEXT_QUIET_FLAG)) {
#endif
error (&cmds->fileinfo,
_("warning: overriding commands for target `%s'"),
f->name);
error (&f->cmds->fileinfo,
_("warning: ignoring old commands for target `%s'"),
f->name);
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
}
#endif
}
f->is_target = 1;
if (f == default_file && this == 0 && cmds == 0)
f->cmds = 0;
if (cmds != 0)
f->cmds = cmds;
if (f == suffix_file && this == 0)
{
d = f->deps;
while (d != 0)
{
struct dep *nextd = d->next;
free (d->name);
free ((char *)d);
d = nextd;
}
f->deps = 0;
}
else if (f->deps != 0)
{
struct dep *firstdeps, *moredeps;
if (cmds != 0)
{
firstdeps = this;
moredeps = f->deps;
}
else
{
firstdeps = f->deps;
moredeps = this;
}
if (firstdeps == 0)
firstdeps = moredeps;
else
{
d = firstdeps;
while (d->next != 0)
d = d->next;
d->next = moredeps;
}
f->deps = firstdeps;
}
else
f->deps = this;
if (pattern != 0)
{
static char *percent = "%";
char *buffer = variable_expand ("");
char *o = patsubst_expand (buffer, name, pattern, percent,
pattern_percent, percent);
f->stem = savestring (buffer, o - buffer);
}
}
else
{
f = lookup_file (name);
if (f != 0 && f->is_target && !f->double_colon)
fatal (flocp,
_("target file `%s' has both : and :: entries"), f->name);
f = enter_file (name);
if (f->double_colon == 0)
f->double_colon = f;
f->is_target = 1;
f->deps = this;
f->cmds = cmds;
}
if (f != 0 && name != f->name
&& (name < f->name || name > f->name + strlen (f->name)))
{
free (name);
name = f->name;
}
if (default_goal_file == 0 && set_default
&& (*name != '.' || strchr (name, '/') != 0
#if defined(__MSDOS__) || defined(WINDOWS32)
|| strchr (name, '\\') != 0
#endif
))
{
int reject = 0;
for (d = suffix_file->deps; d != 0; d = d->next)
{
register struct dep *d2;
if (*dep_name (d) != '.' && streq (name, dep_name (d)))
{
reject = 1;
break;
}
for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next)
{
register unsigned int len = strlen (dep_name (d2));
if (!strneq (name, dep_name (d2), len))
continue;
if (streq (name + len, dep_name (d)))
{
reject = 1;
break;
}
}
if (reject)
break;
}
if (!reject)
default_goal_file = f;
}
}
if (implicit)
{
targets[target_idx] = 0;
target_percents[target_idx] = 0;
create_pattern_rule (targets, target_percents, two_colon, deps, cmds, 1);
free ((char *) target_percents);
}
}
char *
find_char_unquote (string, stopchars, blank)
char *string;
char *stopchars;
int blank;
{
unsigned int string_len = 0;
register char *p = string;
while (1)
{
while (*p != '\0' && strchr (stopchars, *p) == 0
&& (!blank || !isblank (*p)))
++p;
if (*p == '\0')
break;
if (p > string && p[-1] == '\\')
{
register int i = -2;
while (&p[i] >= string && p[i] == '\\')
--i;
++i;
if (string_len == 0)
string_len = strlen (string);
bcopy (&p[i / 2], &p[i], (string_len - (p - string)) - (i / 2) + 1);
p += i / 2;
if (i % 2 == 0)
return p;
}
else
return p;
}
return 0;
}
char *
find_percent (pattern)
char *pattern;
{
return find_char_unquote (pattern, "%", 0);
}
struct nameseq *
parse_file_seq (stringp, stopchar, size, strip)
char **stringp;
int stopchar;
unsigned int size;
int strip;
{
register struct nameseq *new = 0;
register struct nameseq *new1, *lastnew1;
register char *p = *stringp;
char *q;
char *name;
char stopchars[3];
#ifdef VMS
stopchars[0] = ',';
stopchars[1] = stopchar;
stopchars[2] = '\0';
#else
stopchars[0] = stopchar;
stopchars[1] = '\0';
#endif
while (1)
{
p = next_token (p);
if (*p == '\0')
break;
if (*p == stopchar)
break;
q = p;
p = find_char_unquote (q, stopchars, 1);
#ifdef VMS
if (p && *p == ',')
*p =' ';
#endif
#ifdef _AMIGA
if (stopchar == ':' && p && *p == ':'
&& !(isspace ((unsigned char)p[1]) || !p[1]
|| isspace ((unsigned char)p[-1])))
{
p = find_char_unquote (p+1, stopchars, 1);
}
#endif
#if defined(WINDOWS32) || defined(__MSDOS__)
if (stopchar == ':')
while (p != 0 && !isspace ((unsigned char)*p) &&
(p[1] == '\\' || p[1] == '/') && isalpha ((unsigned char)p[-1]))
p = find_char_unquote (p + 1, stopchars, 1);
#endif
if (p == 0)
p = q + strlen (q);
if (strip)
#ifdef VMS
while (p - q > 2 && q[0] == '[' && q[1] == ']')
#else
while (p - q > 2 && q[0] == '.' && q[1] == '/')
#endif
{
q += 2;
while (q < p && *q == '/')
++q;
}
if (q == p)
#ifdef VMS
continue;
#else
#ifdef _AMIGA
name = savestring ("", 0);
#else
name = savestring ("./", 2);
#endif
#endif
else
#ifdef VMS
{
char *qbase = xstrdup (q);
char *pbase = qbase + (p-q);
char *q1 = qbase;
char *q2 = q1;
char *p1 = pbase;
while (q1 != pbase)
{
if (*q1 == '\\' && *(q1+1) == ':')
{
q1++;
p1--;
}
*q2++ = *q1++;
}
name = savestring (qbase, p1 - qbase);
free (qbase);
}
#else
name = savestring (q, p - q);
#endif
new1 = (struct nameseq *) xmalloc (size);
new1->name = name;
new1->next = new;
new = new1;
}
#ifndef NO_ARCHIVES
new1 = new;
lastnew1 = 0;
while (new1 != 0)
if (new1->name[0] != '('
&& new1->name[strlen (new1->name) - 1] == ')'
&& strchr (new1->name, '(') == 0)
{
struct nameseq *n = new1->next, *lastn = new1;
char *paren = 0;
while (n != 0 && (paren = strchr (n->name, '(')) == 0)
{
lastn = n;
n = n->next;
}
if (n != 0
&& n->name[0] != '(')
{
char *libname;
++paren;
libname = (char *) alloca (paren - n->name + 1);
bcopy (n->name, libname, paren - n->name);
libname[paren - n->name] = '\0';
if (*paren == '\0')
{
lastn->next = n->next;
free (n->name);
free ((char *) n);
n = lastn->next;
}
else
{
name = concat (libname, paren, ")");
free (n->name);
n->name = name;
}
if (new1->name[1] == '\0')
{
if (lastnew1 == 0)
new = new1->next;
else
lastnew1->next = new1->next;
lastn = new1;
new1 = new1->next;
free (lastn->name);
free ((char *) lastn);
}
else
{
name = concat (libname, new1->name, "");
free (new1->name);
new1->name = name;
new1 = new1->next;
}
while (new1 != n)
{
name = concat (libname, new1->name, ")");
free (new1->name);
new1->name = name;
lastnew1 = new1;
new1 = new1->next;
}
}
else
{
lastnew1 = new1;
new1 = new1->next;
}
}
else
{
lastnew1 = new1;
new1 = new1->next;
}
#endif
*stringp = p;
return new;
}
static unsigned long
readline (linebuffer, stream, flocp)
struct linebuffer *linebuffer;
FILE *stream;
const struct floc *flocp;
{
char *buffer = linebuffer->buffer;
register char *p = linebuffer->buffer;
register char *end = p + linebuffer->size;
register int len, lastlen = 0;
register char *p2;
register unsigned int nlines = 0;
register int backslash;
*p = '\0';
while (fgets (p, end - p, stream) != 0)
{
len = strlen (p);
if (len == 0)
{
error (flocp, _("warning: NUL character seen; rest of line ignored"));
p[0] = '\n';
len = 1;
}
p += len;
if (p[-1] != '\n')
{
register unsigned int p_off = p - buffer;
linebuffer->size *= 2;
buffer = (char *) xrealloc (buffer, linebuffer->size);
p = buffer + p_off;
end = buffer + linebuffer->size;
linebuffer->buffer = buffer;
*p = '\0';
lastlen = len;
continue;
}
++nlines;
#if !defined(WINDOWS32) && !defined(__MSDOS__)
if (len > 1 && p[-2] == '\r')
{
--len;
--p;
p[-1] = '\n';
}
#endif
if (len == 1 && p > buffer)
len += lastlen;
lastlen = len;
backslash = 0;
for (p2 = p - 2; --len > 0; --p2)
{
if (*p2 == '\\')
backslash = !backslash;
else
break;
}
if (!backslash)
{
p[-1] = '\0';
break;
}
if (end - p <= 1)
{
register unsigned int p_off = p - buffer;
linebuffer->size *= 2;
buffer = (char *) xrealloc (buffer, linebuffer->size);
p = buffer + p_off;
end = buffer + linebuffer->size;
linebuffer->buffer = buffer;
}
}
if (ferror (stream))
pfatal_with_name (flocp->filenm);
return nlines;
}
static enum make_word_type
get_next_mword (buffer, delim, startp, length)
char *buffer;
char *delim;
char **startp;
unsigned int *length;
{
enum make_word_type wtype = w_bogus;
char *p = buffer, *beg;
char c;
while (isblank(*p))
++p;
beg = p;
c = *(p++);
switch (c)
{
case '\0':
wtype = w_eol;
break;
case '#':
wtype = w_comment;
break;
case ';':
wtype = w_semicolon;
break;
case '=':
wtype = w_varassign;
break;
case ':':
wtype = w_colon;
switch (*p)
{
case ':':
++p;
wtype = w_dcolon;
break;
case '=':
++p;
wtype = w_varassign;
break;
}
break;
case '+':
case '?':
if (*p == '=')
{
++p;
wtype = w_varassign;
break;
}
default:
if (delim && strchr (delim, c))
wtype = w_static;
break;
}
if (wtype != w_bogus)
goto done;
wtype = w_static;
while (1)
{
char closeparen;
int count;
switch (c)
{
case '\0':
case ' ':
case '\t':
case '=':
case '#':
goto done_word;
case ':':
#if defined(__MSDOS__) || defined(WINDOWS32)
if (!(p - beg >= 2
&& (*p == '/' || *p == '\\') && isalpha ((unsigned char)p[-2])
&& (p - beg == 2 || p[-3] == '(')))
#endif
goto done_word;
case '$':
c = *(p++);
if (c == '$')
break;
wtype = w_variable;
if (c == '(')
closeparen = ')';
else if (c == '{')
closeparen = '}';
else
break;
for (count=0; *p != '\0'; ++p)
{
if (*p == c)
++count;
else if (*p == closeparen && --count < 0)
{
++p;
break;
}
}
break;
case '?':
case '+':
if (*p == '=')
goto done_word;
break;
case '\\':
switch (*p)
{
case ':':
case ';':
case '=':
case '\\':
++p;
break;
}
break;
default:
if (delim && strchr (delim, c))
goto done_word;
break;
}
c = *(p++);
}
done_word:
--p;
done:
if (startp)
*startp = beg;
if (length)
*length = p - beg;
return wtype;
}
void
construct_include_path (arg_dirs)
char **arg_dirs;
{
register unsigned int i;
#ifdef VAXC
stat_t stbuf;
#else
struct stat stbuf;
#endif
register unsigned int defsize = (sizeof (default_include_directories)
/ sizeof (default_include_directories[0]));
register unsigned int max = 5;
register char **dirs = (char **) xmalloc ((5 + defsize) * sizeof (char *));
register unsigned int idx = 0;
#ifdef __MSDOS__
defsize++;
#endif
if (arg_dirs != 0)
while (*arg_dirs != 0)
{
char *dir = *arg_dirs++;
if (dir[0] == '~')
{
char *expanded = tilde_expand (dir);
if (expanded != 0)
dir = expanded;
}
if (stat (dir, &stbuf) == 0 && S_ISDIR (stbuf.st_mode))
{
if (idx == max - 1)
{
max += 5;
dirs = (char **)
xrealloc ((char *) dirs, (max + defsize) * sizeof (char *));
}
dirs[idx++] = dir;
}
else if (dir != arg_dirs[-1])
free (dir);
}
#ifdef __MSDOS__
{
struct variable *djdir = lookup_variable ("DJDIR", 5);
if (djdir)
{
char *defdir = (char *) xmalloc (strlen (djdir->value) + 8 + 1);
strcat (strcpy (defdir, djdir->value), "/include");
dirs[idx++] = defdir;
}
}
#endif
for (i = 0; default_include_directories[i] != 0; ++i)
if (stat (default_include_directories[i], &stbuf) == 0
&& S_ISDIR (stbuf.st_mode))
dirs[idx++] = default_include_directories[i];
dirs[idx] = 0;
max_incl_len = 0;
for (i = 0; i < idx; ++i)
{
unsigned int len = strlen (dirs[i]);
if (dirs[i][len - 1] == '/')
dirs[i] = savestring (dirs[i], len - 1);
if (len > max_incl_len)
max_incl_len = len;
}
include_directories = dirs;
}
char *
tilde_expand (name)
char *name;
{
#ifndef VMS
if (name[1] == '/' || name[1] == '\0')
{
extern char *getenv ();
char *home_dir;
int is_variable;
{
int save = warn_undefined_variables_flag;
warn_undefined_variables_flag = 0;
home_dir = allocated_variable_expand ("$(HOME)");
warn_undefined_variables_flag = save;
}
is_variable = home_dir[0] != '\0';
if (!is_variable)
{
free (home_dir);
home_dir = getenv ("HOME");
}
#if !defined(_AMIGA) && !defined(WINDOWS32)
if (home_dir == 0 || home_dir[0] == '\0')
{
extern char *getlogin ();
char *logname = getlogin ();
home_dir = 0;
if (logname != 0)
{
struct passwd *p = getpwnam (logname);
if (p != 0)
home_dir = p->pw_dir;
}
}
#endif
if (home_dir != 0)
{
char *new = concat (home_dir, "", name + 1);
if (is_variable)
free (home_dir);
return new;
}
}
#if !defined(_AMIGA) && !defined(WINDOWS32)
else
{
struct passwd *pwent;
char *userend = strchr (name + 1, '/');
if (userend != 0)
*userend = '\0';
pwent = getpwnam (name + 1);
if (pwent != 0)
{
if (userend == 0)
return xstrdup (pwent->pw_dir);
else
return concat (pwent->pw_dir, "/", userend + 1);
}
else if (userend != 0)
*userend = '/';
}
#endif
#endif
return 0;
}
struct nameseq *
multi_glob (chain, size)
struct nameseq *chain;
unsigned int size;
{
extern void dir_setup_glob ();
register struct nameseq *new = 0;
register struct nameseq *old;
struct nameseq *nexto;
glob_t gl;
dir_setup_glob (&gl);
for (old = chain; old != 0; old = nexto)
{
#ifndef NO_ARCHIVES
char *memname;
#endif
nexto = old->next;
if (old->name[0] == '~')
{
char *newname = tilde_expand (old->name);
if (newname != 0)
{
free (old->name);
old->name = newname;
}
}
#ifndef NO_ARCHIVES
if (ar_name (old->name))
{
char *arname;
ar_parse_name (old->name, &arname, &memname);
free (old->name);
old->name = arname;
}
else
memname = 0;
#endif
switch (glob (old->name, GLOB_NOCHECK|GLOB_ALTDIRFUNC, NULL, &gl))
{
case 0:
{
register int i = gl.gl_pathc;
while (i-- > 0)
{
#ifndef NO_ARCHIVES
if (memname != 0)
{
struct nameseq *found
= ar_glob (gl.gl_pathv[i], memname, size);
if (found == 0)
{
struct nameseq *elt
= (struct nameseq *) xmalloc (size);
unsigned int alen = strlen (gl.gl_pathv[i]);
unsigned int mlen = strlen (memname);
elt->name = (char *) xmalloc (alen + 1 + mlen + 2);
bcopy (gl.gl_pathv[i], elt->name, alen);
elt->name[alen] = '(';
bcopy (memname, &elt->name[alen + 1], mlen);
elt->name[alen + 1 + mlen] = ')';
elt->name[alen + 1 + mlen + 1] = '\0';
elt->next = new;
new = elt;
}
else
{
struct nameseq *f = found;
while (f->next != 0)
f = f->next;
f->next = new;
new = found;
}
free (memname);
}
else
#endif
{
struct nameseq *elt = (struct nameseq *) xmalloc (size);
elt->name = xstrdup (gl.gl_pathv[i]);
elt->next = new;
new = elt;
}
}
globfree (&gl);
free (old->name);
free ((char *)old);
break;
}
case GLOB_NOSPACE:
fatal (NILF, _("virtual memory exhausted"));
break;
default:
old->next = new;
new = old;
break;
}
}
return new;
}