#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"
#include "hash.h"
#ifndef WINDOWS32
#ifndef _AMIGA
#ifndef VMS
#include <pwd.h>
#else
struct passwd *getpwnam PARAMS ((char *name));
#endif
#endif
#endif
struct ebuffer
{
char *buffer;
char *bufnext;
char *bufstart;
unsigned int size;
FILE *fp;
struct floc floc;
};
enum make_word_type
{
w_bogus, w_eol, w_static, w_variable, w_colon, w_dcolon, w_semicolon,
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 = 0;
static struct dep *read_makefiles = 0;
static int eval_makefile PARAMS ((char *filename, int flags));
static int eval PARAMS ((struct ebuffer *buffer, int flags));
static long readline PARAMS ((struct ebuffer *ebuf));
static void do_define PARAMS ((char *name, unsigned int namelen,
enum variable_origin origin,
struct ebuffer *ebuf));
static int conditional_line PARAMS ((char *line, int len, 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));
static void record_target_var PARAMS ((struct nameseq *filenames, char *defn,
enum variable_origin origin,
int enabled,
const struct floc *flocp));
static enum make_word_type get_next_mword PARAMS ((char *buffer, char *delim,
char **startp, unsigned int *length));
static void remove_comments PARAMS ((char *line));
static char *find_char_unquote PARAMS ((char *string, int stop1,
int stop2, int blank, int ignorevars));
struct dep *
read_all_makefiles (char **makefiles)
{
unsigned int num_makefiles = 0;
define_variable ("MAKEFILE_LIST", sizeof ("MAKEFILE_LIST")-1, "", o_file, 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';
eval_makefile (name, RM_NO_DEFAULT_GOAL|RM_INCLUDED|RM_DONTCARE);
}
free (value);
}
if (makefiles != 0)
while (*makefiles != 0)
{
struct dep *tail = read_makefiles;
register struct dep *d;
if (! eval_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 (! eval_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 = alloc_dep ();
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 struct conditionals *
install_conditionals (struct conditionals *new)
{
struct conditionals *save = conditionals;
bzero ((char *) new, sizeof (*new));
conditionals = new;
return save;
}
static void
restore_conditionals (struct conditionals *saved)
{
if (conditionals->ignoring)
free (conditionals->ignoring);
if (conditionals->seen_else)
free (conditionals->seen_else);
conditionals = saved;
}
static int
eval_makefile (char *filename, int flags)
{
struct dep *deps;
struct ebuffer ebuf;
const struct floc *curfile;
char *expanded = 0;
char *included = 0;
int makefile_errno;
int r;
ebuf.floc.filenm = strcache_add (filename);
ebuf.floc.lineno = 1;
if (ISDB (DB_VERBOSE))
{
printf (_("Reading makefile `%s'"), filename);
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] == '~')
{
expanded = tilde_expand (filename);
if (expanded != 0)
filename = expanded;
}
ebuf.fp = fopen (filename, "r");
makefile_errno = errno;
if (ebuf.fp == 0 && (flags & RM_INCLUDED) && *filename != '/')
{
register unsigned int i;
for (i = 0; include_directories[i] != 0; ++i)
{
included = concat (include_directories[i], "/", filename);
ebuf.fp = fopen (included, "r");
if (ebuf.fp)
{
filename = included;
break;
}
free (included);
}
if (filename != included)
included = 0;
}
deps = alloc_dep ();
deps->next = read_makefiles;
read_makefiles = deps;
deps->file = lookup_file (filename);
if (deps->file == 0)
deps->file = enter_file (xstrdup (filename));
filename = deps->file->name;
deps->changed = flags;
if (flags & RM_DONTCARE)
deps->file->dontcare = 1;
if (expanded)
free (expanded);
if (included)
free (included);
if (ebuf.fp == 0)
{
errno = makefile_errno;
return 0;
}
do_variable_definition (&ebuf.floc, "MAKEFILE_LIST", filename, o_file,
f_append, 0);
ebuf.size = 200;
ebuf.buffer = ebuf.bufnext = ebuf.bufstart = xmalloc (ebuf.size);
curfile = reading_file;
reading_file = &ebuf.floc;
r = eval (&ebuf, !(flags & RM_NO_DEFAULT_GOAL));
reading_file = curfile;
fclose (ebuf.fp);
free (ebuf.bufstart);
alloca (0);
return r;
}
int
eval_buffer (char *buffer)
{
struct ebuffer ebuf;
struct conditionals *saved;
struct conditionals new;
const struct floc *curfile;
int r;
ebuf.size = strlen (buffer);
ebuf.buffer = ebuf.bufnext = ebuf.bufstart = buffer;
ebuf.fp = NULL;
ebuf.floc = *reading_file;
curfile = reading_file;
reading_file = &ebuf.floc;
saved = install_conditionals (&new);
r = eval (&ebuf, 1);
restore_conditionals (saved);
reading_file = curfile;
alloca (0);
return r;
}
static int
eval (struct ebuffer *ebuf, int set_default)
{
char *collapsed = 0;
unsigned int collapsed_length = 0;
unsigned int commands_len = 200;
char *commands;
unsigned int commands_idx = 0;
unsigned int cmds_started, tgts_started;
int ignoring = 0, in_ignored_define = 0;
int no_targets = 0;
struct nameseq *filenames = 0;
struct dep *deps = 0;
long nlines = 0;
int two_colon = 0;
char *pattern = 0, *pattern_percent;
struct floc *fstart;
struct floc fi;
#define record_waiting_files() \
do \
{ \
if (filenames != 0) \
{ \
fi.lineno = tgts_started; \
record_files (filenames, pattern, pattern_percent, deps, \
cmds_started, commands, commands_idx, two_colon, \
&fi); \
} \
filenames = 0; \
commands_idx = 0; \
no_targets = 0; \
if (pattern) { free(pattern); pattern = 0; } \
} while (0)
pattern_percent = 0;
cmds_started = tgts_started = 1;
fstart = &ebuf->floc;
fi.filenm = ebuf->floc.filenm;
commands = xmalloc (200);
while (1)
{
unsigned int linelen;
char *line;
int len;
char *p;
char *p2;
ebuf->floc.lineno += nlines;
nlines = readline (ebuf);
if (nlines < 0)
break;
line = ebuf->buffer;
if (line[0] == '\0')
continue;
linelen = strlen (line);
if (line[0] == '\t')
{
if (no_targets)
continue;
if (filenames != 0)
{
if (ignoring)
continue;
if (commands_idx == 0)
cmds_started = ebuf->floc.lineno;
if (linelen + 1 + commands_idx > commands_len)
{
commands_len = (linelen + 1 + commands_idx) * 2;
commands = xrealloc (commands, commands_len);
}
bcopy (line, &commands[commands_idx], linelen);
commands_idx += linelen;
commands[commands_idx++] = '\n';
continue;
}
}
if (collapsed_length < linelen+1)
{
collapsed_length = linelen+1;
if (collapsed)
free ((char *)collapsed);
collapsed = (char *) xmalloc (collapsed_length);
}
strcpy (collapsed, line);
collapse_continuations (collapsed);
remove_comments (collapsed);
#define word1eq(s) (len == sizeof(s)-1 && strneq (s, p, sizeof(s)-1))
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;
while (isspace ((unsigned char)*p2))
++p2;
if ((p2[0] == ':' || p2[0] == '+' || p2[0] == '=') && p2[1] == '\0')
{
if (ignoring)
continue;
goto skip_conditionals;
}
if (!in_ignored_define)
{
int i = conditional_line (p, len, fstart);
if (i != -2)
{
if (i == -1)
fatal (fstart, _("invalid syntax in conditional"));
ignoring = i;
continue;
}
}
if (word1eq ("endef"))
{
if (!in_ignored_define)
fatal (fstart, _("extraneous `endef'"));
in_ignored_define = 0;
continue;
}
if (word1eq ("define"))
{
if (ignoring)
in_ignored_define = 1;
else
{
if (*p2 == '\0')
fatal (fstart, _("empty variable name"));
p = strchr (p2, '\0');
while (isblank ((unsigned char)p[-1]))
--p;
do_define (p2, p - p2, o_file, ebuf);
}
continue;
}
if (word1eq ("override"))
{
if (*p2 == '\0')
error (fstart, _("empty `override' directive"));
if (strneq (p2, "define", 6)
&& (isblank ((unsigned char)p2[6]) || p2[6] == '\0'))
{
if (ignoring)
in_ignored_define = 1;
else
{
p2 = next_token (p2 + 6);
if (*p2 == '\0')
fatal (fstart, _("empty variable name"));
p = strchr (p2, '\0');
while (isblank ((unsigned char)p[-1]))
--p;
do_define (p2, p - p2, o_override, ebuf);
}
}
else if (!ignoring
&& !try_variable_definition (fstart, p2, o_override, 0))
error (fstart, _("invalid `override' directive"));
continue;
}
if (ignoring)
continue;
if (word1eq ("export"))
{
if (*p2 == '\0')
export_all_variables = 1;
else
{
struct variable *v;
v = try_variable_definition (fstart, p2, o_file, 0);
if (v != 0)
v->export = v_export;
else
{
unsigned int len;
char *ap;
p2 = ap = allocated_variable_expand (p2);
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,
fstart);
v->export = v_export;
}
free (ap);
}
}
goto rule_complete;
}
if (word1eq ("unexport"))
{
if (*p2 == '\0')
export_all_variables = 0;
else
{
unsigned int len;
struct variable *v;
char *ap;
p2 = ap = allocated_variable_expand (p2);
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, fstart);
v->export = v_noexport;
}
free (ap);
}
goto rule_complete;
}
skip_conditionals:
if (word1eq ("vpath"))
{
char *pattern;
unsigned int len;
p2 = variable_expand (p2);
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);
goto rule_complete;
}
if (word1eq ("include") || word1eq ("-include") || word1eq ("sinclude"))
{
struct conditionals *save;
struct conditionals new_conditionals;
struct nameseq *files;
int noerror = (p[0] != 'i');
p = allocated_variable_expand (p2);
if (*p == '\0')
{
free (p);
continue;
}
p2 = p;
files = multi_glob (parse_file_seq (&p2, '\0',
sizeof (struct nameseq),
1),
sizeof (struct nameseq));
free (p);
save = install_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 = eval_makefile (name, (RM_INCLUDED | RM_NO_TILDE
| (noerror ? RM_DONTCARE : 0)));
if (!r && !noerror)
error (fstart, "%s: %s", name, strerror (errno));
free (name);
}
restore_conditionals (save);
goto rule_complete;
}
if (try_variable_definition (fstart, p, o_file, 0))
goto rule_complete;
if (line[0] == '\t')
fatal(fstart, _("commands commence before first target"));
{
enum make_word_type wtype;
enum variable_origin v_origin;
int exported;
char *cmdleft, *semip, *lb_next;
unsigned int len, plen = 0;
char *colonp;
const char *end, *beg;
record_waiting_files ();
tgts_started = fstart->lineno;
cmdleft = find_char_unquote (line, ';', '#', 0, 1);
if (cmdleft != 0 && *cmdleft == '#')
{
*cmdleft = '\0';
cmdleft = 0;
}
else if (cmdleft != 0)
*(cmdleft++) = '\0';
semip = cmdleft;
collapse_continuations (line);
wtype = get_next_mword(line, NULL, &lb_next, &len);
switch (wtype)
{
case w_eol:
if (cmdleft != 0)
fatal(fstart, _("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, 0, 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, 0, 0);
#ifdef HAVE_DOS_PATHS
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, 0, 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 (fstart, _("missing separator%s"),
!strneq(line, " ", 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;
exported = 0;
if (wtype == w_static)
{
if (word1eq ("override"))
{
v_origin = o_override;
wtype = get_next_mword (p+len, NULL, &p, &len);
}
else if (word1eq ("export"))
{
exported = 1;
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)
{
if (semip)
{
unsigned int l = p - variable_buffer;
*(--semip) = ';';
variable_buffer_output (p2 + strlen (p2),
semip, strlen (semip)+1);
p = variable_buffer + l;
}
record_target_var (filenames, p, v_origin, exported, fstart);
filenames = 0;
continue;
}
find_char_unquote (lb_next, '=', 0, 0, 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, 0, 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
#ifdef HAVE_DOS_PATHS
{
int check_again;
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 (fstart, _("missing target pattern"));
else if (target->next != 0)
fatal (fstart, _("multiple target patterns"));
pattern = target->name;
pattern_percent = find_percent (pattern);
if (pattern_percent == 0)
fatal (fstart, _("target pattern contains no `%%'"));
free ((char *)target);
}
else
pattern = 0;
beg = p2;
end = beg + strlen (beg) - 1;
strip_whitespace (&beg, &end);
if (beg <= end && *beg != '\0')
{
deps = alloc_dep ();
deps->name = savestring (beg, end - beg + 1);
}
else
deps = 0;
commands_idx = 0;
if (cmdleft != 0)
{
unsigned int len = strlen (cmdleft);
cmds_started = fstart->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';
}
if (**default_goal_name == '\0' && set_default)
{
char* name;
struct dep *d;
struct nameseq *t = filenames;
for (; t != 0; t = t->next)
{
int reject = 0;
name = t->name;
if (strchr (name, '%') != 0)
break;
if (*name == '.' && strchr (name, '/') == 0
#ifdef HAVE_DOS_PATHS
&& strchr (name, '\\') == 0
#endif
)
continue;
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)
{
define_variable_global (".DEFAULT_GOAL", 13, t->name,
o_file, 0, NILF);
break;
}
}
}
continue;
}
rule_complete:
record_waiting_files ();
}
#undef word1eq
if (conditionals->if_cmds)
fatal (fstart, _("missing `endif'"));
record_waiting_files ();
if (collapsed)
free ((char *) collapsed);
free ((char *) commands);
return 1;
}
static void
remove_comments (char *line)
{
char *comment;
comment = find_char_unquote (line, '#', 0, 0, 0);
if (comment != 0)
*comment = '\0';
}
static void
do_define (char *name, unsigned int namelen,
enum variable_origin origin, struct ebuffer *ebuf)
{
struct floc defstart;
long nlines = 0;
int nlevels = 1;
unsigned int length = 100;
char *definition = (char *) xmalloc (length);
unsigned int idx = 0;
char *p;
char *var = (char *) alloca (namelen + 1);
bcopy (name, var, namelen);
var[namelen] = '\0';
var = variable_expand (var);
defstart = ebuf->floc;
while (1)
{
unsigned int len;
char *line;
nlines = readline (ebuf);
ebuf->floc.lineno += nlines;
if (nlines < 0)
break;
line = ebuf->buffer;
collapse_continuations (line);
if (line[0] != '\t')
{
p = next_token (line);
len = strlen (p);
if ((len == 6 || (len > 6 && isblank ((unsigned char)p[6])))
&& strneq (p, "define", 6))
++nlevels;
else if ((len == 5 || (len > 5 && isblank ((unsigned char)p[5])))
&& strneq (p, "endef", 5))
{
p += 5;
remove_comments (p);
if (*next_token (p) != '\0')
error (&ebuf->floc,
_("Extraneous text after `endef' directive"));
if (--nlevels == 0)
{
if (idx == 0)
definition[0] = '\0';
else
definition[idx - 1] = '\0';
define_variable_global (var, strlen (var), definition,
origin, 1, &defstart);
free (definition);
return;
}
}
}
len = strlen (line);
if (idx + len + 1 > length)
{
length = (idx + len) * 2;
definition = (char *) xrealloc (definition, length + 1);
}
bcopy (line, &definition[idx], len);
idx += len;
definition[idx++] = '\n';
}
fatal (&defstart, _("missing `endef', unterminated `define'"));
return;
}
static int
conditional_line (char *line, int len, const struct floc *flocp)
{
char *cmdname;
enum { c_ifdef, c_ifndef, c_ifeq, c_ifneq, c_else, c_endif } cmdtype;
unsigned int i;
unsigned int o;
#define word1eq(s) (len == sizeof(s)-1 && strneq (s, line, sizeof(s)-1))
#define chkword(s, t) if (word1eq (s)) { cmdtype = (t); cmdname = (s); }
chkword ("ifdef", c_ifdef)
else chkword ("ifndef", c_ifndef)
else chkword ("ifeq", c_ifeq)
else chkword ("ifneq", c_ifneq)
else chkword ("else", c_else)
else chkword ("endif", c_endif)
else
return -2;
line = next_token (line + len);
#define EXTRANEOUS() error (flocp, _("Extraneous text after `%s' directive"), cmdname)
if (cmdtype == c_endif)
{
if (*line != '\0')
EXTRANEOUS ();
if (!conditionals->if_cmds)
fatal (flocp, _("extraneous `%s'"), cmdname);
--conditionals->if_cmds;
goto DONE;
}
if (cmdtype == c_else)
{
const char *p;
if (!conditionals->if_cmds)
fatal (flocp, _("extraneous `%s'"), cmdname);
o = conditionals->if_cmds - 1;
if (conditionals->seen_else[o])
fatal (flocp, _("only one `else' per conditional"));
switch (conditionals->ignoring[o])
{
case 0:
conditionals->ignoring[o] = 2;
break;
case 1:
conditionals->ignoring[o] = 0;
break;
}
if (*line == '\0')
{
conditionals->seen_else[o] = 1;
goto DONE;
}
for (p = line+1; *p != '\0' && !isspace ((unsigned char)*p); ++p)
;
len = p - line;
if (word1eq("else") || word1eq("endif")
|| conditional_line (line, len, flocp) < 0)
EXTRANEOUS ();
else
{
if (conditionals->ignoring[o] < 2)
conditionals->ignoring[o] = conditionals->ignoring[o+1];
--conditionals->if_cmds;
}
goto DONE;
}
if (conditionals->allocated == 0)
{
conditionals->allocated = 5;
conditionals->ignoring = (char *) xmalloc (conditionals->allocated);
conditionals->seen_else = (char *) xmalloc (conditionals->allocated);
}
o = 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[o] = 0;
for (i = 0; i < o; ++i)
if (conditionals->ignoring[i])
{
conditionals->ignoring[o] = 1;
return 1;
}
if (cmdtype == c_ifdef || cmdtype == c_ifndef)
{
char *var;
struct variable *v;
char *p;
var = allocated_variable_expand (line);
p = end_of_token (var);
i = p - var;
p = next_token (p);
if (*p != '\0')
return -1;
var[i] = '\0';
v = lookup_variable (var, i);
conditionals->ignoring[o] =
((v != 0 && *v->value != '\0') == (cmdtype == c_ifndef));
free (var);
}
else
{
char *s1, *s2;
unsigned int len;
char termin = *line == '(' ? ',' : *line;
if (termin != ',' && termin != '"' && termin != '\'')
return -1;
s1 = ++line;
if (termin == ',')
{
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 ((unsigned char)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')
EXTRANEOUS ();
s2 = variable_expand (s2);
conditionals->ignoring[o] = (streq (s1, s2) == (cmdtype == c_ifneq));
}
DONE:
for (i = 0; i < conditionals->if_cmds; ++i)
if (conditionals->ignoring[i])
return 1;
return 0;
}
static unsigned long
dep_hash_1 (const void *key)
{
return_STRING_HASH_1 (dep_name ((struct dep const *) key));
}
static unsigned long
dep_hash_2 (const void *key)
{
return_STRING_HASH_2 (dep_name ((struct dep const *) key));
}
static int
dep_hash_cmp (const void *x, const void *y)
{
struct dep *dx = (struct dep *) x;
struct dep *dy = (struct dep *) y;
int cmp = strcmp (dep_name (dx), dep_name (dy));
if (!cmp && dx->ignore_mtime != dy->ignore_mtime)
dx->ignore_mtime = dy->ignore_mtime = 0;
return cmp;
}
void
uniquize_deps (struct dep *chain)
{
struct hash_table deps;
register struct dep **depp;
hash_init (&deps, 500, dep_hash_1, dep_hash_2, dep_hash_cmp);
depp = &chain;
while (*depp)
{
struct dep *dep = *depp;
struct dep **dep_slot = (struct dep **) hash_find_slot (&deps, dep);
if (HASH_VACANT (*dep_slot))
{
hash_insert_at (&deps, dep, dep_slot);
depp = &dep->next;
}
else
{
*depp = dep->next;
}
}
hash_free (&deps, 0);
}
static void
record_target_var (struct nameseq *filenames, char *defn,
enum variable_origin origin, int exported,
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;
char *fname;
char *percent;
struct pattern_var *p;
nextf = filenames->next;
free ((char *) filenames);
percent = find_percent (name);
if (percent)
{
p = create_pattern_var (name, percent);
p->variable.fileinfo = *flocp;
v = parse_variable_definition (&p->variable, defn);
assert (v != 0);
if (v->flavor == f_simple)
v->value = allocated_variable_expand (v->value);
else
v->value = xstrdup (v->value);
fname = p->target;
}
else
{
struct file *f;
f = lookup_file (name);
if (!f)
f = enter_file (name);
else if (f->double_colon)
f = f->double_colon;
initialize_file_variables (f, 1);
fname = f->name;
current_variable_set_list = f->variables;
v = try_variable_definition (flocp, defn, origin, 1);
if (!v)
fatal (flocp, _("Malformed target-specific variable definition"));
current_variable_set_list = global;
}
v->origin = origin;
v->per_target = 1;
v->export = exported ? v_export : v_default;
if (origin != o_override)
{
struct variable *gv;
int len = strlen(v->name);
gv = lookup_variable (v->name, len);
if (gv && (gv->origin == o_env_override || gv->origin == o_command))
{
if (v->value != 0)
free (v->value);
v->value = xstrdup (gv->value);
v->origin = gv->origin;
v->recursive = gv->recursive;
v->append = 0;
}
}
if (name != fname && (name < fname || name > fname + strlen (fname)))
free (name);
}
}
static void
record_files (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)
{
struct nameseq *nextf;
int implicit = 0;
unsigned int max_targets = 0, target_idx = 0;
char **targets = 0, **target_percents = 0;
struct commands *cmds;
if (snapped_deps)
fatal (flocp, _("prerequisites cannot be defined in command scripts"));
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)
{
char *name = filenames->name;
struct file *f;
struct dep *this = 0;
char *implicit_percent;
nextf = filenames->next;
free (filenames);
if (streq (name, ".POSIX"))
posix_pedantic = 1;
else if (streq (name, ".SECONDEXPANSION"))
second_expansion = 1;
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;
}
if (pattern && !pattern_matches (pattern, pattern_percent, name))
error (flocp, _("target `%s' doesn't match the target pattern"), name);
else if (deps)
{
this = nextf != 0 ? copy_dep_chain (deps) : deps;
this->need_2nd_expansion = (second_expansion
&& strchr (this->name, '$'));
}
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)
{
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)
{
free_dep_chain (f->deps);
f->deps = 0;
}
else if (this != 0)
{
if (f->deps != 0)
{
struct dep **d_ptr = &f->deps;
while ((*d_ptr)->next != 0)
d_ptr = &(*d_ptr)->next;
if (cmds != 0)
(*d_ptr)->next = this;
else
{
if (f->cmds != 0)
{
this->next = *d_ptr;
*d_ptr = this;
}
else
(*d_ptr)->next = this;
}
}
else
f->deps = this;
if (cmds != 0)
f->updating = 1;
}
}
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 (pattern)
{
static char *percent = "%";
char *buffer = variable_expand ("");
char *o = patsubst_expand (buffer, name, pattern, percent,
pattern_percent+1, percent+1);
f->stem = savestring (buffer, o - buffer);
if (this)
{
this->staticpattern = 1;
this->stem = xstrdup (f->stem);
}
}
if (f != 0 && name != f->name
&& (name < f->name || name > f->name + strlen (f->name)))
{
free (name);
name = f->name;
}
if (streq (*default_goal_name, name)
&& (default_goal_file == 0
|| ! streq (default_goal_file->name, name)))
default_goal_file = f;
}
if (implicit)
{
targets[target_idx] = 0;
target_percents[target_idx] = 0;
if (deps)
deps->need_2nd_expansion = second_expansion;
create_pattern_rule (targets, target_percents, two_colon, deps, cmds, 1);
free ((char *) target_percents);
}
}
static char *
find_char_unquote (char *string, int stop1, int stop2, int blank,
int ignorevars)
{
unsigned int string_len = 0;
register char *p = string;
if (ignorevars)
ignorevars = '$';
while (1)
{
if (stop2 && blank)
while (*p != '\0' && *p != ignorevars && *p != stop1 && *p != stop2
&& ! isblank ((unsigned char) *p))
++p;
else if (stop2)
while (*p != '\0' && *p != ignorevars && *p != stop1 && *p != stop2)
++p;
else if (blank)
while (*p != '\0' && *p != ignorevars && *p != stop1
&& ! isblank ((unsigned char) *p))
++p;
else
while (*p != '\0' && *p != ignorevars && *p != stop1)
++p;
if (*p == '\0')
break;
if (*p == ignorevars)
{
char openparen = p[1];
p += 2;
if (openparen == '(' || openparen == '{')
{
unsigned int pcount = 1;
char closeparen = (openparen == '(' ? ')' : '}');
while (*p)
{
if (*p == openparen)
++pcount;
else if (*p == closeparen)
if (--pcount == 0)
{
++p;
break;
}
++p;
}
}
continue;
}
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 (char *pattern)
{
return find_char_unquote (pattern, '%', 0, 0, 0);
}
struct nameseq *
parse_file_seq (char **stringp, int stopchar, unsigned int size, int strip)
{
struct nameseq *new = 0;
struct nameseq *new1, *lastnew1;
char *p = *stringp;
char *q;
char *name;
#ifdef VMS
# define VMS_COMMA ','
#else
# define VMS_COMMA 0
#endif
while (1)
{
p = next_token (p);
if (*p == '\0')
break;
if (*p == stopchar)
break;
q = p;
p = find_char_unquote (q, stopchar, VMS_COMMA, 1, 0);
#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, stopchar, VMS_COMMA, 1, 0);
}
#endif
#ifdef HAVE_DOS_PATHS
if (stopchar == ':')
while (p != 0 && !isspace ((unsigned char)*p) &&
(p[1] == '\\' || p[1] == '/') && isalpha ((unsigned char)p[-1]))
p = find_char_unquote (p + 1, stopchar, VMS_COMMA, 1, 0);
#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
readstring (struct ebuffer *ebuf)
{
char *eol;
if (ebuf->bufnext >= ebuf->bufstart + ebuf->size)
return -1;
eol = ebuf->buffer = ebuf->bufnext;
while (1)
{
int backslash = 0;
char *bol = eol;
char *p;
eol = p = strchr (eol , '\n');
if (!eol)
{
ebuf->bufnext = ebuf->bufstart + ebuf->size + 1;
return 0;
}
while (p > bol && *(--p) == '\\')
backslash = !backslash;
if (!backslash)
break;
++eol;
}
*eol = '\0';
ebuf->bufnext = eol+1;
return 0;
}
static long
readline (struct ebuffer *ebuf)
{
char *p;
char *end;
char *start;
long nlines = 0;
if (!ebuf->fp)
return readstring (ebuf);
p = start = ebuf->bufstart;
end = p + ebuf->size;
*p = '\0';
while (fgets (p, end - p, ebuf->fp) != 0)
{
char *p2;
unsigned long len;
int backslash;
len = strlen (p);
if (len == 0)
{
error (&ebuf->floc,
_("warning: NUL character seen; rest of line ignored"));
p[0] = '\n';
len = 1;
}
p += len;
if (p[-1] != '\n')
goto more_buffer;
++nlines;
#if !defined(WINDOWS32) && !defined(__MSDOS__) && !defined(__EMX__)
if ((p - start) > 1 && p[-2] == '\r')
{
--p;
p[-1] = '\n';
}
#endif
backslash = 0;
for (p2 = p - 2; p2 >= start; --p2)
{
if (*p2 != '\\')
break;
backslash = !backslash;
}
if (!backslash)
{
p[-1] = '\0';
break;
}
if (end - p >= 80)
continue;
more_buffer:
{
unsigned long off = p - start;
ebuf->size *= 2;
start = ebuf->buffer = ebuf->bufstart = (char *) xrealloc (start,
ebuf->size);
p = start + off;
end = start + ebuf->size;
*p = '\0';
}
}
if (ferror (ebuf->fp))
pfatal_with_name (ebuf->floc.filenm);
return nlines ? nlines : p == ebuf->bufstart ? -1 : 1;
}
static enum make_word_type
get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length)
{
enum make_word_type wtype = w_bogus;
char *p = buffer, *beg;
char c;
while (isblank ((unsigned char)*p))
++p;
beg = p;
c = *(p++);
switch (c)
{
case '\0':
wtype = w_eol;
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 '=':
goto done_word;
case ':':
#ifdef HAVE_DOS_PATHS
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 (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++;
int e;
if (dir[0] == '~')
{
char *expanded = tilde_expand (dir);
if (expanded != 0)
dir = expanded;
}
EINTRLOOP (e, stat (dir, &stbuf));
if (e == 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)
{
int e;
EINTRLOOP (e, stat (default_include_directories[i], &stbuf));
if (e == 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;
do_variable_definition (NILF, ".INCLUDE_DIRS", dirs[i],
o_default, f_append, 0);
}
include_directories = dirs;
}
char *
tilde_expand (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 (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)
{
unsigned int alen = strlen (gl.gl_pathv[i]);
unsigned int mlen = strlen (memname);
struct nameseq *elt
= (struct nameseq *) xmalloc (size);
if (size > sizeof (struct nameseq))
bzero (((char *) elt) + sizeof (struct nameseq),
size - sizeof (struct nameseq));
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);
if (size > sizeof (struct nameseq))
bzero (((char *) elt) + sizeof (struct nameseq),
size - sizeof (struct nameseq));
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;
}