1321964Ssjg/*      $NetBSD: meta.c,v 1.68 2017/07/09 04:54:00 sjg Exp $ */
2236769Sobrien
3236769Sobrien/*
4236769Sobrien * Implement 'meta' mode.
5236769Sobrien * Adapted from John Birrell's patches to FreeBSD make.
6236769Sobrien * --sjg
7236769Sobrien */
8236769Sobrien/*
9321964Ssjg * Copyright (c) 2009-2016, Juniper Networks, Inc.
10236769Sobrien * Portions Copyright (c) 2009, John Birrell.
11236769Sobrien *
12236769Sobrien * Redistribution and use in source and binary forms, with or without
13236769Sobrien * modification, are permitted provided that the following conditions
14236769Sobrien * are met:
15236769Sobrien * 1. Redistributions of source code must retain the above copyright
16236769Sobrien *    notice, this list of conditions and the following disclaimer.
17236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright
18236769Sobrien *    notice, this list of conditions and the following disclaimer in the
19236769Sobrien *    documentation and/or other materials provided with the distribution.
20236769Sobrien *
21236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22236769Sobrien * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23236769Sobrien * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24236769Sobrien * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25236769Sobrien * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26236769Sobrien * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27236769Sobrien * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28236769Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29236769Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30236769Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31236769Sobrien * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32236769Sobrien */
33236769Sobrien#if defined(USE_META)
34236769Sobrien
35236769Sobrien#ifdef HAVE_CONFIG_H
36236769Sobrien# include "config.h"
37236769Sobrien#endif
38236769Sobrien#include <sys/stat.h>
39236769Sobrien#include <sys/ioctl.h>
40292068Ssjg#ifdef HAVE_LIBGEN_H
41236769Sobrien#include <libgen.h>
42292068Ssjg#elif !defined(HAVE_DIRNAME)
43292068Ssjgchar * dirname(char *);
44292068Ssjg#endif
45236769Sobrien#include <errno.h>
46236769Sobrien#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H)
47236769Sobrien#include <err.h>
48236769Sobrien#endif
49236769Sobrien
50236769Sobrien#include "make.h"
51236769Sobrien#include "job.h"
52236769Sobrien
53236769Sobrien#ifdef HAVE_FILEMON_H
54236769Sobrien# include <filemon.h>
55236769Sobrien#endif
56236769Sobrien#if !defined(USE_FILEMON) && defined(FILEMON_SET_FD)
57236769Sobrien# define USE_FILEMON
58236769Sobrien#endif
59236769Sobrien
60236769Sobrienstatic BuildMon Mybm;			/* for compat */
61253883Ssjgstatic Lst metaBailiwick;		/* our scope of control */
62321964Ssjgstatic char *metaBailiwickStr;		/* string storage for the list */
63253883Ssjgstatic Lst metaIgnorePaths;		/* paths we deliberately ignore */
64321964Ssjgstatic char *metaIgnorePathsStr;	/* string storage for the list */
65236769Sobrien
66253883Ssjg#ifndef MAKE_META_IGNORE_PATHS
67253883Ssjg#define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS"
68253883Ssjg#endif
69321964Ssjg#ifndef MAKE_META_IGNORE_PATTERNS
70321964Ssjg#define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS"
71321964Ssjg#endif
72321964Ssjg#ifndef MAKE_META_IGNORE_FILTER
73321964Ssjg#define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
74321964Ssjg#endif
75253883Ssjg
76236769SobrienBoolean useMeta = FALSE;
77236769Sobrienstatic Boolean useFilemon = FALSE;
78236769Sobrienstatic Boolean writeMeta = FALSE;
79321964Ssjgstatic Boolean metaMissing = FALSE;	/* oodate if missing */
80321964Ssjgstatic Boolean filemonMissing = FALSE;	/* oodate if missing */
81236769Sobrienstatic Boolean metaEnv = FALSE;		/* don't save env unless asked */
82236769Sobrienstatic Boolean metaVerbose = FALSE;
83236769Sobrienstatic Boolean metaIgnoreCMDs = FALSE;	/* ignore CMDs in .meta files */
84321964Ssjgstatic Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */
85321964Ssjgstatic Boolean metaIgnoreFilter = FALSE;   /* do we have more complex filtering? */
86236769Sobrienstatic Boolean metaCurdirOk = FALSE;	/* write .meta in .CURDIR Ok? */
87236769Sobrienstatic Boolean metaSilent = FALSE;	/* if we have a .meta be SILENT */
88236769Sobrien
89236769Sobrienextern Boolean forceJobs;
90236769Sobrienextern Boolean comatMake;
91238152Sobrienextern char    **environ;
92236769Sobrien
93236769Sobrien#define	MAKE_META_PREFIX	".MAKE.META.PREFIX"
94236769Sobrien
95236769Sobrien#ifndef N2U
96236769Sobrien# define N2U(n, u)   (((n) + ((u) - 1)) / (u))
97236769Sobrien#endif
98236769Sobrien#ifndef ROUNDUP
99236769Sobrien# define ROUNDUP(n, u)   (N2U((n), (u)) * (u))
100236769Sobrien#endif
101236769Sobrien
102236769Sobrien#if !defined(HAVE_STRSEP)
103236769Sobrien# define strsep(s, d) stresep((s), (d), 0)
104236769Sobrien#endif
105236769Sobrien
106236769Sobrien/*
107236769Sobrien * Filemon is a kernel module which snoops certain syscalls.
108236769Sobrien *
109236769Sobrien * C chdir
110236769Sobrien * E exec
111236769Sobrien * F [v]fork
112236769Sobrien * L [sym]link
113236769Sobrien * M rename
114236769Sobrien * R read
115236769Sobrien * W write
116236769Sobrien * S stat
117236769Sobrien *
118236769Sobrien * See meta_oodate below - we mainly care about 'E' and 'R'.
119236769Sobrien *
120236769Sobrien * We can still use meta mode without filemon, but
121236769Sobrien * the benefits are more limited.
122236769Sobrien */
123236769Sobrien#ifdef USE_FILEMON
124236769Sobrien# ifndef _PATH_FILEMON
125236769Sobrien#   define _PATH_FILEMON "/dev/filemon"
126236769Sobrien# endif
127236769Sobrien
128236769Sobrien/*
129236769Sobrien * Open the filemon device.
130236769Sobrien */
131236769Sobrienstatic void
132236769Sobrienfilemon_open(BuildMon *pbm)
133236769Sobrien{
134236769Sobrien    int retry;
135236769Sobrien
136236769Sobrien    pbm->mon_fd = pbm->filemon_fd = -1;
137236769Sobrien    if (!useFilemon)
138236769Sobrien	return;
139236769Sobrien
140236769Sobrien    for (retry = 5; retry >= 0; retry--) {
141236769Sobrien	if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0)
142236769Sobrien	    break;
143236769Sobrien    }
144236769Sobrien
145236769Sobrien    if (pbm->filemon_fd < 0) {
146236769Sobrien	useFilemon = FALSE;
147236769Sobrien	warn("Could not open %s", _PATH_FILEMON);
148236769Sobrien	return;
149236769Sobrien    }
150236769Sobrien
151236769Sobrien    /*
152236769Sobrien     * We use a file outside of '.'
153236769Sobrien     * to avoid a FreeBSD kernel bug where unlink invalidates
154236769Sobrien     * cwd causing getcwd to do a lot more work.
155236769Sobrien     * We only care about the descriptor.
156236769Sobrien     */
157236769Sobrien    pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL);
158236769Sobrien    if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) {
159236769Sobrien	err(1, "Could not set filemon file descriptor!");
160236769Sobrien    }
161236769Sobrien    /* we don't need these once we exec */
162321964Ssjg    (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC);
163321964Ssjg    (void)fcntl(pbm->filemon_fd, F_SETFD, FD_CLOEXEC);
164236769Sobrien}
165236769Sobrien
166236769Sobrien/*
167236769Sobrien * Read the build monitor output file and write records to the target's
168236769Sobrien * metadata file.
169236769Sobrien */
170321964Ssjgstatic int
171236769Sobrienfilemon_read(FILE *mfp, int fd)
172236769Sobrien{
173236769Sobrien    char buf[BUFSIZ];
174292068Ssjg    int n;
175321964Ssjg    int error;
176236769Sobrien
177236769Sobrien    /* Check if we're not writing to a meta data file.*/
178236769Sobrien    if (mfp == NULL) {
179236769Sobrien	if (fd >= 0)
180236769Sobrien	    close(fd);			/* not interested */
181321964Ssjg	return 0;
182236769Sobrien    }
183236769Sobrien    /* rewind */
184236769Sobrien    (void)lseek(fd, (off_t)0, SEEK_SET);
185236769Sobrien
186321964Ssjg    error = 0;
187292068Ssjg    fprintf(mfp, "\n-- filemon acquired metadata --\n");
188236769Sobrien
189292068Ssjg    while ((n = read(fd, buf, sizeof(buf))) > 0) {
190321964Ssjg	if ((int)fwrite(buf, 1, n, mfp) < n)
191321964Ssjg	    error = EIO;
192236769Sobrien    }
193236769Sobrien    fflush(mfp);
194321964Ssjg    if (close(fd) < 0)
195321964Ssjg	error = errno;
196321964Ssjg    return error;
197236769Sobrien}
198236769Sobrien#endif
199236769Sobrien
200236769Sobrien/*
201236769Sobrien * when realpath() fails,
202236769Sobrien * we use this, to clean up ./ and ../
203236769Sobrien */
204236769Sobrienstatic void
205236769Sobrieneat_dots(char *buf, size_t bufsz, int dots)
206236769Sobrien{
207236769Sobrien    char *cp;
208236769Sobrien    char *cp2;
209236769Sobrien    const char *eat;
210236769Sobrien    size_t eatlen;
211236769Sobrien
212236769Sobrien    switch (dots) {
213236769Sobrien    case 1:
214236769Sobrien	eat = "/./";
215236769Sobrien	eatlen = 2;
216236769Sobrien	break;
217236769Sobrien    case 2:
218236769Sobrien	eat = "/../";
219236769Sobrien	eatlen = 3;
220236769Sobrien	break;
221236769Sobrien    default:
222236769Sobrien	return;
223236769Sobrien    }
224236769Sobrien
225236769Sobrien    do {
226236769Sobrien	cp = strstr(buf, eat);
227236769Sobrien	if (cp) {
228236769Sobrien	    cp2 = cp + eatlen;
229236769Sobrien	    if (dots == 2 && cp > buf) {
230236769Sobrien		do {
231236769Sobrien		    cp--;
232236769Sobrien		} while (cp > buf && *cp != '/');
233236769Sobrien	    }
234236769Sobrien	    if (*cp == '/') {
235236769Sobrien		strlcpy(cp, cp2, bufsz - (cp - buf));
236236769Sobrien	    } else {
237236769Sobrien		return;			/* can't happen? */
238236769Sobrien	    }
239236769Sobrien	}
240236769Sobrien    } while (cp);
241236769Sobrien}
242236769Sobrien
243236769Sobrienstatic char *
244321964Ssjgmeta_name(char *mname, size_t mnamelen,
245236769Sobrien	  const char *dname,
246321964Ssjg	  const char *tname,
247321964Ssjg	  const char *cwd)
248236769Sobrien{
249236769Sobrien    char buf[MAXPATHLEN];
250236769Sobrien    char *rp;
251236769Sobrien    char *cp;
252236769Sobrien    char *tp;
253236769Sobrien
254236769Sobrien    /*
255236769Sobrien     * Weed out relative paths from the target file name.
256236769Sobrien     * We have to be careful though since if target is a
257236769Sobrien     * symlink, the result will be unstable.
258236769Sobrien     * So we use realpath() just to get the dirname, and leave the
259236769Sobrien     * basename as given to us.
260236769Sobrien     */
261236769Sobrien    if ((cp = strrchr(tname, '/'))) {
262321964Ssjg	if (cached_realpath(tname, buf)) {
263236769Sobrien	    if ((rp = strrchr(buf, '/'))) {
264236769Sobrien		rp++;
265236769Sobrien		cp++;
266236769Sobrien		if (strcmp(cp, rp) != 0)
267236769Sobrien		    strlcpy(rp, cp, sizeof(buf) - (rp - buf));
268236769Sobrien	    }
269236769Sobrien	    tname = buf;
270236769Sobrien	} else {
271236769Sobrien	    /*
272236769Sobrien	     * We likely have a directory which is about to be made.
273236769Sobrien	     * We pretend realpath() succeeded, to have a chance
274236769Sobrien	     * of generating the same meta file name that we will
275236769Sobrien	     * next time through.
276236769Sobrien	     */
277236769Sobrien	    if (tname[0] == '/') {
278236769Sobrien		strlcpy(buf, tname, sizeof(buf));
279236769Sobrien	    } else {
280236769Sobrien		snprintf(buf, sizeof(buf), "%s/%s", cwd, tname);
281236769Sobrien	    }
282236769Sobrien	    eat_dots(buf, sizeof(buf), 1);	/* ./ */
283236769Sobrien	    eat_dots(buf, sizeof(buf), 2);	/* ../ */
284236769Sobrien	    tname = buf;
285236769Sobrien	}
286236769Sobrien    }
287236769Sobrien    /* on some systems dirname may modify its arg */
288236769Sobrien    tp = bmake_strdup(tname);
289236769Sobrien    if (strcmp(dname, dirname(tp)) == 0)
290236769Sobrien	snprintf(mname, mnamelen, "%s.meta", tname);
291236769Sobrien    else {
292236769Sobrien	snprintf(mname, mnamelen, "%s/%s.meta", dname, tname);
293236769Sobrien
294236769Sobrien	/*
295236769Sobrien	 * Replace path separators in the file name after the
296236769Sobrien	 * current object directory path.
297236769Sobrien	 */
298236769Sobrien	cp = mname + strlen(dname) + 1;
299236769Sobrien
300236769Sobrien	while (*cp != '\0') {
301236769Sobrien	    if (*cp == '/')
302236769Sobrien		*cp = '_';
303236769Sobrien	    cp++;
304236769Sobrien	}
305236769Sobrien    }
306236769Sobrien    free(tp);
307236769Sobrien    return (mname);
308236769Sobrien}
309236769Sobrien
310236769Sobrien/*
311236769Sobrien * Return true if running ${.MAKE}
312236769Sobrien * Bypassed if target is flagged .MAKE
313236769Sobrien */
314236769Sobrienstatic int
315236769Sobrienis_submake(void *cmdp, void *gnp)
316236769Sobrien{
317236769Sobrien    static char *p_make = NULL;
318236769Sobrien    static int p_len;
319236769Sobrien    char  *cmd = cmdp;
320236769Sobrien    GNode *gn = gnp;
321236769Sobrien    char *mp = NULL;
322236769Sobrien    char *cp;
323236769Sobrien    char *cp2;
324236769Sobrien    int rc = 0;				/* keep looking */
325236769Sobrien
326236769Sobrien    if (!p_make) {
327236769Sobrien	p_make = Var_Value(".MAKE", gn, &cp);
328236769Sobrien	p_len = strlen(p_make);
329236769Sobrien    }
330236769Sobrien    cp = strchr(cmd, '$');
331236769Sobrien    if ((cp)) {
332321964Ssjg	mp = Var_Subst(NULL, cmd, gn, VARF_WANTRES);
333236769Sobrien	cmd = mp;
334236769Sobrien    }
335236769Sobrien    cp2 = strstr(cmd, p_make);
336236769Sobrien    if ((cp2)) {
337236769Sobrien	switch (cp2[p_len]) {
338236769Sobrien	case '\0':
339236769Sobrien	case ' ':
340236769Sobrien	case '\t':
341236769Sobrien	case '\n':
342236769Sobrien	    rc = 1;
343236769Sobrien	    break;
344236769Sobrien	}
345236769Sobrien	if (cp2 > cmd && rc > 0) {
346236769Sobrien	    switch (cp2[-1]) {
347236769Sobrien	    case ' ':
348236769Sobrien	    case '\t':
349236769Sobrien	    case '\n':
350236769Sobrien		break;
351236769Sobrien	    default:
352236769Sobrien		rc = 0;			/* no match */
353236769Sobrien		break;
354236769Sobrien	    }
355236769Sobrien	}
356236769Sobrien    }
357321964Ssjg    free(mp);
358236769Sobrien    return (rc);
359236769Sobrien}
360236769Sobrien
361236769Sobrientypedef struct meta_file_s {
362236769Sobrien    FILE *fp;
363236769Sobrien    GNode *gn;
364236769Sobrien} meta_file_t;
365236769Sobrien
366236769Sobrienstatic int
367236769SobrienprintCMD(void *cmdp, void *mfpp)
368236769Sobrien{
369236769Sobrien    meta_file_t *mfp = mfpp;
370236769Sobrien    char *cmd = cmdp;
371236769Sobrien    char *cp = NULL;
372236769Sobrien
373236769Sobrien    if (strchr(cmd, '$')) {
374321964Ssjg	cmd = cp = Var_Subst(NULL, cmd, mfp->gn, VARF_WANTRES);
375236769Sobrien    }
376236769Sobrien    fprintf(mfp->fp, "CMD %s\n", cmd);
377321964Ssjg    free(cp);
378236769Sobrien    return 0;
379236769Sobrien}
380236769Sobrien
381236769Sobrien/*
382236769Sobrien * Certain node types never get a .meta file
383236769Sobrien */
384236769Sobrien#define SKIP_META_TYPE(_type) do { \
385236769Sobrien    if ((gn->type & __CONCAT(OP_, _type))) {	\
386321964Ssjg	if (verbose) { \
387236769Sobrien	    fprintf(debug_file, "Skipping meta for %s: .%s\n", \
388236769Sobrien		    gn->name, __STRING(_type));		       \
389236769Sobrien	} \
390321964Ssjg	return FALSE; \
391236769Sobrien    } \
392236769Sobrien} while (0)
393236769Sobrien
394321964Ssjg
395321964Ssjg/*
396321964Ssjg * Do we need/want a .meta file ?
397321964Ssjg */
398321964Ssjgstatic Boolean
399321964Ssjgmeta_needed(GNode *gn, const char *dname,
400321964Ssjg	     char *objdir, int verbose)
401236769Sobrien{
402236769Sobrien    struct stat fs;
403236769Sobrien
404321964Ssjg    if (verbose)
405321964Ssjg	verbose = DEBUG(META);
406236769Sobrien
407236769Sobrien    /* This may be a phony node which we don't want meta data for... */
408236769Sobrien    /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */
409236769Sobrien    /* Or it may be explicitly flagged as .NOMETA */
410236769Sobrien    SKIP_META_TYPE(NOMETA);
411236769Sobrien    /* Unless it is explicitly flagged as .META */
412236769Sobrien    if (!(gn->type & OP_META)) {
413236769Sobrien	SKIP_META_TYPE(PHONY);
414236769Sobrien	SKIP_META_TYPE(SPECIAL);
415236769Sobrien	SKIP_META_TYPE(MAKE);
416236769Sobrien    }
417236769Sobrien
418236769Sobrien    /* Check if there are no commands to execute. */
419236769Sobrien    if (Lst_IsEmpty(gn->commands)) {
420321964Ssjg	if (verbose)
421236769Sobrien	    fprintf(debug_file, "Skipping meta for %s: no commands\n",
422236769Sobrien		    gn->name);
423321964Ssjg	return FALSE;
424236769Sobrien    }
425321964Ssjg    if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
426321964Ssjg	/* OP_SUBMAKE is a bit too aggressive */
427321964Ssjg	if (Lst_ForEach(gn->commands, is_submake, gn)) {
428321964Ssjg	    if (DEBUG(META))
429321964Ssjg		fprintf(debug_file, "Skipping meta for %s: .SUBMAKE\n",
430321964Ssjg			gn->name);
431321964Ssjg	    return FALSE;
432321964Ssjg	}
433321964Ssjg    }
434236769Sobrien
435321964Ssjg    /* The object directory may not exist. Check it.. */
436321964Ssjg    if (cached_stat(dname, &fs) != 0) {
437321964Ssjg	if (verbose)
438321964Ssjg	    fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n",
439321964Ssjg		    gn->name);
440321964Ssjg	return FALSE;
441321964Ssjg    }
442321964Ssjg
443236769Sobrien    /* make sure these are canonical */
444321964Ssjg    if (cached_realpath(dname, objdir))
445236769Sobrien	dname = objdir;
446236769Sobrien
447236769Sobrien    /* If we aren't in the object directory, don't create a meta file. */
448236769Sobrien    if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
449321964Ssjg	if (verbose)
450236769Sobrien	    fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n",
451236769Sobrien		    gn->name);
452321964Ssjg	return FALSE;
453236769Sobrien    }
454321964Ssjg    return TRUE;
455321964Ssjg}
456236769Sobrien
457321964Ssjg
458321964Ssjgstatic FILE *
459321964Ssjgmeta_create(BuildMon *pbm, GNode *gn)
460321964Ssjg{
461321964Ssjg    meta_file_t mf;
462321964Ssjg    char buf[MAXPATHLEN];
463321964Ssjg    char objdir[MAXPATHLEN];
464321964Ssjg    char **ptr;
465321964Ssjg    const char *dname;
466321964Ssjg    const char *tname;
467321964Ssjg    char *fname;
468321964Ssjg    const char *cp;
469321964Ssjg    char *p[4];				/* >= possible uses */
470321964Ssjg    int i;
471321964Ssjg
472321964Ssjg    mf.fp = NULL;
473321964Ssjg    i = 0;
474321964Ssjg
475321964Ssjg    dname = Var_Value(".OBJDIR", gn, &p[i++]);
476321964Ssjg    tname = Var_Value(TARGET, gn, &p[i++]);
477321964Ssjg
478321964Ssjg    /* if this succeeds objdir is realpath of dname */
479321964Ssjg    if (!meta_needed(gn, dname, objdir, TRUE))
480321964Ssjg	goto out;
481321964Ssjg    dname = objdir;
482321964Ssjg
483236769Sobrien    if (metaVerbose) {
484236769Sobrien	char *mp;
485236769Sobrien
486236769Sobrien	/* Describe the target we are building */
487321964Ssjg	mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, VARF_WANTRES);
488236769Sobrien	if (*mp)
489236769Sobrien	    fprintf(stdout, "%s\n", mp);
490236769Sobrien	free(mp);
491236769Sobrien    }
492236769Sobrien    /* Get the basename of the target */
493236769Sobrien    if ((cp = strrchr(tname, '/')) == NULL) {
494236769Sobrien	cp = tname;
495236769Sobrien    } else {
496236769Sobrien	cp++;
497236769Sobrien    }
498236769Sobrien
499236769Sobrien    fflush(stdout);
500236769Sobrien
501236769Sobrien    if (!writeMeta)
502236769Sobrien	/* Don't create meta data. */
503236769Sobrien	goto out;
504236769Sobrien
505321964Ssjg    fname = meta_name(pbm->meta_fname, sizeof(pbm->meta_fname),
506321964Ssjg		      dname, tname, objdir);
507236769Sobrien
508236769Sobrien#ifdef DEBUG_META_MODE
509236769Sobrien    if (DEBUG(META))
510236769Sobrien	fprintf(debug_file, "meta_create: %s\n", fname);
511236769Sobrien#endif
512236769Sobrien
513236769Sobrien    if ((mf.fp = fopen(fname, "w")) == NULL)
514236769Sobrien	err(1, "Could not open meta file '%s'", fname);
515236769Sobrien
516236769Sobrien    fprintf(mf.fp, "# Meta data file %s\n", fname);
517236769Sobrien
518236769Sobrien    mf.gn = gn;
519236769Sobrien
520236769Sobrien    Lst_ForEach(gn->commands, printCMD, &mf);
521236769Sobrien
522236769Sobrien    fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf)));
523236769Sobrien    fprintf(mf.fp, "TARGET %s\n", tname);
524236769Sobrien
525236769Sobrien    if (metaEnv) {
526236769Sobrien	for (ptr = environ; *ptr != NULL; ptr++)
527236769Sobrien	    fprintf(mf.fp, "ENV %s\n", *ptr);
528236769Sobrien    }
529236769Sobrien
530236769Sobrien    fprintf(mf.fp, "-- command output --\n");
531236769Sobrien    fflush(mf.fp);
532236769Sobrien
533236769Sobrien    Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
534236769Sobrien    Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL);
535236769Sobrien
536236769Sobrien    gn->type |= OP_META;		/* in case anyone wants to know */
537236769Sobrien    if (metaSilent) {
538236769Sobrien	    gn->type |= OP_SILENT;
539236769Sobrien    }
540236769Sobrien out:
541236769Sobrien    for (i--; i >= 0; i--) {
542321964Ssjg	free(p[i]);
543236769Sobrien    }
544236769Sobrien
545236769Sobrien    return (mf.fp);
546236769Sobrien}
547236769Sobrien
548236769Sobrienstatic Boolean
549236769SobrienboolValue(char *s)
550236769Sobrien{
551236769Sobrien    switch(*s) {
552236769Sobrien    case '0':
553236769Sobrien    case 'N':
554236769Sobrien    case 'n':
555236769Sobrien    case 'F':
556236769Sobrien    case 'f':
557236769Sobrien	return FALSE;
558236769Sobrien    }
559236769Sobrien    return TRUE;
560236769Sobrien}
561236769Sobrien
562249033Ssjg/*
563249033Ssjg * Initialization we need before reading makefiles.
564249033Ssjg */
565236769Sobrienvoid
566250837Ssjgmeta_init(void)
567236769Sobrien{
568249033Ssjg#ifdef USE_FILEMON
569249033Ssjg	/* this allows makefiles to test if we have filemon support */
570249033Ssjg	Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0);
571249033Ssjg#endif
572249033Ssjg}
573249033Ssjg
574249033Ssjg
575321964Ssjg#define get_mode_bf(bf, token) \
576321964Ssjg    if ((cp = strstr(make_mode, token))) \
577321964Ssjg	bf = boolValue(&cp[sizeof(token) - 1])
578321964Ssjg
579249033Ssjg/*
580249033Ssjg * Initialization we need after reading makefiles.
581249033Ssjg */
582249033Ssjgvoid
583249033Ssjgmeta_mode_init(const char *make_mode)
584249033Ssjg{
585236769Sobrien    static int once = 0;
586236769Sobrien    char *cp;
587236769Sobrien
588236769Sobrien    useMeta = TRUE;
589236769Sobrien    useFilemon = TRUE;
590236769Sobrien    writeMeta = TRUE;
591236769Sobrien
592236769Sobrien    if (make_mode) {
593236769Sobrien	if (strstr(make_mode, "env"))
594236769Sobrien	    metaEnv = TRUE;
595236769Sobrien	if (strstr(make_mode, "verb"))
596236769Sobrien	    metaVerbose = TRUE;
597236769Sobrien	if (strstr(make_mode, "read"))
598236769Sobrien	    writeMeta = FALSE;
599236769Sobrien	if (strstr(make_mode, "nofilemon"))
600236769Sobrien	    useFilemon = FALSE;
601236769Sobrien	if (strstr(make_mode, "ignore-cmd"))
602236769Sobrien	    metaIgnoreCMDs = TRUE;
603321964Ssjg	if (useFilemon)
604321964Ssjg	    get_mode_bf(filemonMissing, "missing-filemon=");
605321964Ssjg	get_mode_bf(metaCurdirOk, "curdirok=");
606321964Ssjg	get_mode_bf(metaMissing, "missing-meta=");
607321964Ssjg	get_mode_bf(metaSilent, "silent=");
608236769Sobrien    }
609236769Sobrien    if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) {
610236769Sobrien	/*
611236769Sobrien	 * The default value for MAKE_META_PREFIX
612236769Sobrien	 * prints the absolute path of the target.
613236769Sobrien	 * This works be cause :H will generate '.' if there is no /
614236769Sobrien	 * and :tA will resolve that to cwd.
615236769Sobrien	 */
616236769Sobrien	Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0);
617236769Sobrien    }
618236769Sobrien    if (once)
619236769Sobrien	return;
620236769Sobrien    once = 1;
621236769Sobrien    memset(&Mybm, 0, sizeof(Mybm));
622236769Sobrien    /*
623236769Sobrien     * We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
624236769Sobrien     */
625236769Sobrien    metaBailiwick = Lst_Init(FALSE);
626321964Ssjg    metaBailiwickStr = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}",
627321964Ssjg	VAR_GLOBAL, VARF_WANTRES);
628321964Ssjg    if (metaBailiwickStr) {
629321964Ssjg	str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
630236769Sobrien    }
631253883Ssjg    /*
632253883Ssjg     * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
633253883Ssjg     */
634253883Ssjg    metaIgnorePaths = Lst_Init(FALSE);
635253883Ssjg    Var_Append(MAKE_META_IGNORE_PATHS,
636253883Ssjg	       "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
637321964Ssjg    metaIgnorePathsStr = Var_Subst(NULL,
638292068Ssjg		   "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL,
639321964Ssjg		   VARF_WANTRES);
640321964Ssjg    if (metaIgnorePathsStr) {
641321964Ssjg	str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL);
642253883Ssjg    }
643321964Ssjg
644321964Ssjg    /*
645321964Ssjg     * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
646321964Ssjg     */
647321964Ssjg    cp = NULL;
648321964Ssjg    if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &cp)) {
649321964Ssjg	metaIgnorePatterns = TRUE;
650321964Ssjg	free(cp);
651321964Ssjg    }
652321964Ssjg    cp = NULL;
653321964Ssjg    if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &cp)) {
654321964Ssjg	metaIgnoreFilter = TRUE;
655321964Ssjg	free(cp);
656321964Ssjg    }
657236769Sobrien}
658236769Sobrien
659236769Sobrien/*
660236769Sobrien * In each case below we allow for job==NULL
661236769Sobrien */
662236769Sobrienvoid
663236769Sobrienmeta_job_start(Job *job, GNode *gn)
664236769Sobrien{
665236769Sobrien    BuildMon *pbm;
666236769Sobrien
667236769Sobrien    if (job != NULL) {
668236769Sobrien	pbm = &job->bm;
669236769Sobrien    } else {
670236769Sobrien	pbm = &Mybm;
671236769Sobrien    }
672236769Sobrien    pbm->mfp = meta_create(pbm, gn);
673236769Sobrien#ifdef USE_FILEMON_ONCE
674236769Sobrien    /* compat mode we open the filemon dev once per command */
675236769Sobrien    if (job == NULL)
676236769Sobrien	return;
677236769Sobrien#endif
678236769Sobrien#ifdef USE_FILEMON
679236769Sobrien    if (pbm->mfp != NULL && useFilemon) {
680236769Sobrien	filemon_open(pbm);
681236769Sobrien    } else {
682236769Sobrien	pbm->mon_fd = pbm->filemon_fd = -1;
683236769Sobrien    }
684236769Sobrien#endif
685236769Sobrien}
686236769Sobrien
687236769Sobrien/*
688236769Sobrien * The child calls this before doing anything.
689236769Sobrien * It does not disturb our state.
690236769Sobrien */
691236769Sobrienvoid
692236769Sobrienmeta_job_child(Job *job)
693236769Sobrien{
694236769Sobrien#ifdef USE_FILEMON
695236769Sobrien    BuildMon *pbm;
696236769Sobrien
697236769Sobrien    if (job != NULL) {
698236769Sobrien	pbm = &job->bm;
699236769Sobrien    } else {
700236769Sobrien	pbm = &Mybm;
701236769Sobrien    }
702292068Ssjg    if (pbm->mfp != NULL) {
703292068Ssjg	close(fileno(pbm->mfp));
704292068Ssjg	if (useFilemon) {
705292068Ssjg	    pid_t pid;
706292068Ssjg
707292068Ssjg	    pid = getpid();
708292068Ssjg	    if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) {
709292068Ssjg		err(1, "Could not set filemon pid!");
710292068Ssjg	    }
711236769Sobrien	}
712236769Sobrien    }
713236769Sobrien#endif
714236769Sobrien}
715236769Sobrien
716236769Sobrienvoid
717236769Sobrienmeta_job_error(Job *job, GNode *gn, int flags, int status)
718236769Sobrien{
719236769Sobrien    char cwd[MAXPATHLEN];
720236769Sobrien    BuildMon *pbm;
721236769Sobrien
722236769Sobrien    if (job != NULL) {
723236769Sobrien	pbm = &job->bm;
724236769Sobrien	if (!gn)
725236769Sobrien	    gn = job->node;
726321964Ssjg    } else {
727236769Sobrien	pbm = &Mybm;
728236769Sobrien    }
729236769Sobrien    if (pbm->mfp != NULL) {
730321964Ssjg	fprintf(pbm->mfp, "\n*** Error code %d%s\n",
731236769Sobrien		status,
732236769Sobrien		(flags & JOB_IGNERR) ?
733236769Sobrien		"(ignored)" : "");
734236769Sobrien    }
735236769Sobrien    if (gn) {
736236769Sobrien	Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0);
737236769Sobrien    }
738236769Sobrien    getcwd(cwd, sizeof(cwd));
739236769Sobrien    Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0);
740321964Ssjg    if (pbm->meta_fname[0]) {
741236769Sobrien	Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0);
742236769Sobrien    }
743236769Sobrien    meta_job_finish(job);
744236769Sobrien}
745236769Sobrien
746236769Sobrienvoid
747236769Sobrienmeta_job_output(Job *job, char *cp, const char *nl)
748236769Sobrien{
749236769Sobrien    BuildMon *pbm;
750236769Sobrien
751236769Sobrien    if (job != NULL) {
752236769Sobrien	pbm = &job->bm;
753236769Sobrien    } else {
754236769Sobrien	pbm = &Mybm;
755236769Sobrien    }
756236769Sobrien    if (pbm->mfp != NULL) {
757236769Sobrien	if (metaVerbose) {
758236769Sobrien	    static char *meta_prefix = NULL;
759236769Sobrien	    static int meta_prefix_len;
760236769Sobrien
761236769Sobrien	    if (!meta_prefix) {
762236769Sobrien		char *cp2;
763236769Sobrien
764292068Ssjg		meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}",
765321964Ssjg					VAR_GLOBAL, VARF_WANTRES);
766236769Sobrien		if ((cp2 = strchr(meta_prefix, '$')))
767236769Sobrien		    meta_prefix_len = cp2 - meta_prefix;
768236769Sobrien		else
769236769Sobrien		    meta_prefix_len = strlen(meta_prefix);
770236769Sobrien	    }
771236769Sobrien	    if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) {
772236769Sobrien		cp = strchr(cp+1, '\n');
773236769Sobrien		if (!cp++)
774236769Sobrien		    return;
775236769Sobrien	    }
776236769Sobrien	}
777236769Sobrien	fprintf(pbm->mfp, "%s%s", cp, nl);
778236769Sobrien    }
779236769Sobrien}
780236769Sobrien
781321964Ssjgint
782236769Sobrienmeta_cmd_finish(void *pbmp)
783236769Sobrien{
784321964Ssjg    int error = 0;
785236769Sobrien    BuildMon *pbm = pbmp;
786321964Ssjg    int x;
787236769Sobrien
788236769Sobrien    if (!pbm)
789236769Sobrien	pbm = &Mybm;
790236769Sobrien
791321964Ssjg#ifdef USE_FILEMON
792236769Sobrien    if (pbm->filemon_fd >= 0) {
793321964Ssjg	if (close(pbm->filemon_fd) < 0)
794321964Ssjg	    error = errno;
795321964Ssjg	x = filemon_read(pbm->mfp, pbm->mon_fd);
796321964Ssjg	if (error == 0 && x != 0)
797321964Ssjg	    error = x;
798236769Sobrien	pbm->filemon_fd = pbm->mon_fd = -1;
799321964Ssjg    } else
800236769Sobrien#endif
801321964Ssjg	fprintf(pbm->mfp, "\n");	/* ensure end with newline */
802321964Ssjg    return error;
803236769Sobrien}
804236769Sobrien
805321964Ssjgint
806236769Sobrienmeta_job_finish(Job *job)
807236769Sobrien{
808236769Sobrien    BuildMon *pbm;
809321964Ssjg    int error = 0;
810321964Ssjg    int x;
811236769Sobrien
812236769Sobrien    if (job != NULL) {
813236769Sobrien	pbm = &job->bm;
814236769Sobrien    } else {
815236769Sobrien	pbm = &Mybm;
816236769Sobrien    }
817236769Sobrien    if (pbm->mfp != NULL) {
818321964Ssjg	error = meta_cmd_finish(pbm);
819321964Ssjg	x = fclose(pbm->mfp);
820321964Ssjg	if (error == 0 && x != 0)
821321964Ssjg	    error = errno;
822236769Sobrien	pbm->mfp = NULL;
823236769Sobrien	pbm->meta_fname[0] = '\0';
824236769Sobrien    }
825321964Ssjg    return error;
826236769Sobrien}
827236769Sobrien
828321964Ssjgvoid
829321964Ssjgmeta_finish(void)
830321964Ssjg{
831321964Ssjg    Lst_Destroy(metaBailiwick, NULL);
832321964Ssjg    free(metaBailiwickStr);
833321964Ssjg    Lst_Destroy(metaIgnorePaths, NULL);
834321964Ssjg    free(metaIgnorePathsStr);
835321964Ssjg}
836321964Ssjg
837236769Sobrien/*
838236769Sobrien * Fetch a full line from fp - growing bufp if needed
839236769Sobrien * Return length in bufp.
840236769Sobrien */
841236769Sobrienstatic int
842236769SobrienfgetLine(char **bufp, size_t *szp, int o, FILE *fp)
843236769Sobrien{
844236769Sobrien    char *buf = *bufp;
845236769Sobrien    size_t bufsz = *szp;
846236769Sobrien    struct stat fs;
847236769Sobrien    int x;
848236769Sobrien
849236769Sobrien    if (fgets(&buf[o], bufsz - o, fp) != NULL) {
850236769Sobrien    check_newline:
851236769Sobrien	x = o + strlen(&buf[o]);
852236769Sobrien	if (buf[x - 1] == '\n')
853236769Sobrien	    return x;
854236769Sobrien	/*
855236769Sobrien	 * We need to grow the buffer.
856236769Sobrien	 * The meta file can give us a clue.
857236769Sobrien	 */
858236769Sobrien	if (fstat(fileno(fp), &fs) == 0) {
859236769Sobrien	    size_t newsz;
860236769Sobrien	    char *p;
861236769Sobrien
862236769Sobrien	    newsz = ROUNDUP((fs.st_size / 2), BUFSIZ);
863236769Sobrien	    if (newsz <= bufsz)
864236769Sobrien		newsz = ROUNDUP(fs.st_size, BUFSIZ);
865321964Ssjg	    if (newsz <= bufsz)
866321964Ssjg		return x;		/* truncated */
867236769Sobrien	    if (DEBUG(META))
868236769Sobrien		fprintf(debug_file, "growing buffer %u -> %u\n",
869236769Sobrien			(unsigned)bufsz, (unsigned)newsz);
870236769Sobrien	    p = bmake_realloc(buf, newsz);
871236769Sobrien	    if (p) {
872236769Sobrien		*bufp = buf = p;
873236769Sobrien		*szp = bufsz = newsz;
874236769Sobrien		/* fetch the rest */
875236769Sobrien		if (!fgets(&buf[x], bufsz - x, fp))
876236769Sobrien		    return x;		/* truncated! */
877236769Sobrien		goto check_newline;
878236769Sobrien	    }
879236769Sobrien	}
880236769Sobrien    }
881236769Sobrien    return 0;
882236769Sobrien}
883236769Sobrien
884321964Ssjg/* Lst_ForEach wants 1 to stop search */
885236769Sobrienstatic int
886236769Sobrienprefix_match(void *p, void *q)
887236769Sobrien{
888236769Sobrien    const char *prefix = p;
889236769Sobrien    const char *path = q;
890236769Sobrien    size_t n = strlen(prefix);
891236769Sobrien
892236769Sobrien    return (0 == strncmp(path, prefix, n));
893236769Sobrien}
894236769Sobrien
895321964Ssjg/*
896321964Ssjg * looking for exact or prefix/ match to
897321964Ssjg * Lst_Find wants 0 to stop search
898321964Ssjg */
899236769Sobrienstatic int
900321964Ssjgpath_match(const void *p, const void *q)
901321964Ssjg{
902321964Ssjg    const char *prefix = q;
903321964Ssjg    const char *path = p;
904321964Ssjg    size_t n = strlen(prefix);
905321964Ssjg    int rc;
906321964Ssjg
907321964Ssjg    if ((rc = strncmp(path, prefix, n)) == 0) {
908321964Ssjg	switch (path[n]) {
909321964Ssjg	case '\0':
910321964Ssjg	case '/':
911321964Ssjg	    break;
912321964Ssjg	default:
913321964Ssjg	    rc = 1;
914321964Ssjg	    break;
915321964Ssjg	}
916321964Ssjg    }
917321964Ssjg    return rc;
918321964Ssjg}
919321964Ssjg
920321964Ssjg/* Lst_Find wants 0 to stop search */
921321964Ssjgstatic int
922236769Sobrienstring_match(const void *p, const void *q)
923236769Sobrien{
924236769Sobrien    const char *p1 = p;
925236769Sobrien    const char *p2 = q;
926236769Sobrien
927236769Sobrien    return strcmp(p1, p2);
928236769Sobrien}
929236769Sobrien
930236769Sobrien
931321964Ssjgstatic int
932321964Ssjgmeta_ignore(GNode *gn, const char *p)
933321964Ssjg{
934321964Ssjg    char fname[MAXPATHLEN];
935321964Ssjg
936321964Ssjg    if (p == NULL)
937321964Ssjg	return TRUE;
938321964Ssjg
939321964Ssjg    if (*p == '/') {
940321964Ssjg	cached_realpath(p, fname); /* clean it up */
941321964Ssjg	if (Lst_ForEach(metaIgnorePaths, prefix_match, fname)) {
942321964Ssjg#ifdef DEBUG_META_MODE
943321964Ssjg	    if (DEBUG(META))
944321964Ssjg		fprintf(debug_file, "meta_oodate: ignoring path: %s\n",
945321964Ssjg			p);
946321964Ssjg#endif
947321964Ssjg	    return TRUE;
948321964Ssjg	}
949321964Ssjg    }
950321964Ssjg
951321964Ssjg    if (metaIgnorePatterns) {
952321964Ssjg	char *pm;
953321964Ssjg
954321964Ssjg	Var_Set(".p.", p, gn, 0);
955321964Ssjg	pm = Var_Subst(NULL,
956321964Ssjg		       "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}",
957321964Ssjg		       gn, VARF_WANTRES);
958321964Ssjg	if (*pm) {
959321964Ssjg#ifdef DEBUG_META_MODE
960321964Ssjg	    if (DEBUG(META))
961321964Ssjg		fprintf(debug_file, "meta_oodate: ignoring pattern: %s\n",
962321964Ssjg			p);
963321964Ssjg#endif
964321964Ssjg	    free(pm);
965321964Ssjg	    return TRUE;
966321964Ssjg	}
967321964Ssjg	free(pm);
968321964Ssjg    }
969321964Ssjg
970321964Ssjg    if (metaIgnoreFilter) {
971321964Ssjg	char *fm;
972321964Ssjg
973321964Ssjg	/* skip if filter result is empty */
974321964Ssjg	snprintf(fname, sizeof(fname),
975321964Ssjg		 "${%s:L:${%s:ts:}}",
976321964Ssjg		 p, MAKE_META_IGNORE_FILTER);
977321964Ssjg	fm = Var_Subst(NULL, fname, gn, VARF_WANTRES);
978321964Ssjg	if (*fm == '\0') {
979321964Ssjg#ifdef DEBUG_META_MODE
980321964Ssjg	    if (DEBUG(META))
981321964Ssjg		fprintf(debug_file, "meta_oodate: ignoring filtered: %s\n",
982321964Ssjg			p);
983321964Ssjg#endif
984321964Ssjg	    free(fm);
985321964Ssjg	    return TRUE;
986321964Ssjg	}
987321964Ssjg	free(fm);
988321964Ssjg    }
989321964Ssjg    return FALSE;
990321964Ssjg}
991321964Ssjg
992236769Sobrien/*
993236769Sobrien * When running with 'meta' functionality, a target can be out-of-date
994292068Ssjg * if any of the references in its meta data file is more recent.
995236769Sobrien * We have to track the latestdir on a per-process basis.
996236769Sobrien */
997292068Ssjg#define LCWD_VNAME_FMT ".meta.%d.lcwd"
998236769Sobrien#define LDIR_VNAME_FMT ".meta.%d.ldir"
999236769Sobrien
1000236769Sobrien/*
1001236769Sobrien * It is possible that a .meta file is corrupted,
1002236769Sobrien * if we detect this we want to reproduce it.
1003236769Sobrien * Setting oodate TRUE will have that effect.
1004236769Sobrien */
1005236769Sobrien#define CHECK_VALID_META(p) if (!(p && *p)) { \
1006236769Sobrien    warnx("%s: %d: malformed", fname, lineno); \
1007236769Sobrien    oodate = TRUE; \
1008236769Sobrien    continue; \
1009236769Sobrien    }
1010236769Sobrien
1011276305Sngie#define DEQUOTE(p) if (*p == '\'') {	\
1012276305Sngie    char *ep; \
1013276305Sngie    p++; \
1014276305Sngie    if ((ep = strchr(p, '\''))) \
1015276305Sngie	*ep = '\0'; \
1016276305Sngie    }
1017276305Sngie
1018236769SobrienBoolean
1019236769Sobrienmeta_oodate(GNode *gn, Boolean oodate)
1020236769Sobrien{
1021236769Sobrien    static char *tmpdir = NULL;
1022236769Sobrien    static char cwd[MAXPATHLEN];
1023292068Ssjg    char lcwd_vname[64];
1024236769Sobrien    char ldir_vname[64];
1025292068Ssjg    char lcwd[MAXPATHLEN];
1026236769Sobrien    char latestdir[MAXPATHLEN];
1027236769Sobrien    char fname[MAXPATHLEN];
1028236769Sobrien    char fname1[MAXPATHLEN];
1029236769Sobrien    char fname2[MAXPATHLEN];
1030292068Ssjg    char fname3[MAXPATHLEN];
1031321964Ssjg    const char *dname;
1032321964Ssjg    const char *tname;
1033236769Sobrien    char *p;
1034236769Sobrien    char *cp;
1035276305Sngie    char *link_src;
1036276305Sngie    char *move_target;
1037236769Sobrien    static size_t cwdlen = 0;
1038236769Sobrien    static size_t tmplen = 0;
1039236769Sobrien    FILE *fp;
1040246223Ssjg    Boolean needOODATE = FALSE;
1041236769Sobrien    Lst missingFiles;
1042321964Ssjg    char *pa[4];			/* >= possible uses */
1043321964Ssjg    int i;
1044321964Ssjg    int have_filemon = FALSE;
1045321964Ssjg
1046236769Sobrien    if (oodate)
1047236769Sobrien	return oodate;		/* we're done */
1048236769Sobrien
1049321964Ssjg    i = 0;
1050321964Ssjg
1051321964Ssjg    dname = Var_Value(".OBJDIR", gn, &pa[i++]);
1052321964Ssjg    tname = Var_Value(TARGET, gn, &pa[i++]);
1053321964Ssjg
1054321964Ssjg    /* if this succeeds fname3 is realpath of dname */
1055321964Ssjg    if (!meta_needed(gn, dname, fname3, FALSE))
1056321964Ssjg	goto oodate_out;
1057321964Ssjg    dname = fname3;
1058321964Ssjg
1059236769Sobrien    missingFiles = Lst_Init(FALSE);
1060236769Sobrien
1061236769Sobrien    /*
1062236769Sobrien     * We need to check if the target is out-of-date. This includes
1063236769Sobrien     * checking if the expanded command has changed. This in turn
1064236769Sobrien     * requires that all variables are set in the same way that they
1065236769Sobrien     * would be if the target needs to be re-built.
1066236769Sobrien     */
1067236769Sobrien    Make_DoAllVar(gn);
1068236769Sobrien
1069321964Ssjg    meta_name(fname, sizeof(fname), dname, tname, dname);
1070236769Sobrien
1071236769Sobrien#ifdef DEBUG_META_MODE
1072236769Sobrien    if (DEBUG(META))
1073236769Sobrien	fprintf(debug_file, "meta_oodate: %s\n", fname);
1074236769Sobrien#endif
1075236769Sobrien
1076236769Sobrien    if ((fp = fopen(fname, "r")) != NULL) {
1077236769Sobrien	static char *buf = NULL;
1078236769Sobrien	static size_t bufsz;
1079236769Sobrien	int lineno = 0;
1080236769Sobrien	int lastpid = 0;
1081236769Sobrien	int pid;
1082236769Sobrien	int x;
1083236769Sobrien	LstNode ln;
1084236769Sobrien	struct stat fs;
1085236769Sobrien
1086236769Sobrien	if (!buf) {
1087236769Sobrien	    bufsz = 8 * BUFSIZ;
1088236769Sobrien	    buf = bmake_malloc(bufsz);
1089236769Sobrien	}
1090236769Sobrien
1091236769Sobrien	if (!cwdlen) {
1092236769Sobrien	    if (getcwd(cwd, sizeof(cwd)) == NULL)
1093236769Sobrien		err(1, "Could not get current working directory");
1094236769Sobrien	    cwdlen = strlen(cwd);
1095236769Sobrien	}
1096292068Ssjg	strlcpy(lcwd, cwd, sizeof(lcwd));
1097292068Ssjg	strlcpy(latestdir, cwd, sizeof(latestdir));
1098236769Sobrien
1099236769Sobrien	if (!tmpdir) {
1100236769Sobrien	    tmpdir = getTmpdir();
1101236769Sobrien	    tmplen = strlen(tmpdir);
1102236769Sobrien	}
1103236769Sobrien
1104236769Sobrien	/* we want to track all the .meta we read */
1105236769Sobrien	Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
1106236769Sobrien
1107236769Sobrien	ln = Lst_First(gn->commands);
1108236769Sobrien	while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
1109236769Sobrien	    lineno++;
1110236769Sobrien	    if (buf[x - 1] == '\n')
1111236769Sobrien		buf[x - 1] = '\0';
1112236769Sobrien	    else {
1113236769Sobrien		warnx("%s: %d: line truncated at %u", fname, lineno, x);
1114236769Sobrien		oodate = TRUE;
1115236769Sobrien		break;
1116236769Sobrien	    }
1117276305Sngie	    link_src = NULL;
1118276305Sngie	    move_target = NULL;
1119236769Sobrien	    /* Find the start of the build monitor section. */
1120321964Ssjg	    if (!have_filemon) {
1121236769Sobrien		if (strncmp(buf, "-- filemon", 10) == 0) {
1122321964Ssjg		    have_filemon = TRUE;
1123236769Sobrien		    continue;
1124236769Sobrien		}
1125236769Sobrien		if (strncmp(buf, "# buildmon", 10) == 0) {
1126321964Ssjg		    have_filemon = TRUE;
1127236769Sobrien		    continue;
1128236769Sobrien		}
1129236769Sobrien	    }
1130236769Sobrien
1131236769Sobrien	    /* Delimit the record type. */
1132236769Sobrien	    p = buf;
1133236769Sobrien#ifdef DEBUG_META_MODE
1134236769Sobrien	    if (DEBUG(META))
1135236769Sobrien		fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf);
1136236769Sobrien#endif
1137236769Sobrien	    strsep(&p, " ");
1138321964Ssjg	    if (have_filemon) {
1139236769Sobrien		/*
1140236769Sobrien		 * We are in the 'filemon' output section.
1141236769Sobrien		 * Each record from filemon follows the general form:
1142236769Sobrien		 *
1143236769Sobrien		 * <key> <pid> <data>
1144236769Sobrien		 *
1145236769Sobrien		 * Where:
1146236769Sobrien		 * <key> is a single letter, denoting the syscall.
1147236769Sobrien		 * <pid> is the process that made the syscall.
1148236769Sobrien		 * <data> is the arguments (of interest).
1149236769Sobrien		 */
1150236769Sobrien		switch(buf[0]) {
1151236769Sobrien		case '#':		/* comment */
1152236769Sobrien		case 'V':		/* version */
1153236769Sobrien		    break;
1154236769Sobrien		default:
1155236769Sobrien		    /*
1156236769Sobrien		     * We need to track pathnames per-process.
1157236769Sobrien		     *
1158236769Sobrien		     * Each process run by make, starts off in the 'CWD'
1159236769Sobrien		     * recorded in the .meta file, if it chdirs ('C')
1160236769Sobrien		     * elsewhere we need to track that - but only for
1161236769Sobrien		     * that process.  If it forks ('F'), we initialize
1162236769Sobrien		     * the child to have the same cwd as its parent.
1163236769Sobrien		     *
1164236769Sobrien		     * We also need to track the 'latestdir' of
1165236769Sobrien		     * interest.  This is usually the same as cwd, but
1166236769Sobrien		     * not if a process is reading directories.
1167236769Sobrien		     *
1168236769Sobrien		     * Each time we spot a different process ('pid')
1169236769Sobrien		     * we save the current value of 'latestdir' in a
1170236769Sobrien		     * variable qualified by 'lastpid', and
1171236769Sobrien		     * re-initialize 'latestdir' to any pre-saved
1172236769Sobrien		     * value for the current 'pid' and 'CWD' if none.
1173236769Sobrien		     */
1174236769Sobrien		    CHECK_VALID_META(p);
1175236769Sobrien		    pid = atoi(p);
1176236769Sobrien		    if (pid > 0 && pid != lastpid) {
1177236769Sobrien			char *ldir;
1178236769Sobrien			char *tp;
1179236769Sobrien
1180236769Sobrien			if (lastpid > 0) {
1181292068Ssjg			    /* We need to remember these. */
1182292068Ssjg			    Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0);
1183236769Sobrien			    Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0);
1184236769Sobrien			}
1185292068Ssjg			snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid);
1186236769Sobrien			snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid);
1187236769Sobrien			lastpid = pid;
1188236769Sobrien			ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp);
1189236769Sobrien			if (ldir) {
1190236769Sobrien			    strlcpy(latestdir, ldir, sizeof(latestdir));
1191321964Ssjg			    free(tp);
1192292068Ssjg			}
1193292068Ssjg			ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp);
1194292068Ssjg			if (ldir) {
1195292068Ssjg			    strlcpy(lcwd, ldir, sizeof(lcwd));
1196321964Ssjg			    free(tp);
1197292068Ssjg			}
1198236769Sobrien		    }
1199236769Sobrien		    /* Skip past the pid. */
1200236769Sobrien		    if (strsep(&p, " ") == NULL)
1201236769Sobrien			continue;
1202236769Sobrien#ifdef DEBUG_META_MODE
1203236769Sobrien		    if (DEBUG(META))
1204292068Ssjg			    fprintf(debug_file, "%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
1205292068Ssjg				    fname, lineno,
1206292068Ssjg				    pid, buf[0], cwd, lcwd, latestdir);
1207236769Sobrien#endif
1208236769Sobrien		    break;
1209236769Sobrien		}
1210236769Sobrien
1211236769Sobrien		CHECK_VALID_META(p);
1212236769Sobrien
1213236769Sobrien		/* Process according to record type. */
1214236769Sobrien		switch (buf[0]) {
1215236769Sobrien		case 'X':		/* eXit */
1216292068Ssjg		    Var_Delete(lcwd_vname, VAR_GLOBAL);
1217236769Sobrien		    Var_Delete(ldir_vname, VAR_GLOBAL);
1218236769Sobrien		    lastpid = 0;	/* no need to save ldir_vname */
1219236769Sobrien		    break;
1220236769Sobrien
1221236769Sobrien		case 'F':		/* [v]Fork */
1222236769Sobrien		    {
1223236769Sobrien			char cldir[64];
1224236769Sobrien			int child;
1225236769Sobrien
1226236769Sobrien			child = atoi(p);
1227236769Sobrien			if (child > 0) {
1228292068Ssjg			    snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child);
1229292068Ssjg			    Var_Set(cldir, lcwd, VAR_GLOBAL, 0);
1230236769Sobrien			    snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child);
1231236769Sobrien			    Var_Set(cldir, latestdir, VAR_GLOBAL, 0);
1232292068Ssjg#ifdef DEBUG_META_MODE
1233292068Ssjg			    if (DEBUG(META))
1234292068Ssjg				    fprintf(debug_file, "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
1235292068Ssjg					    fname, lineno,
1236292068Ssjg					    child, cwd, lcwd, latestdir);
1237292068Ssjg#endif
1238236769Sobrien			}
1239236769Sobrien		    }
1240236769Sobrien		    break;
1241236769Sobrien
1242236769Sobrien		case 'C':		/* Chdir */
1243292068Ssjg		    /* Update lcwd and latest directory. */
1244292068Ssjg		    strlcpy(latestdir, p, sizeof(latestdir));
1245292068Ssjg		    strlcpy(lcwd, p, sizeof(lcwd));
1246292068Ssjg		    Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0);
1247292068Ssjg		    Var_Set(ldir_vname, lcwd, VAR_GLOBAL, 0);
1248292068Ssjg#ifdef DEBUG_META_MODE
1249292068Ssjg		    if (DEBUG(META))
1250292068Ssjg			fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd);
1251292068Ssjg#endif
1252236769Sobrien		    break;
1253236769Sobrien
1254236769Sobrien		case 'M':		/* renaMe */
1255276305Sngie		    /*
1256276305Sngie		     * For 'M'oves we want to check
1257276305Sngie		     * the src as for 'R'ead
1258276305Sngie		     * and the target as for 'W'rite.
1259276305Sngie		     */
1260276305Sngie		    cp = p;		/* save this for a second */
1261276305Sngie		    /* now get target */
1262276305Sngie		    if (strsep(&p, " ") == NULL)
1263276305Sngie			continue;
1264276305Sngie		    CHECK_VALID_META(p);
1265276305Sngie		    move_target = p;
1266276305Sngie		    p = cp;
1267236769Sobrien		    /* 'L' and 'M' put single quotes around the args */
1268276305Sngie		    DEQUOTE(p);
1269276305Sngie		    DEQUOTE(move_target);
1270236769Sobrien		    /* FALLTHROUGH */
1271236769Sobrien		case 'D':		/* unlink */
1272236769Sobrien		    if (*p == '/' && !Lst_IsEmpty(missingFiles)) {
1273321964Ssjg			/* remove any missingFiles entries that match p */
1274321964Ssjg			if ((ln = Lst_Find(missingFiles, p,
1275321964Ssjg					   path_match)) != NULL) {
1276321964Ssjg			    LstNode nln;
1277321964Ssjg			    char *tp;
1278321964Ssjg
1279321964Ssjg			    do {
1280321964Ssjg				nln = Lst_FindFrom(missingFiles, Lst_Succ(ln),
1281321964Ssjg						   p, path_match);
1282321964Ssjg				tp = Lst_Datum(ln);
1283321964Ssjg				Lst_Remove(missingFiles, ln);
1284321964Ssjg				free(tp);
1285321964Ssjg			    } while ((ln = nln) != NULL);
1286236769Sobrien			}
1287236769Sobrien		    }
1288276305Sngie		    if (buf[0] == 'M') {
1289276305Sngie			/* the target of the mv is a file 'W'ritten */
1290276305Sngie#ifdef DEBUG_META_MODE
1291276305Sngie			if (DEBUG(META))
1292276305Sngie			    fprintf(debug_file, "meta_oodate: M %s -> %s\n",
1293276305Sngie				    p, move_target);
1294276305Sngie#endif
1295276305Sngie			p = move_target;
1296276305Sngie			goto check_write;
1297276305Sngie		    }
1298236769Sobrien		    break;
1299236769Sobrien		case 'L':		/* Link */
1300276305Sngie		    /*
1301276305Sngie		     * For 'L'inks check
1302276305Sngie		     * the src as for 'R'ead
1303276305Sngie		     * and the target as for 'W'rite.
1304276305Sngie		     */
1305276305Sngie		    link_src = p;
1306276305Sngie		    /* now get target */
1307236769Sobrien		    if (strsep(&p, " ") == NULL)
1308236769Sobrien			continue;
1309236769Sobrien		    CHECK_VALID_META(p);
1310236769Sobrien		    /* 'L' and 'M' put single quotes around the args */
1311276305Sngie		    DEQUOTE(p);
1312276305Sngie		    DEQUOTE(link_src);
1313276305Sngie#ifdef DEBUG_META_MODE
1314276305Sngie		    if (DEBUG(META))
1315276305Sngie			fprintf(debug_file, "meta_oodate: L %s -> %s\n",
1316276305Sngie				link_src, p);
1317276305Sngie#endif
1318236769Sobrien		    /* FALLTHROUGH */
1319236769Sobrien		case 'W':		/* Write */
1320276305Sngie		check_write:
1321236769Sobrien		    /*
1322236769Sobrien		     * If a file we generated within our bailiwick
1323236769Sobrien		     * but outside of .OBJDIR is missing,
1324236769Sobrien		     * we need to do it again.
1325236769Sobrien		     */
1326236769Sobrien		    /* ignore non-absolute paths */
1327236769Sobrien		    if (*p != '/')
1328236769Sobrien			break;
1329236769Sobrien
1330236769Sobrien		    if (Lst_IsEmpty(metaBailiwick))
1331236769Sobrien			break;
1332236769Sobrien
1333236769Sobrien		    /* ignore cwd - normal dependencies handle those */
1334236769Sobrien		    if (strncmp(p, cwd, cwdlen) == 0)
1335236769Sobrien			break;
1336236769Sobrien
1337236769Sobrien		    if (!Lst_ForEach(metaBailiwick, prefix_match, p))
1338236769Sobrien			break;
1339236769Sobrien
1340236769Sobrien		    /* tmpdir might be within */
1341236769Sobrien		    if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0)
1342236769Sobrien			break;
1343236769Sobrien
1344236769Sobrien		    /* ignore anything containing the string "tmp" */
1345236769Sobrien		    if ((strstr("tmp", p)))
1346236769Sobrien			break;
1347236769Sobrien
1348321964Ssjg		    if ((link_src != NULL && cached_lstat(p, &fs) < 0) ||
1349321964Ssjg			(link_src == NULL && cached_stat(p, &fs) < 0)) {
1350321964Ssjg			if (!meta_ignore(gn, p)) {
1351321964Ssjg			    if (Lst_Find(missingFiles, p, string_match) == NULL)
1352321964Ssjg				Lst_AtEnd(missingFiles, bmake_strdup(p));
1353321964Ssjg			}
1354236769Sobrien		    }
1355236769Sobrien		    break;
1356276305Sngie		check_link_src:
1357276305Sngie		    p = link_src;
1358276305Sngie		    link_src = NULL;
1359276305Sngie#ifdef DEBUG_META_MODE
1360276305Sngie		    if (DEBUG(META))
1361276305Sngie			fprintf(debug_file, "meta_oodate: L src %s\n", p);
1362276305Sngie#endif
1363276305Sngie		    /* FALLTHROUGH */
1364236769Sobrien		case 'R':		/* Read */
1365236769Sobrien		case 'E':		/* Exec */
1366236769Sobrien		    /*
1367236769Sobrien		     * Check for runtime files that can't
1368236769Sobrien		     * be part of the dependencies because
1369236769Sobrien		     * they are _expected_ to change.
1370236769Sobrien		     */
1371321964Ssjg		    if (meta_ignore(gn, p))
1372236769Sobrien			break;
1373321964Ssjg
1374236769Sobrien		    /*
1375236769Sobrien		     * The rest of the record is the file name.
1376236769Sobrien		     * Check if it's not an absolute path.
1377236769Sobrien		     */
1378236769Sobrien		    {
1379236769Sobrien			char *sdirs[4];
1380236769Sobrien			char **sdp;
1381236769Sobrien			int sdx = 0;
1382236769Sobrien			int found = 0;
1383236769Sobrien
1384236769Sobrien			if (*p == '/') {
1385236769Sobrien			    sdirs[sdx++] = p; /* done */
1386236769Sobrien			} else {
1387236769Sobrien			    if (strcmp(".", p) == 0)
1388236769Sobrien				continue;  /* no point */
1389236769Sobrien
1390236769Sobrien			    /* Check vs latestdir */
1391236769Sobrien			    snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p);
1392236769Sobrien			    sdirs[sdx++] = fname1;
1393236769Sobrien
1394292068Ssjg			    if (strcmp(latestdir, lcwd) != 0) {
1395292068Ssjg				/* Check vs lcwd */
1396292068Ssjg				snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p);
1397236769Sobrien				sdirs[sdx++] = fname2;
1398236769Sobrien			    }
1399292068Ssjg			    if (strcmp(lcwd, cwd) != 0) {
1400292068Ssjg				/* Check vs cwd */
1401292068Ssjg				snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p);
1402292068Ssjg				sdirs[sdx++] = fname3;
1403292068Ssjg			    }
1404236769Sobrien			}
1405236769Sobrien			sdirs[sdx++] = NULL;
1406236769Sobrien
1407236769Sobrien			for (sdp = sdirs; *sdp && !found; sdp++) {
1408236769Sobrien#ifdef DEBUG_META_MODE
1409236769Sobrien			    if (DEBUG(META))
1410236769Sobrien				fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp);
1411236769Sobrien#endif
1412321964Ssjg			    if (cached_stat(*sdp, &fs) == 0) {
1413236769Sobrien				found = 1;
1414236769Sobrien				p = *sdp;
1415236769Sobrien			    }
1416236769Sobrien			}
1417236769Sobrien			if (found) {
1418236769Sobrien#ifdef DEBUG_META_MODE
1419236769Sobrien			    if (DEBUG(META))
1420236769Sobrien				fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p);
1421236769Sobrien#endif
1422236769Sobrien			    if (!S_ISDIR(fs.st_mode) &&
1423236769Sobrien				fs.st_mtime > gn->mtime) {
1424236769Sobrien				if (DEBUG(META))
1425236769Sobrien				    fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p);
1426236769Sobrien				oodate = TRUE;
1427236769Sobrien			    } else if (S_ISDIR(fs.st_mode)) {
1428236769Sobrien				/* Update the latest directory. */
1429321964Ssjg				cached_realpath(p, latestdir);
1430236769Sobrien			    }
1431236769Sobrien			} else if (errno == ENOENT && *p == '/' &&
1432236769Sobrien				   strncmp(p, cwd, cwdlen) != 0) {
1433236769Sobrien			    /*
1434236769Sobrien			     * A referenced file outside of CWD is missing.
1435236769Sobrien			     * We cannot catch every eventuality here...
1436236769Sobrien			     */
1437321964Ssjg			    if (Lst_Find(missingFiles, p, string_match) == NULL)
1438321964Ssjg				    Lst_AtEnd(missingFiles, bmake_strdup(p));
1439236769Sobrien			}
1440236769Sobrien		    }
1441292068Ssjg		    if (buf[0] == 'E') {
1442292068Ssjg			/* previous latestdir is no longer relevant */
1443292068Ssjg			strlcpy(latestdir, lcwd, sizeof(latestdir));
1444292068Ssjg		    }
1445236769Sobrien		    break;
1446236769Sobrien		default:
1447236769Sobrien		    break;
1448236769Sobrien		}
1449276305Sngie		if (!oodate && buf[0] == 'L' && link_src != NULL)
1450276305Sngie		    goto check_link_src;
1451236769Sobrien	    } else if (strcmp(buf, "CMD") == 0) {
1452236769Sobrien		/*
1453236769Sobrien		 * Compare the current command with the one in the
1454236769Sobrien		 * meta data file.
1455236769Sobrien		 */
1456236769Sobrien		if (ln == NULL) {
1457236769Sobrien		    if (DEBUG(META))
1458236769Sobrien			fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno);
1459236769Sobrien		    oodate = TRUE;
1460236769Sobrien		} else {
1461236769Sobrien		    char *cmd = (char *)Lst_Datum(ln);
1462249033Ssjg		    Boolean hasOODATE = FALSE;
1463236769Sobrien
1464249033Ssjg		    if (strstr(cmd, "$?"))
1465249033Ssjg			hasOODATE = TRUE;
1466249033Ssjg		    else if ((cp = strstr(cmd, ".OODATE"))) {
1467249033Ssjg			/* check for $[{(].OODATE[:)}] */
1468249033Ssjg			if (cp > cmd + 2 && cp[-2] == '$')
1469249033Ssjg			    hasOODATE = TRUE;
1470236769Sobrien		    }
1471249033Ssjg		    if (hasOODATE) {
1472249033Ssjg			needOODATE = TRUE;
1473249033Ssjg			if (DEBUG(META))
1474249033Ssjg			    fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno);
1475249033Ssjg		    }
1476321964Ssjg		    cmd = Var_Subst(NULL, cmd, gn, VARF_WANTRES|VARF_UNDEFERR);
1477236769Sobrien
1478236769Sobrien		    if ((cp = strchr(cmd, '\n'))) {
1479236769Sobrien			int n;
1480236769Sobrien
1481236769Sobrien			/*
1482236769Sobrien			 * This command contains newlines, we need to
1483236769Sobrien			 * fetch more from the .meta file before we
1484236769Sobrien			 * attempt a comparison.
1485236769Sobrien			 */
1486236769Sobrien			/* first put the newline back at buf[x - 1] */
1487236769Sobrien			buf[x - 1] = '\n';
1488236769Sobrien			do {
1489236769Sobrien			    /* now fetch the next line */
1490236769Sobrien			    if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0)
1491236769Sobrien				break;
1492236769Sobrien			    x = n;
1493236769Sobrien			    lineno++;
1494236769Sobrien			    if (buf[x - 1] != '\n') {
1495236769Sobrien				warnx("%s: %d: line truncated at %u", fname, lineno, x);
1496236769Sobrien				break;
1497236769Sobrien			    }
1498236769Sobrien			    cp = strchr(++cp, '\n');
1499236769Sobrien			} while (cp);
1500236769Sobrien			if (buf[x - 1] == '\n')
1501236769Sobrien			    buf[x - 1] = '\0';
1502236769Sobrien		    }
1503249033Ssjg		    if (!hasOODATE &&
1504236769Sobrien			!(gn->type & OP_NOMETA_CMP) &&
1505236769Sobrien			strcmp(p, cmd) != 0) {
1506236769Sobrien			if (DEBUG(META))
1507236769Sobrien			    fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd);
1508236769Sobrien			if (!metaIgnoreCMDs)
1509236769Sobrien			    oodate = TRUE;
1510236769Sobrien		    }
1511236769Sobrien		    free(cmd);
1512236769Sobrien		    ln = Lst_Succ(ln);
1513236769Sobrien		}
1514236769Sobrien	    } else if (strcmp(buf, "CWD") == 0) {
1515236769Sobrien		/*
1516236769Sobrien		 * Check if there are extra commands now
1517236769Sobrien		 * that weren't in the meta data file.
1518236769Sobrien		 */
1519236769Sobrien		if (!oodate && ln != NULL) {
1520236769Sobrien		    if (DEBUG(META))
1521236769Sobrien			fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno);
1522236769Sobrien		    oodate = TRUE;
1523236769Sobrien		}
1524236769Sobrien		if (strcmp(p, cwd) != 0) {
1525236769Sobrien		    if (DEBUG(META))
1526236769Sobrien			fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir);
1527236769Sobrien		    oodate = TRUE;
1528236769Sobrien		}
1529236769Sobrien	    }
1530236769Sobrien	}
1531236769Sobrien
1532236769Sobrien	fclose(fp);
1533236769Sobrien	if (!Lst_IsEmpty(missingFiles)) {
1534236769Sobrien	    if (DEBUG(META))
1535236769Sobrien		fprintf(debug_file, "%s: missing files: %s...\n",
1536236769Sobrien			fname, (char *)Lst_Datum(Lst_First(missingFiles)));
1537236769Sobrien	    oodate = TRUE;
1538236769Sobrien	}
1539321964Ssjg	if (!oodate && !have_filemon && filemonMissing) {
1540236769Sobrien	    if (DEBUG(META))
1541321964Ssjg		fprintf(debug_file, "%s: missing filemon data\n", fname);
1542236769Sobrien	    oodate = TRUE;
1543236769Sobrien	}
1544321964Ssjg    } else {
1545321964Ssjg	if (writeMeta && metaMissing) {
1546321964Ssjg	    cp = NULL;
1547321964Ssjg
1548321964Ssjg	    /* if target is in .CURDIR we do not need a meta file */
1549321964Ssjg	    if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) {
1550321964Ssjg		if (strncmp(curdir, gn->path, (cp - gn->path)) != 0) {
1551321964Ssjg		    cp = NULL;		/* not in .CURDIR */
1552321964Ssjg		}
1553321964Ssjg	    }
1554321964Ssjg	    if (!cp) {
1555321964Ssjg		if (DEBUG(META))
1556321964Ssjg		    fprintf(debug_file, "%s: required but missing\n", fname);
1557321964Ssjg		oodate = TRUE;
1558321964Ssjg		needOODATE = TRUE;	/* assume the worst */
1559321964Ssjg	    }
1560321964Ssjg	}
1561236769Sobrien    }
1562321964Ssjg
1563321964Ssjg    Lst_Destroy(missingFiles, (FreeProc *)free);
1564321964Ssjg
1565246223Ssjg    if (oodate && needOODATE) {
1566236769Sobrien	/*
1567246223Ssjg	 * Target uses .OODATE which is empty; or we wouldn't be here.
1568246223Ssjg	 * We have decided it is oodate, so .OODATE needs to be set.
1569246223Ssjg	 * All we can sanely do is set it to .ALLSRC.
1570236769Sobrien	 */
1571236769Sobrien	Var_Delete(OODATE, gn);
1572246223Ssjg	Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn, 0);
1573321964Ssjg	free(cp);
1574236769Sobrien    }
1575321964Ssjg
1576321964Ssjg oodate_out:
1577321964Ssjg    for (i--; i >= 0; i--) {
1578321964Ssjg	free(pa[i]);
1579321964Ssjg    }
1580236769Sobrien    return oodate;
1581236769Sobrien}
1582236769Sobrien
1583236769Sobrien/* support for compat mode */
1584236769Sobrien
1585236769Sobrienstatic int childPipe[2];
1586236769Sobrien
1587236769Sobrienvoid
1588236769Sobrienmeta_compat_start(void)
1589236769Sobrien{
1590236769Sobrien#ifdef USE_FILEMON_ONCE
1591236769Sobrien    /*
1592236769Sobrien     * We need to re-open filemon for each cmd.
1593236769Sobrien     */
1594236769Sobrien    BuildMon *pbm = &Mybm;
1595236769Sobrien
1596236769Sobrien    if (pbm->mfp != NULL && useFilemon) {
1597236769Sobrien	filemon_open(pbm);
1598236769Sobrien    } else {
1599236769Sobrien	pbm->mon_fd = pbm->filemon_fd = -1;
1600236769Sobrien    }
1601236769Sobrien#endif
1602236769Sobrien    if (pipe(childPipe) < 0)
1603236769Sobrien	Punt("Cannot create pipe: %s", strerror(errno));
1604236769Sobrien    /* Set close-on-exec flag for both */
1605321964Ssjg    (void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC);
1606321964Ssjg    (void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC);
1607236769Sobrien}
1608236769Sobrien
1609236769Sobrienvoid
1610236769Sobrienmeta_compat_child(void)
1611236769Sobrien{
1612236769Sobrien    meta_job_child(NULL);
1613236769Sobrien    if (dup2(childPipe[1], 1) < 0 ||
1614236769Sobrien	dup2(1, 2) < 0) {
1615236769Sobrien	execError("dup2", "pipe");
1616236769Sobrien	_exit(1);
1617236769Sobrien    }
1618236769Sobrien}
1619236769Sobrien
1620236769Sobrienvoid
1621236769Sobrienmeta_compat_parent(void)
1622236769Sobrien{
1623236769Sobrien    FILE *fp;
1624236769Sobrien    char buf[BUFSIZ];
1625236769Sobrien
1626236769Sobrien    close(childPipe[1]);			/* child side */
1627236769Sobrien    fp = fdopen(childPipe[0], "r");
1628236769Sobrien    while (fgets(buf, sizeof(buf), fp)) {
1629236769Sobrien	meta_job_output(NULL, buf, "");
1630236769Sobrien	printf("%s", buf);
1631321964Ssjg	fflush(stdout);
1632236769Sobrien    }
1633236769Sobrien    fclose(fp);
1634236769Sobrien}
1635236769Sobrien
1636236769Sobrien#endif	/* USE_META */
1637