#include "make.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "dep.h"
#include "variable.h"
#include "debug.h"
#include <assert.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
#include <sys/file.h>
#endif
#ifdef VMS
#include <starlet.h>
#endif
#ifdef WINDOWS32
#include <io.h>
#endif
extern int try_implicit_rule PARAMS ((struct file *file, unsigned int depth));
#define start_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\
->updating = 1)
#define finish_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\
->updating = 0)
#define is_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\
->updating)
unsigned int commands_started = 0;
static unsigned int considered;
static int update_file PARAMS ((struct file *file, unsigned int depth));
static int update_file_1 PARAMS ((struct file *file, unsigned int depth));
static int check_dep PARAMS ((struct file *file, unsigned int depth, FILE_TIMESTAMP this_mtime, int *must_make_ptr));
static int touch_file PARAMS ((struct file *file));
static void remake_file PARAMS ((struct file *file));
static FILE_TIMESTAMP name_mtime PARAMS ((char *name));
static int library_search PARAMS ((char **lib, FILE_TIMESTAMP *mtime_ptr));
int
update_goal_chain (struct dep *goals)
{
int t = touch_flag, q = question_flag, n = just_print_flag;
unsigned int j = job_slots;
int status = -1;
#define MTIME(file) (rebuilding_makefiles ? file_mtime_no_search (file) \
: file_mtime (file))
goals = copy_dep_chain (goals);
{
struct dep *g;
for (g = goals; g != 0; g = g->next)
g->changed = 0;
}
considered = 1;
while (goals != 0)
{
register struct dep *g, *lastgoal;
start_waiting_jobs ();
reap_children (1, 0);
lastgoal = 0;
g = goals;
while (g != 0)
{
struct file *file;
int stop = 0, any_not_updated = 0;
for (file = g->file->double_colon ? g->file->double_colon : g->file;
file != NULL;
file = file->prev)
{
unsigned int ocommands_started;
int x;
check_renamed (file);
if (rebuilding_makefiles)
{
if (file->cmd_target)
{
touch_flag = t;
question_flag = q;
just_print_flag = n;
}
else
touch_flag = question_flag = just_print_flag = 0;
}
ocommands_started = commands_started;
x = update_file (file, rebuilding_makefiles ? 1 : 0);
check_renamed (file);
if (commands_started > ocommands_started)
g->changed = 1;
stop = 0;
if ((x != 0 || file->updated) && status < 1)
{
if (file->update_status != 0)
{
status = file->update_status;
stop = (question_flag && !keep_going_flag
&& !rebuilding_makefiles);
}
else
{
FILE_TIMESTAMP mtime = MTIME (file);
check_renamed (file);
if (file->updated && g->changed &&
mtime != file->mtime_before_update)
{
if (!rebuilding_makefiles
|| (!just_print_flag && !question_flag))
status = 0;
if (rebuilding_makefiles && file->dontcare)
stop = 1;
}
}
}
any_not_updated |= !file->updated;
if (stop)
break;
}
file = g->file;
if (stop || !any_not_updated)
{
if (!rebuilding_makefiles
&& file->update_status == 0 && !g->changed
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
&& !(next_flag & NEXT_QUIET_FLAG)
#endif
&& !silent_flag && !question_flag)
message (1, ((file->phony || file->cmds == 0)
? _("Nothing to be done for `%s'.")
: _("`%s' is up to date.")),
file->name);
if (lastgoal == 0)
goals = g->next;
else
lastgoal->next = g->next;
free ((char *) g);
g = lastgoal == 0 ? goals : lastgoal->next;
if (stop)
break;
}
else
{
lastgoal = g;
g = g->next;
}
}
if (g == 0)
considered = !considered;
}
if (rebuilding_makefiles)
{
touch_flag = t;
question_flag = q;
just_print_flag = n;
job_slots = j;
}
return status;
}
static int
update_file (struct file *file, unsigned int depth)
{
register int status = 0;
register struct file *f;
f = file->double_colon ? file->double_colon : file;
if (f->considered == considered)
{
DBF (DB_VERBOSE, _("Pruning file `%s'.\n"));
return f->command_state == cs_finished ? f->update_status : 0;
}
for (; f != 0; f = f->prev)
{
f->considered = considered;
status |= update_file_1 (f, depth);
check_renamed (f);
alloca (0);
if (status != 0 && !keep_going_flag)
return status;
if (f->command_state == cs_running
|| f->command_state == cs_deps_running)
{
status = 0;
break;
}
}
if (file->double_colon)
for (; f != 0 ; f = f->prev)
{
struct dep *d;
f->considered = considered;
for (d = f->deps; d != 0; d = d->next)
status |= update_file (d->file, depth + 1);
}
return status;
}
static void
complain (const struct file *file)
{
const char *msg_noparent
= _("%sNo rule to make target `%s'%s");
const char *msg_parent
= _("%sNo rule to make target `%s', needed by `%s'%s");
if (!keep_going_flag)
{
if (file->parent == 0)
fatal (NILF, msg_noparent, "", file->name, "");
fatal (NILF, msg_parent, "", file->name, file->parent->name, "");
}
if (file->parent == 0)
error (NILF, msg_noparent, "*** ", file->name, ".");
else
error (NILF, msg_parent, "*** ", file->name, file->parent->name, ".");
}
static int
update_file_1 (struct file *file, unsigned int depth)
{
register FILE_TIMESTAMP this_mtime;
int noexist, must_make, deps_changed;
int dep_status = 0;
register struct dep *d, *lastd;
int running = 0;
DBF (DB_VERBOSE, _("Considering target file `%s'.\n"));
if (file->updated)
{
if (file->update_status > 0)
{
DBF (DB_VERBOSE,
_("Recently tried and failed to update file `%s'.\n"));
if (file->dontcare && !rebuilding_makefiles)
{
file->dontcare = 0;
complain (file);
}
return file->update_status;
}
DBF (DB_VERBOSE, _("File `%s' was considered already.\n"));
return 0;
}
switch (file->command_state)
{
case cs_not_started:
case cs_deps_running:
break;
case cs_running:
DBF (DB_VERBOSE, _("Still updating file `%s'.\n"));
return 0;
case cs_finished:
DBF (DB_VERBOSE, _("Finished updating file `%s'.\n"));
return file->update_status;
default:
abort ();
}
++depth;
start_updating (file);
this_mtime = file_mtime (file);
check_renamed (file);
noexist = this_mtime == NONEXISTENT_MTIME;
if (noexist)
DBF (DB_BASIC, _("File `%s' does not exist.\n"));
else if (ORDINARY_MTIME_MIN <= this_mtime && this_mtime <= ORDINARY_MTIME_MAX
&& file->low_resolution_time)
{
int ns = FILE_TIMESTAMP_NS (this_mtime);
if (ns != 0)
error (NILF, _("*** Warning: .LOW_RESOLUTION_TIME file `%s' has a high resolution time stamp"),
file->name);
this_mtime += FILE_TIMESTAMPS_PER_S - 1 - ns;
}
must_make = noexist;
if (!file->phony && file->cmds == 0 && !file->tried_implicit)
{
if (try_implicit_rule (file, depth))
DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n"));
else
DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n"));
file->tried_implicit = 1;
}
if (file->cmds == 0 && !file->is_target
&& default_file != 0 && default_file->cmds != 0)
{
DBF (DB_IMPLICIT, _("Using default commands for `%s'.\n"));
file->cmds = default_file->cmds;
}
lastd = 0;
d = file->deps;
while (d != 0)
{
FILE_TIMESTAMP mtime;
int maybe_make;
int dontcare = 0;
check_renamed (d->file);
mtime = file_mtime (d->file);
check_renamed (d->file);
if (is_updating (d->file))
{
#if __APPLE__ || NeXT || NeXT_PDO
if (!(next_flag & NEXT_QUIET_FLAG))
#endif
error (NILF, _("Circular %s <- %s dependency dropped."),
file->name, d->file->name);
if (lastd == 0)
file->deps = d->next;
else
lastd->next = d->next;
d = d->next;
continue;
}
d->file->parent = file;
maybe_make = must_make;
if (rebuilding_makefiles)
{
dontcare = d->file->dontcare;
d->file->dontcare = file->dontcare;
}
dep_status |= check_dep (d->file, depth, this_mtime, &maybe_make);
if (rebuilding_makefiles)
d->file->dontcare = dontcare;
if (! d->ignore_mtime)
must_make = maybe_make;
check_renamed (d->file);
{
register struct file *f = d->file;
if (f->double_colon)
f = f->double_colon;
do
{
running |= (f->command_state == cs_running
|| f->command_state == cs_deps_running);
f = f->prev;
}
while (f != 0);
}
if (dep_status != 0 && !keep_going_flag)
break;
if (!running)
d->changed = ((file_mtime (d->file) != mtime)
);
lastd = d;
d = d->next;
}
if (must_make || always_make_flag)
{
for (d = file->deps; d != 0; d = d->next)
if (d->file->intermediate)
{
int dontcare = 0;
FILE_TIMESTAMP mtime = file_mtime (d->file);
check_renamed (d->file);
d->file->parent = file;
if (rebuilding_makefiles)
{
dontcare = d->file->dontcare;
d->file->dontcare = file->dontcare;
}
dep_status |= update_file (d->file, depth);
if (rebuilding_makefiles)
d->file->dontcare = dontcare;
check_renamed (d->file);
{
register struct file *f = d->file;
if (f->double_colon)
f = f->double_colon;
do
{
running |= (f->command_state == cs_running
|| f->command_state == cs_deps_running);
f = f->prev;
}
while (f != 0);
}
if (dep_status != 0 && !keep_going_flag)
break;
if (!running)
d->changed = ((file->phony && file->cmds != 0)
|| file_mtime (d->file) != mtime);
}
}
finish_updating (file);
DBF (DB_VERBOSE, _("Finished prerequisites of target file `%s'.\n"));
if (running)
{
set_command_state (file, cs_deps_running);
--depth;
DBF (DB_VERBOSE, _("The prerequisites of `%s' are being made.\n"));
return 0;
}
if (dep_status != 0)
{
file->update_status = dep_status;
notice_finished_file (file);
--depth;
DBF (DB_VERBOSE, _("Giving up on target file `%s'.\n"));
if (depth == 0 && keep_going_flag
&& !just_print_flag && !question_flag)
error (NILF,
_("Target `%s' not remade because of errors."), file->name);
return dep_status;
}
if (file->command_state == cs_deps_running)
set_command_state (file, cs_not_started);
deps_changed = 0;
for (d = file->deps; d != 0; d = d->next)
{
FILE_TIMESTAMP d_mtime = file_mtime (d->file);
check_renamed (d->file);
if (! d->ignore_mtime)
{
#if 1
if (d_mtime == NONEXISTENT_MTIME && !d->file->intermediate)
must_make = 1;
#endif
deps_changed |= d->changed;
}
d->changed |= noexist || d_mtime > this_mtime;
if (!noexist && ISDB (DB_BASIC|DB_VERBOSE))
{
const char *fmt = 0;
if (d->ignore_mtime)
{
if (ISDB (DB_VERBOSE))
fmt = _("Prerequisite `%s' is order-only for target `%s'.\n");
}
else if (d_mtime == NONEXISTENT_MTIME)
{
if (ISDB (DB_BASIC))
fmt = _("Prerequisite `%s' of target `%s' does not exist.\n");
}
else if (d->changed)
{
if (ISDB (DB_BASIC))
fmt = _("Prerequisite `%s' is newer than target `%s'.\n");
}
else if (ISDB (DB_VERBOSE))
fmt = _("Prerequisite `%s' is older than target `%s'.\n");
if (fmt)
{
print_spaces (depth);
printf (fmt, dep_name (d), file->name);
fflush (stdout);
}
}
}
depth--;
if (file->double_colon && file->deps == 0)
{
must_make = 1;
DBF (DB_BASIC,
_("Target `%s' is double-colon and has no prerequisites.\n"));
}
else if (!noexist && file->is_target && !deps_changed && file->cmds == 0
&& !always_make_flag)
{
must_make = 0;
DBF (DB_VERBOSE,
_("No commands for `%s' and no prerequisites actually changed.\n"));
}
else if (!must_make && file->cmds != 0 && always_make_flag)
{
must_make = 1;
DBF (DB_VERBOSE, _("Making `%s' due to always-make flag.\n"));
}
if (!must_make)
{
if (ISDB (DB_VERBOSE))
{
print_spaces (depth);
printf (_("No need to remake target `%s'"), file->name);
if (!streq (file->name, file->hname))
printf (_("; using VPATH name `%s'"), file->hname);
puts (".");
fflush (stdout);
}
notice_finished_file (file);
while (file)
{
#if __APPLE__ || NeXT || NeXT_PDO
file->old_name = file->name;
#endif
file->name = file->hname;
file = file->prev;
}
return 0;
}
DBF (DB_BASIC, _("Must remake target `%s'.\n"));
if (!streq(file->name, file->hname))
{
DB (DB_BASIC, (_(" Ignoring VPATH name `%s'.\n"), file->hname));
file->ignore_vpath = 1;
}
remake_file (file);
if (file->command_state != cs_finished)
{
DBF (DB_VERBOSE, _("Commands of `%s' are being run.\n"));
return 0;
}
switch (file->update_status)
{
case 2:
DBF (DB_BASIC, _("Failed to remake target file `%s'.\n"));
break;
case 0:
DBF (DB_BASIC, _("Successfully remade target file `%s'.\n"));
break;
case 1:
DBF (DB_BASIC, _("Target file `%s' needs remade under -q.\n"));
break;
default:
assert (file->update_status >= 0 && file->update_status <= 2);
break;
}
file->updated = 1;
return file->update_status;
}
void
notice_finished_file (struct file *file)
{
struct dep *d;
int ran = file->command_state == cs_running;
int touched = 0;
file->command_state = cs_finished;
file->updated = 1;
if (touch_flag
&& file->update_status == 0)
{
if (file->cmds != 0 && file->cmds->any_recurse)
{
unsigned int i;
for (i = 0; i < file->cmds->ncommand_lines; ++i)
if (!(file->cmds->lines_flags[i] & COMMANDS_RECURSE))
goto have_nonrecursing;
}
else
{
have_nonrecursing:
if (file->phony)
file->update_status = 0;
else if (file->cmds != 0)
{
file->update_status = touch_file (file);
commands_started++;
touched = 1;
}
}
}
if (file->mtime_before_update == UNKNOWN_MTIME)
file->mtime_before_update = file->last_mtime;
if ((ran && !file->phony) || touched)
{
int i = 0;
if (question_flag || just_print_flag || touch_flag)
{
for (i = file->cmds->ncommand_lines; i > 0; --i)
if (! (file->cmds->lines_flags[i-1] & COMMANDS_RECURSE))
break;
}
else if (file->is_target && file->cmds == 0)
i = 1;
file->last_mtime = i == 0 ? UNKNOWN_MTIME : NEW_MTIME;
}
if (file->double_colon)
{
struct file *f;
FILE_TIMESTAMP max_mtime = file->last_mtime;
for (f = file->double_colon; f != 0 && f->updated; f = f->prev)
if (max_mtime != UNKNOWN_MTIME
&& (f->last_mtime == UNKNOWN_MTIME || f->last_mtime > max_mtime))
max_mtime = f->last_mtime;
if (f == 0)
for (f = file->double_colon; f != 0; f = f->prev)
f->last_mtime = max_mtime;
}
if (ran && file->update_status != -1)
for (d = file->also_make; d != 0; d = d->next)
{
d->file->command_state = cs_finished;
d->file->updated = 1;
d->file->update_status = file->update_status;
if (ran && !d->file->phony)
(void) f_mtime (d->file, 0);
}
else if (file->update_status == -1)
file->update_status = 0;
}
static int
check_dep (struct file *file, unsigned int depth,
FILE_TIMESTAMP this_mtime, int *must_make_ptr)
{
struct dep *d;
int dep_status = 0;
++depth;
start_updating (file);
if (file->phony || !file->intermediate)
{
FILE_TIMESTAMP mtime;
dep_status = update_file (file, depth);
check_renamed (file);
mtime = file_mtime (file);
check_renamed (file);
if (mtime == NONEXISTENT_MTIME || mtime > this_mtime)
*must_make_ptr = 1;
}
else
{
FILE_TIMESTAMP mtime;
if (!file->phony && file->cmds == 0 && !file->tried_implicit)
{
if (try_implicit_rule (file, depth))
DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n"));
else
DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n"));
file->tried_implicit = 1;
}
if (file->cmds == 0 && !file->is_target
&& default_file != 0 && default_file->cmds != 0)
{
DBF (DB_IMPLICIT, _("Using default commands for `%s'.\n"));
file->cmds = default_file->cmds;
}
check_renamed (file);
mtime = file_mtime (file);
check_renamed (file);
if (mtime != NONEXISTENT_MTIME && mtime > this_mtime)
*must_make_ptr = 1;
else
{
struct dep *lastd;
lastd = 0;
d = file->deps;
while (d != 0)
{
int maybe_make;
if (is_updating (d->file))
{
#if __APPLE__ || NeXT || NeXT_PDO
if (!(next_flag & NEXT_QUIET_FLAG))
#endif
error (NILF, _("Circular %s <- %s dependency dropped."),
file->name, d->file->name);
if (lastd == 0)
{
file->deps = d->next;
free_dep (d);
d = file->deps;
}
else
{
lastd->next = d->next;
free_dep (d);
d = lastd->next;
}
continue;
}
d->file->parent = file;
maybe_make = *must_make_ptr;
dep_status |= check_dep (d->file, depth, this_mtime,
&maybe_make);
if (! d->ignore_mtime)
*must_make_ptr = maybe_make;
check_renamed (d->file);
if (dep_status != 0 && !keep_going_flag)
break;
if (d->file->command_state == cs_running
|| d->file->command_state == cs_deps_running)
set_command_state (file, cs_deps_running);
lastd = d;
d = d->next;
}
}
}
finish_updating (file);
return dep_status;
}
#define TOUCH_ERROR(call) return (perror_with_name (call, file->name), 1)
static int
touch_file (struct file *file)
{
if (!silent_flag)
message (0, "touch %s", file->name);
#ifndef NO_ARCHIVES
if (ar_name (file->name))
return ar_touch (file->name);
else
#endif
{
int fd = open (file->name, O_RDWR | O_CREAT, 0666);
if (fd < 0)
TOUCH_ERROR ("touch: open: ");
else
{
struct stat statbuf;
char buf;
int e;
EINTRLOOP (e, fstat (fd, &statbuf));
if (e < 0)
TOUCH_ERROR ("touch: fstat: ");
if (read (fd, &buf, 1) < 0)
TOUCH_ERROR ("touch: read: ");
if (lseek (fd, 0L, 0) < 0L)
TOUCH_ERROR ("touch: lseek: ");
if (write (fd, &buf, 1) < 0)
TOUCH_ERROR ("touch: write: ");
if (statbuf.st_size == 0)
{
(void) close (fd);
fd = open (file->name, O_RDWR | O_TRUNC, 0666);
if (fd < 0)
TOUCH_ERROR ("touch: open: ");
}
(void) close (fd);
}
}
return 0;
}
static void
remake_file (struct file *file)
{
if (file->cmds == 0)
{
if (file->phony)
file->update_status = 0;
else if (file->is_target)
file->update_status = 0;
else
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
{
char *name = file->name;
if ((next_flag & NEXT_VPATH_FLAG) && general_vpath_search(&name)) {
free(name);
file->update_status = 0;
} else
#endif
{
if (!rebuilding_makefiles || !file->dontcare)
complain (file);
file->update_status = 2;
}
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
}
#endif
}
else
{
chop_commands (file->cmds);
if (!touch_flag || file->cmds->any_recurse)
{
execute_file_commands (file);
return;
}
file->update_status = 0;
}
notice_finished_file (file);
}
FILE_TIMESTAMP
f_mtime (struct file *file, int search)
{
FILE_TIMESTAMP mtime;
#ifndef NO_ARCHIVES
if (ar_name (file->name))
{
char *arname, *memname;
struct file *arfile;
int arname_used = 0;
time_t member_date;
ar_parse_name (file->name, &arname, &memname);
arfile = lookup_file (arname);
if (arfile == 0)
{
arfile = enter_file (arname);
arname_used = 1;
}
mtime = f_mtime (arfile, search);
check_renamed (arfile);
if (search && strcmp (arfile->hname, arname))
{
char *name;
unsigned int arlen, memlen;
if (!arname_used)
{
free (arname);
arname_used = 1;
}
arname = arfile->hname;
arlen = strlen (arname);
memlen = strlen (memname);
name = (char *) xmalloc (arlen + 1 + memlen + 2);
bcopy (arname, name, arlen);
name[arlen] = '(';
bcopy (memname, name + arlen + 1, memlen);
name[arlen + 1 + memlen] = ')';
name[arlen + 1 + memlen + 1] = '\0';
if (arfile->name == arfile->hname)
rename_file (file, name);
else
rehash_file (file, name);
check_renamed (file);
}
if (!arname_used)
free (arname);
free (memname);
file->low_resolution_time = 1;
if (mtime == NONEXISTENT_MTIME)
return NONEXISTENT_MTIME;
member_date = ar_member_date (file->hname);
mtime = (member_date == (time_t) -1
? NONEXISTENT_MTIME
: file_timestamp_cons (file->hname, member_date, 0));
}
else
#endif
{
mtime = name_mtime (file->name);
if (mtime == NONEXISTENT_MTIME && search && !file->ignore_vpath)
{
char *name = file->name;
if (vpath_search (&name, &mtime)
|| (name[0] == '-' && name[1] == 'l'
&& library_search (&name, &mtime)))
{
if (mtime != UNKNOWN_MTIME)
file->last_mtime = mtime;
if (gpath_search (name, strlen(name) - strlen(file->name) - 1))
{
rename_file (file, name);
check_renamed (file);
return file_mtime (file);
}
rehash_file (file, name);
check_renamed (file);
if (mtime != OLD_MTIME && mtime != NEW_MTIME)
mtime = name_mtime (name);
}
}
}
if (!clock_skew_detected
&& mtime != NONEXISTENT_MTIME && mtime != NEW_MTIME
&& !file->updated)
{
static FILE_TIMESTAMP adjusted_now;
FILE_TIMESTAMP adjusted_mtime = mtime;
#if defined(WINDOWS32) || defined(__MSDOS__)
#define FAT_ADJ_OFFSET (FILE_TIMESTAMP) 3
FILE_TIMESTAMP adjustment = FAT_ADJ_OFFSET << FILE_TIMESTAMP_LO_BITS;
if (ORDINARY_MTIME_MIN + adjustment <= adjusted_mtime)
adjusted_mtime -= adjustment;
#elif defined(__EMX__)
FILE_TIMESTAMP adjustment = (((FILE_TIMESTAMP_S (adjusted_mtime) & 1) == 0
&& FILE_TIMESTAMP_NS (adjusted_mtime) == 0)
? (FILE_TIMESTAMP) 1 << FILE_TIMESTAMP_LO_BITS
: 0);
#endif
if (adjusted_now < adjusted_mtime)
{
int resolution;
FILE_TIMESTAMP now = file_timestamp_now (&resolution);
adjusted_now = now + (resolution - 1);
if (adjusted_now < adjusted_mtime)
{
#ifdef NO_FLOAT
error (NILF, _("Warning: File `%s' has modification time in the future"),
file->name);
#else
double from_now =
(FILE_TIMESTAMP_S (mtime) - FILE_TIMESTAMP_S (now)
+ ((FILE_TIMESTAMP_NS (mtime) - FILE_TIMESTAMP_NS (now))
/ 1e9));
error (NILF, _("Warning: File `%s' has modification time %.2g s in the future"),
file->name, from_now);
#endif
clock_skew_detected = 1;
}
}
}
if (file->double_colon)
file = file->double_colon;
do
{
if (mtime != NONEXISTENT_MTIME && file->command_state == cs_not_started
&& file->command_state == cs_not_started
&& !file->tried_implicit && file->intermediate)
file->intermediate = 0;
file->last_mtime = mtime;
file = file->prev;
}
while (file != 0);
return mtime;
}
static FILE_TIMESTAMP
name_mtime (char *name)
{
FILE_TIMESTAMP mtime;
struct stat st;
int e;
EINTRLOOP (e, stat (name, &st));
if (e == 0)
mtime = FILE_TIMESTAMP_STAT_MODTIME (name, st);
else if (errno == ENOENT || errno == ENOTDIR)
mtime = NONEXISTENT_MTIME;
else
{
perror_with_name ("stat: ", name);
return NONEXISTENT_MTIME;
}
#ifdef MAKE_SYMLINKS
#ifndef S_ISLNK
# define S_ISLNK(_m) (((_m)&S_IFMT)==S_IFLNK)
#endif
if (check_symlink_flag)
{
PATH_VAR (lpath);
strcpy (lpath, name);
while (1)
{
FILE_TIMESTAMP ltime;
PATH_VAR (lbuf);
long llen;
char *p;
EINTRLOOP (e, lstat (lpath, &st));
if (e)
{
if (errno != ENOENT && errno != ENOTDIR)
perror_with_name ("lstat: ", lpath);
break;
}
if (!S_ISLNK (st.st_mode))
break;
ltime = FILE_TIMESTAMP_STAT_MODTIME (lpath, st);
if (ltime > mtime)
mtime = ltime;
EINTRLOOP (llen, readlink (lpath, lbuf, GET_PATH_MAX));
if (llen < 0)
{
perror_with_name ("readlink: ", lpath);
break;
}
lbuf[llen] = '\0';
if (lbuf[0] == '/' || (p = strrchr (lpath, '/')) == NULL)
strcpy (lpath, lbuf);
else if ((p - lpath) + llen + 2 > GET_PATH_MAX)
break;
else
strcpy (p+1, lbuf);
}
}
#endif
return mtime;
}
static int
library_search (char **lib, FILE_TIMESTAMP *mtime_ptr)
{
static char *dirs[] =
{
#ifndef _AMIGA
"/lib",
"/usr/lib",
#endif
#if defined(WINDOWS32) && !defined(LIBDIR)
#define LIBDIR "."
#endif
LIBDIR,
0
};
static char *libpatterns = NULL;
char *libname = &(*lib)[2];
FILE_TIMESTAMP mtime;
char *p, *p2;
unsigned int len;
char *file, **dp;
if (!libpatterns)
{
int save = warn_undefined_variables_flag;
warn_undefined_variables_flag = 0;
libpatterns = xstrdup (variable_expand ("$(strip $(.LIBPATTERNS))"));
warn_undefined_variables_flag = save;
}
p2 = libpatterns;
while ((p = find_next_token (&p2, &len)) != 0)
{
static char *buf = NULL;
static unsigned int buflen = 0;
static int libdir_maxlen = -1;
char *libbuf = variable_expand ("");
{
char c = p[len];
char *p3, *p4;
p[len] = '\0';
p3 = find_percent (p);
if (!p3)
{
error (NILF, _(".LIBPATTERNS element `%s' is not a pattern"), p);
for (; len; --len, ++p)
*p = ' ';
*p = c;
continue;
}
p4 = variable_buffer_output (libbuf, p, p3-p);
p4 = variable_buffer_output (p4, libname, strlen (libname));
p4 = variable_buffer_output (p4, p3+1, len - (p3-p));
p[len] = c;
}
mtime = name_mtime (libbuf);
if (mtime != NONEXISTENT_MTIME)
{
*lib = xstrdup (libbuf);
if (mtime_ptr != 0)
*mtime_ptr = mtime;
return 1;
}
file = libbuf;
if (vpath_search (&file, mtime_ptr))
{
*lib = file;
return 1;
}
if (!buflen)
{
for (dp = dirs; *dp != 0; ++dp)
{
int l = strlen (*dp);
if (l > libdir_maxlen)
libdir_maxlen = l;
}
buflen = strlen (libbuf);
buf = xmalloc(libdir_maxlen + buflen + 2);
}
else if (buflen < strlen (libbuf))
{
buflen = strlen (libbuf);
buf = xrealloc (buf, libdir_maxlen + buflen + 2);
}
for (dp = dirs; *dp != 0; ++dp)
{
sprintf (buf, "%s/%s", *dp, libbuf);
mtime = name_mtime (buf);
if (mtime != NONEXISTENT_MTIME)
{
*lib = xstrdup (buf);
if (mtime_ptr != 0)
*mtime_ptr = mtime;
return 1;
}
}
}
return 0;
}