Logo Search packages:      
Sourcecode: ksh version File versions

state.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1984-2007 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Research
 *
 * make state variable routines
 * this file and the is*() macros specify the state variable name format
 *
 *    (<var>)           the state of <var>
 *    (<var>)<rule>     the state of <var> qualified by <rule>
 *    ()<rule>    the state of <rule>
 *
 * NOTE: update VERSION in compile.c if the format changes
 */

#include "make.h"
#include "options.h"

/*
 * return a pointer to the rule <s1><s2><s3>
 * force causes the rule to be created
 * force<0 canon's name first
 */

Rule_t*
catrule(register char* s1, register char* s2, register char* s3, int force)
{
      Rule_t*           r;

      sfputr(internal.nam, s1, *s2++);
      if (*s2)
            sfputr(internal.nam, s2, s3 ? *s3++ : -1);
      if (s3 && *s3)
            sfputr(internal.nam, s3, -1);
      s1 = sfstruse(internal.nam);
      if (!(r = getrule(s1)) && force)
      {
            if (force < 0)
            {
                  pathcanon(s1, 0);
                  if (r = getrule(s1))
                        return r;
            }
            r = makerule(NiL);
      }
      return r;
}

/*
 * return the state file name
 * assumes the main makefile has already been read
 */

char*
statefile(void)
{
      char*       dir;
      Sfio_t*           sp;
      Stat_t            st;

      if (!state.statefile && state.makefile)
      {
            sp = sfstropen();
            dir = DELETE;
            if (!state.writestate || streq(state.writestate, "-") || !stat(state.writestate, &st) && S_ISDIR(st.st_mode) && (dir = state.writestate))
                  edit(sp, state.makefile, dir, KEEP, external.state);
            else
                  expand(sp, state.writestate);
            state.statefile = strdup(sfstruse(sp));
            sfstrclose(sp);
      }
      return state.statefile;
}

/*
 * reconcile s with state view from r
 *
 * NOTE: requires state.maxview>0 && 0<=view<=state.maxview
 */

static Rule_t*
stateview(int op, char* name, register Rule_t* s, register Rule_t* r, int view, int accept, Rule_t** pv)
{
      register Rule_t*  v;
      register List_t*  p;
      Sfio_t*                 fp;

      if (pv)
            *pv = 0;
      if (state.compile < COMPILED)
      {
#if DEBUG
            if (state.test & 0x00000200)
                  error(2, "STATEVIEW %d %s state file load delayed until after makefile read", view, name);
#endif
            return 0;
      }
      if (!(state.view[view].flags & BIND_LOADED))
      {
            if (view < state.readstate)
            {
                  char* file;
                  Sfio_t*     tmp;
                  long  n;

                  tmp = sfstropen();
                  if (name && !s)
                  {
                        sfputr(tmp, name, 0);
                        n = sfstrtell(tmp);
                  }
                  else
                        n = 0;
                  edit(tmp, statefile(), state.view[view].path, KEEP, KEEP);
                  sfputc(tmp, 0);
                  file = sfstrseek(tmp, n, SEEK_SET);
                  if (fp = sfopen(NiL, file, "br"))
                  {
                        /*
                         * NOTE: this load should not be a problem for
                         *     internal rule pointers since all
                         *     non-state rules in state files
                         *     are just references
                         */

                        state.stateview = view;
                        message((-2, "loading state view %d file %s", view, file));
                        if (load(fp, file, 0) > 0)
                              state.view[view].flags |= BIND_EXISTS;
                        else if (state.corrupt && *state.corrupt == 'a' && !(state.view[0].flags & BIND_EXISTS))
                              state.accept = 1;
                        state.stateview = -1;
                        sfclose(fp);
                  }
                  if (n)
                        strcpy(name, sfstrbase(tmp));
                  sfstrclose(tmp);
            }
            state.view[view].flags |= BIND_LOADED;
      }
      if (name)
      {
            viewname(name, view);
            v = getrule(name);
            unviewname(name);
#if DEBUG
            if (state.test & 0x00000200)
                  error(2, "STATEVIEW %s [%s] test %d [%s] -> %s [%s]", name, s ? timestr(s->time) : "no rule", view, timestr(r->time), v ? v->name : null, v ? timestr(v->time) : "no rule");
#endif
            if (v)
            {
                  if (pv)
                        *pv = v;
                  if (s && (op == RULE && (s->event >= v->event && s->event || !v->time && (v->property & P_force)) || op == PREREQS && s->time >= v->time))
                        return s;
                  if (v->time == r->time || accept || r->view == view || (r->property & (P_state|P_use|P_virtual)) || state.believe && view >= (state.believe - 1))
                  {
                        if (r->property & P_state)
                        {
                              if (r->property & P_statevar)
                              {
                                    if (r->statedata && (!v->statedata && *r->statedata || v->statedata && !streq(r->statedata, v->statedata) || r->time > v->time))
                                          return 0;
                                    s = r;
                                    s->statedata = v->statedata;
                                    if (v->property & P_parameter)
                                          s->property |= P_parameter;
                              }
                        }
                        else
                        {
                              if (r->property & P_use)
                              {
                                    if (r->action && (!v->action || !streq(r->action, v->action)) || !r->action && v->action)
                                          return 0;
                                    r->time = v->time;
                              }
                              if (!s)
                                    s = makerule(name);
                        }
                        s->time = v->time;
                        s->attribute = v->attribute;
                        s->event = v->event;
                        s->action = v->action;
                        if (v->property & P_force)
                              s->property |= P_force;
                        else
                              s->property &= ~P_force;
                        if (v->dynamic & D_built)
                              s->dynamic |= D_built;
                        else
                              s->dynamic &= ~D_built;
                        if (v->dynamic & D_lowres)
                              s->dynamic |= D_lowres;
                        else
                              s->dynamic &= ~D_lowres;
                        s->prereqs = listcopy(v->prereqs);
                        s->scan = v->scan;
                        for (p = s->prereqs; p; p = p->next)
                        {
                              v = p->rule;
                              if (v->dynamic & D_lower)
                              {
                                    unviewname(v->name);
                                    p->rule = makerule(v->name);
                                    viewname(v->name, v->view);
                              }
                        }
#if DEBUG
                        if (state.test & 0x00000200)
                        {
                              error(2, "STATEVIEW %s accept %d", s->name, view);
                              if (state.test & 0x00000400)
                                    dumprule(sfstderr, s);
                        }
#endif
                  }
            }
      }
      return s;
}

/*
 * return a pointer to the state rule of var qualified by r
 * force>0 causes the state rule to be created
 * force<0 prevents a state bind
 */

Rule_t*
staterule(int op, register Rule_t* r, char* var, int force)
{
      register Rule_t*  s;
      register int            i;
      char*             rul;
      char*             nam;
      Rule_t*                 v;
      int               j;
      int               k;
      int               m;
      int               nobind = force < 0;
      Flags_t*          b;

      switch (op)
      {
      case PREREQS:
            rul = var;
            var = "+";
            if (!r)
            {
                  if (nobind)
                  {
                        sfprintf(internal.nam, "(%s)%s", var, rul);
                        r = getrule(sfstruse(internal.nam));
                  }
                  return r;
            }
            break;
      case RULE:
            rul = var;
            var = null;
            if (!r)
            {
                  if (nobind)
                  {
                        sfprintf(internal.nam, "(%s)%s", var, rul);
                        return getrule(sfstruse(internal.nam));
                  }
                  r = makerule(var);
                  nobind = 1;
            }
            break;
      case VAR:
            if (!r)
            {
                  sfprintf(internal.nam, "(%s)", var);
                  if (!(r = getrule(sfstruse(internal.nam))))
                  {
                        if (!force)
                              return 0;
                        r = makerule(sfstrbase(internal.nam));
                  }
            }
            if (nobind)
                  return r;
            break;
#if DEBUG
      default:
            error(PANIC, "invalid op=%d to staterule()", op);
            break;
#endif
      }
 again:
      if (r->property & P_statevar)
      {
            if (op == PREREQS)
                  return 0;
            op = RULE;
            nam = r->name;
            s = r;
      }
      else if (r->property & P_state)
            return 0;
      else
      {
            if ((r->dynamic & (D_member|D_membertoo)) && (rul = strrchr(r->name, '/')))
                  rul++;
            else
                  rul = unbound(r);
            sfprintf(internal.nam, "(%s)%s", var, rul);
            nam = sfstruse(internal.nam);
            if (s = getrule(nam))
                  nam = s->name;
      }
      if (state.maxview && state.readstate && state.makefile && !nobind)
      {
            b = &r->checked[op];
            k = 0;
            if (!tstbit(*b, i = (r->property & (P_statevar|P_use|P_virtual)) && state.targetview > 0 ? state.targetview : r->view) && (!s || !s->time || (r->property & P_statevar) || !(k = statetimeq(r, s))))
            {
                  if (!(r->property & (P_statevar|P_use|P_virtual)) && !(r->dynamic & D_bound) && !(r->mark & M_bind) && (s && s->time || !s && state.compile >= COMPILED))
                  {
                        /*
                         * M_bind guards staterule() recursion
                         */

                        s = r;
                        s->mark |= M_bind;
                        r = bind(r);
                        s->mark &= ~M_bind;
                        goto again;
                  }
                  if ((r->property & P_statevar) && r->time == OLDTIME)
                  {
                        for (i = 0; i <= state.maxview; i++)
                        {
                              setbit(*b, i);
                              setbit(r->checked[CONSISTENT], i);
                        }
                  }
                  else
                  {
                        if (!(r->property & (P_statevar|P_use|P_virtual)) || state.targetview < 0)
                        {
                              m = 1;
                              j = 0;
                              if (!(k = r->view) && !(state.view[0].flags & BIND_EXISTS))
                                    k = state.maxview;
                        }
                        else
                        {
                              m = 0;
                              j = k = state.targetview;
                        }
#if DEBUG
                        if (state.test & 0x00000200)
                              error(2, "STATERULE %s search %d..%d targetview=%d", nam, j, k, state.targetview);
#endif
                        for (i = j; i <= k; i++)
                        {
                              setbit(*b, i);
                              if (i || !(r->property & P_statevar))
                              {
                                    s = stateview(op, nam, s, r, i, i == k, &v);
                                    if (v)
                                    {
                                          if (m)
                                          {
                                                m = i;
                                                while (++m <= k)
                                                      setbit(*b, m);
                                          }
                                          if (op != PREREQS || state.accept)
                                                do setbit(r->checked[CONSISTENT], i); while (--i > j);
                                          break;
                                    }
                              }
                              if (state.accept)
                                    setbit(r->checked[CONSISTENT], i);
                              if (!i)
                              {
                                    if (op != PREREQS)
                                          setbit(r->checked[CONSISTENT], i);
                                    k = state.maxview;
                              }
                        }
                  }
            }
            else if (k)
                  setbit(*b, i);
      }
      if (!s && force > 0)
            s = makerule(nam);
      if (s && op == RULE)
      {
            r->attribute |= s->attribute & internal.retain->attribute;
            r->property |= s->property & (P_dontcare|P_terminal);
      }
      return s;
}

/*
 * return a non-state rule pointer corresponding to the staterule r
 * force causes the non-state rule to be created
 */

Rule_t*
rulestate(register Rule_t* r, int force)
{
      register char*          s;

      if (r->property & P_staterule)
      {
            s = r->name;
            while (*s && *s++ != ')');
            if (!(r = getrule(s)) && force)
                  r = makerule(s);
      }
      return r;
}

/*
 * return a variable pointer corresponding to the state variable r
 * force causes the variable to be created
 */

Var_t*
varstate(register Rule_t* r, int force)
{
      register char*          s;
      register char*          t;
      register Var_t*         v;

      s = r->name;
      if (r->property & P_state)
      {
            if (r->property & P_statevar)
            {
                  s++;
                  *(t = s + strlen(s) - 1) = 0;
            }
            else
                  return 0;
      }
      else
            t = 0;
      if (!(v = getvar(s)) && force)
            v = setvar(s, null, force < 0 ? V_retain : 0);
      if (t)
            *t = ')';
      return v;
}

/*
 * return the auxiliary variable pointer for s
 * force causes the variable to be created
 */

Var_t*
auxiliary(char* s, int force)
{
      Var_t*            v;

      sfprintf(internal.nam, "(&)%s", s);
      if (!(v = getvar(sfstruse(internal.nam))) && force)
            v = setvar(sfstrbase(internal.nam), null, 0);
      return v;
}

/*
 * force r->scan == (*(unsigned char*)h) or all files to be re-scanned
 */

int
forcescan(const char* s, char* v, void* h)
{
      register Rule_t*  r = (Rule_t*)v;
      register int            n = h ? *((unsigned char*)h) : r->scan;

      NoP(s);
      if ((r->property & P_staterule) && r->scan == n && !(r->dynamic & D_scanned))
            r->property |= P_force;
      return 0;
}

/*
 * report state file lock fatal error
 */

static void
badlock(char* file, int view, Time_t date)
{
      long  d;

      /*
       * probably a bad lock if too old
       */

      if ((d = (CURSECS - tmxsec(date))) > 24 * 60 * 60)
            error(1, "%s is probably an invalid lock file", file);
      else if (d > 0)
            error(1, "another make has been running on %s in %s for the past %s", state.makefile, state.view[view].path, fmtelapsed(state.regress ? 1 : d, 1));
      else
            error(1, "another make is running on %s in %s", state.makefile, state.view[view].path);
      error(3, "use -%c to override", OPT(OPT_ignorelock));
}

/*
 * get|release exclusive (advisory) access to state file
 *
 * this is a general solution that should work on all systems
 * the following problems need to be addressed
 *
 *    o  flock() or lockf() need to work for distributed
 *       as well as local file systems
 *
 *    o  the file system clock may be different than the
 *       local system clock
 *
 *    o  creating a specific lock file opens the door for
 *       lock files laying around after program and system
 *       crashes -- placing the pid of the locking process
 *       as file data may not work on distributed process
 *       systems
 */

#define LOCKTIME(p,m)   ((m)?tmxgetmtime(p):tmxgetctime(p))

void
lockstate(int set)
{
      register int            fd;
      register char*          file;
      Time_t                  t;
      Stat_t                  st;

      static char*            lockfile;
      static Time_t           locktime;
      static int        lockmtime;

      if (set)
      {
            if (!state.exec || state.virtualdot || !state.writestate)
                  return;
            edit(internal.nam, statefile(), KEEP, KEEP, external.lock);
            file = strdup(sfstruse(internal.nam));
            if (!state.ignorelock)
            {
                  int   uid = geteuid();

                  for (fd = 1; fd <= state.maxview; fd++)
                  {
                        edit(internal.nam, file, state.view[fd].path, KEEP, KEEP);
                        if (!stat(sfstruse(internal.nam), &st) && st.st_uid != uid)
                              badlock(sfstrbase(internal.nam), fd, LOCKTIME(&st, lockmtime));
                  }
            }
            locktime = 0;
            for (;;)
            {
                  lockfile = file;
                  if ((fd = open(file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0)) >= 0)
                        break;
                  lockfile = 0;
                  if (stat(file, &st) < 0)
                        error(3, "cannot create lock file %s", file);
                  if (!state.ignorelock)
                        badlock(file, 0, LOCKTIME(&st, lockmtime));
                  if (remove(file) < 0)
                        error(3, "cannot remove lock file %s", file);
            }

            /*
             * fstat() here would be best but some systems
             * like cygwin which shall remain nameless
             * return different time values after the close()
             */

            close(fd);
            if (stat(file, &st) < 0)
                  error(3, "cannot stat lock file %s", file);
            lockmtime = tmxgetatime(&st) < tmxgetmtime(&st) || tmxgetctime(&st) < tmxgetmtime(&st);
            locktime = LOCKTIME(&st, lockmtime);
      }
      else if (lockfile)
      {
            if (locktime)
            {
                  if (stat(lockfile, &st) < 0 || (t = LOCKTIME(&st, lockmtime)) != locktime && t != tmxsns(tmxsec(locktime), 0))
                  {
                        if (state.writestate)
                              error(1, "the state file lock on %s has been overridden", state.makefile);
                  }
                  else if (remove(lockfile) < 0)
                        error(1, "cannot remove lock file %s", lockfile);
            }
            else
                  remove(lockfile);
            free(lockfile);
            lockfile = 0;
      }
}

/*
 * read state from a previous make
 */

void
readstate(void)
{
      register Sfio_t*  fp;
      char*             file;

      if (state.makefile)
      {
            lockstate(1);
            if (state.readstate && (fp = sfopen(NiL, file = statefile(), "br")))
            {
                  state.stateview = 0;
                  message((-2, "loading state file %s", file));
                  makerule(file)->dynamic |= D_built;
                  if (load(fp, file, 10) > 0)
                        state.view[0].flags |= BIND_EXISTS;
                  else if (!state.corrupt)
                        error(3, "use -%c%c to accept current state or -%c to remake", OPT(OPT_accept), OPT(OPT_readstate), OPT(OPT_readstate));
                  else if (*state.corrupt == 'a')
                        state.accept = 1;
                  state.stateview = -1;
                  sfclose(fp);
            }
      }
      state.view[0].flags |= BIND_LOADED;
}

/*
 * update the superimposed code
 */

static void
code(register const char* s)
{
      debug((-6, "enter candidate state variable %s", s));
      settype(*s++, C_VARPOS1);
      if (*s)
      {
       settype(*s++, C_VARPOS2);
       if (*s)
       {
        settype(*s++, C_VARPOS3);
        if (*s)
        {
         settype(*s++, C_VARPOS4);
         if (*s)
         {
          settype(*s++, C_VARPOS5);
          if (*s)
          {
           settype(*s++, C_VARPOS6);
           if (*s)
           {
            settype(*s++, C_VARPOS7);
            if (*s) settype(*s, C_VARPOS8);
           }
          }
         }
        }
       }
      }
}

/*
 * bind() and scan() r->parameter file prerequisites
 * this catches all implicit state variables before the
 * parent files are scanned
 */

static int
checkparam(const char* s, char* v, void* h)
{
      register Rule_t*  r = (Rule_t*)v;
      register List_t*  p;
      register char*          t;
      Time_t                  tm;

      NoP(s);
      NoP(h);
      if ((r->property & (P_attribute|P_parameter|P_state)) == P_parameter)
      {
            r->property |= P_ignore;
            maketop(r, 0L, NiL);
            for (p = scan(r, &tm); p; p = p->next)
                  if (((r = p->rule)->property & (P_parameter|P_statevar)) == (P_parameter|P_statevar))
                  {
                        r->dynamic |= D_scanned;
                        if (t = strchr(r->name, ')'))
                        {
                              *t = 0;
                              code(r->name + 1);
                              *t = ')';
                        }
                  }
      }
      return 0;
}

/*
 * check implicit state variable vars
 */

static int
checkvar1(register const char* s, char* u, void* h)
{
      register Var_t*         v = (Var_t*)u;
      register Rule_t*  r;

      NoP(h);
      if (v->property & V_scan)
      {
            state.fullscan = 1;
            r = staterule(VAR, NiL, (char*)s, 1);
            if (!r->scan)
            {
                  debug((-5, "%s and %s force re-scan", v->name, r->name));
                  r->scan = SCAN_STATE;
                  state.forcescan = 1;
            }
            code(s);
      }
      return 0;
}

/*
 * check implicit state variable rules
 */

static int
checkvar2(const char* s, char* u, void* h)
{
      register Rule_t*  r = (Rule_t*)u;
      Var_t*                  v;

      NoP(s);
      NoP(h);
      if ((r->property & P_statevar) && r->scan && !r->view && (!(v = varstate(r, 0)) || !(v->property & V_scan)) && (!(r->property & P_parameter) || !(r->dynamic & D_scanned)))
      {
            debug((-5, "%s forces re-scan", r->name));
            r->scan = 0;
            state.forcescan = 1;
      }
      return 0;
}

/*
 * freeze the parameter files and candidate state variables
 */

void
candidates(void)
{
      int   view;

      if (state.scan)
      {
            message((-2, "freeze candidate state variables and parameter files"));
            hashwalk(table.rule, 0, checkparam, NiL);
            hashwalk(table.var, 0, checkvar1, NiL);
            hashwalk(table.rule, 0, checkvar2, NiL);
            if (state.forcescan)
            {
                  for (view = 1; view <= state.maxview; view++)
                        stateview(0, NiL, NiL, NiL, view, 0, NiL);
                  hashwalk(table.rule, 0, forcescan, NiL);
            }
      }
}

/*
 * save state for the next make
 */

void
savestate(void)
{
      char* file;

      if (state.makefile && state.user && state.compile == COMPILED)
      {
            if (state.writestate)
            {
                  if (state.finish)
                        state.compile = SAVED;
                  if (state.exec && state.savestate)
                  {
                        file = statefile();
                        message((-2, "saving state in %s", file));
                        state.stateview = 0;
                        compile(file, NiL);
                        state.stateview = -1;
                        state.savestate = 0;
                  }
            }
            if (state.finish)
                  lockstate(0);
      }
}

/*
 * bind statevar r to a variable
 */

Rule_t*
bindstate(register Rule_t* r, register char* val)
{
      Rule_t*           s;
      Time_t            t;

#if DEBUG
      if (!(r->property & P_state))
            error(PANIC, "bindstate(%s) called for non-state rule", r->name);
#endif
      if (state.maxview && (r->property & P_statevar) && (s = staterule(VAR, r, NiL, 0)))
            r = s;
      if ((r->dynamic & D_bound) && !val)
            return r;
      if (r->property & P_statevar)
      {
            register Var_t*         v;
            char*             e;
            Sfio_t*                 tmp = 0;

            /*
             * determine the current state variable value
             */

            if (val)
                  r->property |= P_virtual;
            else if (r->property & P_virtual)
                  val = null;
            else if (v = varstate(r, 0))
            {
                  tmp = sfstropen();
                  r->dynamic |= D_bound;
                  expand(tmp, getval(v->name, VAL_PRIMARY));
                  r->dynamic &= ~D_bound;
                  val = sfstruse(tmp);
            }
            else if ((r->property & P_parameter) && r->statedata)
                  val = r->statedata;
            else if (*(r->name + 1) == '-')
            {
                  *(e = r->name + strlen(r->name) - 1) = 0;
                  val = getval(r->name + 1, 0);
                  *e = ')';
            }
            else
                  val = null;
            if (!r->time && state.maxview && (state.view[0].flags & BIND_LOADED))
            {
                  /*
                   * see if some other view has an initial value
                   */

                  r->statedata = strdup(val);
                  staterule(RULE, r, NiL, 0);
            }

            /*
             * check if the state variable value changed
             * the previous value, if defined, has already
             * been placed in r->statedata by readstate()
             */

            message((-2, "checking state variable %s value `%s'", r->name, r->statedata ? r->statedata : null));
            if (!r->time || r->statedata && !streq(r->statedata, val) || !r->statedata && *val)
            {
                  /*
                   * state variable changed
                   */

                  if (!r->view && !(r->property & P_accept))
                  {
                        if (r->time)
                              reason((1, "state variable %s changed to `%s' from `%s'", r->name, val, r->statedata));
                        else
                              reason((1, "state variable %s initialized to `%s'", r->name, val));
                  }
                  state.savestate = 1;
                  if (r->statedata != val)
                        r->statedata = strdup(val);
                  if (r->time == (t = CURTIME))
                        t++;
                  r->time = t;
            }
            if ((r->property & P_accept) || state.accept)
                  r->time = OLDTIME;
            if (tmp)
                  sfstrclose(tmp);
      }
      bindattribute(r);
      return r;
}

/*
 * check and stat built target r
 * otherwise check for motion from . to dir of r
 *
 * NOTE: this needs clarification
 */

static int
checkcurrent(register Rule_t* r, Stat_t* st)
{
      register int      n;
      register char*    s;
      long        pos;

      if (r->uname && !(n = rstat(r->uname, st, 1)))
            oldname(r);
      else if ((n = rstat(r->name, st, 1)) && (state.exec || state.mam.out && !state.mam.port))
      {
            rebind(r, -1);
            n = rstat(r->name, st, 1);
      }
      if (!n && !(r->dynamic & D_entries) && S_ISREG(st->st_mode))
            r->dynamic |= D_regular;
      if (!(r->dynamic & D_triggered))
            return n;
      edit(internal.nam, r->name, KEEP, DELETE, DELETE);
      if (!(pos = sfstrtell(internal.nam)))
            return n;
      sfputc(internal.nam, 0);
      sfputr(internal.nam, r->name, 0);
      s = sfstrseek(internal.nam, pos + 1, SEEK_SET);
      pathcanon(s, 0);
      if (!streq(r->name, s))
      {
            if (!r->uname)
                  r->uname = r->name;
            r->name = strdup(s);
      }
      s = sfstrseek(internal.nam, 0, SEEK_SET);
#if DEBUG
      if (state.test & 0x00000100)
            error(2, "statetime(%s): dir=%s n=%d time=[%s]", r->name, s, n, timestr(n ? NOTIME : tmxgetmtime(st)));
#endif
      newfile(r, s, n ? NOTIME : tmxgetmtime(st));
      return n;
}

/*
 * update internal time of r after its action has completed
 * sync>0 syncs the state rule prereqs and action
 * sync<0 resolves r but does not update state
 */

Time_t
statetime(register Rule_t* r, int sync)
{
      register Rule_t*  s;
      int               a;
      int               n;
      int               skip = 0;
      int               zerostate = 0;
      Time_t                  t;
      Time_t                  q;
      Rule_t*                 x;
      Stat_t                  st;
      Stat_t                  ln;

      if (r->property & P_state)
      {
            if ((r->dynamic & D_triggered) && state.exec)
                  r->time = ((r->property & P_statevar) && r->status == FAILED) ? (Time_t)0 : CURTIME;
            return r->time;
      }
      s = 0;
      if (state.interrupt && r->status != EXISTS)
            zerostate = 1;
      else if (r->status == FAILED)
      {
            tmxsetmtime(&st, 0);
            if ((state.test & 0x00040000) && (s = staterule(RULE, r, NiL, 0)))
            {
                  r->time = s->time;
                  if (r->property & (P_metarule|P_state))
                        r->event = s->event;
                  state.savestate = 1;
                  skip = 1;
            }
      }
      else if (r->property & P_virtual)
      {
            r->time = CURTIME;
            tmxsetmtime(&st, r->time);
      }
      else if (checkcurrent(r, &st))
      {
            if (r->property & P_dontcare)
                  t = 0;
            else
            {
                  t = CURTIME;
                  zerostate = 1;
            }
            tmxsetmtime(&st, t);
      }
      else if (sync < 0)
            return r->time;
      else if ((s = staterule(RULE, r, NiL, 1)) && s->time == tmxgetmtime(&st))
      {
            if (state.exec && !state.touch)
            {
                  /*
                   * this alternate event time prevents the action from
                   * triggering next time if nothing else changes
                   */

                  x = staterule(PREREQS, r, NiL, 1);
                  x->dynamic &= ~D_lowres;
                  x->time = r->time;
                  r->time = s->event;
                  if (r->dynamic & D_triggered)
                        r->dynamic |= D_same;
            }
            state.savestate = 1;
      }
      else if ((r->dynamic & D_triggered) && state.exec)
      {
            static int  localsync;
            static int  localtest;
            static Time_t     localskew;

            /*
             * r is built since its time changed after its action triggered
             */

            s->dynamic |= D_built;
            if (x = staterule(PREREQS, r, NiL, 0))
                  x->property |= P_force;

            /*
             * check for file system and local system time consistency
             * directories, archives and multi hard link files not sync'd
             */

            if (st.st_nlink <= 1 && !S_ISDIR(st.st_mode) && !(r->property & P_archive))
            {

#if DEBUG
                  if (state.test & 0x00000100)
                        error(2, "%s: r[%s] s[%s] f[%s]", r->name, timestr(r->time), timestr(s->time), timestr(tmxgetmtime(&st)));
#endif
                  if (!localsync && !state.override && r->time && r->time != OLDTIME && !(r->property & P_force) && tmxgetmtime(&st) == tmxgetctime(&st))
                  {
                        if (((n = (tmxsec(r->time) - (unsigned long)st.st_mtime - 1)) >= 0 || (n = (CURSECS - (unsigned long)st.st_mtime + 2)) <= 0) && (lstat(r->name, &ln) || !S_ISLNK(ln.st_mode)))
                        {
                              /*
                               * warn if difference not tolerable
                               */

                              a = (n > 0) ? n : -n;
                              if (a > 1)
                                    error(state.regress ? -1 : 1, "%s file system time %s local time by at least %s", r->name, n > 0 ? "lags" : "leads", fmtelapsed(a, 1));
                              localsync = a > state.tolerance ? 1 : -1;
                        }
                  }
                  if (localsync > 0)
                  {
                        /*
                         * NOTE: time stamp syncs work on the assumption that
                         *     all source files have an mtime that is older
                         *     than CURTIME -- this isn't too bad since
                         *     only built files are sync'd; also note that
                         *     nsec is set to 0 to avoid resolution mismatches
                         */

                        for (;;)
                        {
                              t = tmxsns(CURSECS + tmxsec(localskew), 0);
                              if (tmxtouch(r->name, TMX_NOTIME, t, TMX_NOTIME, 0))
                              {
                                    error(ERROR_SYSTEM|1, "%s not sync'd to local time", r->name);
                                    break;
                              }
                              if (localtest)
                              {
                                    tmxsetmtime(&st, t);
                                    break;
                              }

                              /*
                               * some systems try to fix up the local
                               * remote skew in the utime() call
                               * >> this never works <<
                               * members of the club include
                               *    darwin.ppc
                               *    netbsd.i386
                               */

                              if (stat(r->name, &st))
                              {
                                    error(ERROR_SYSTEM|1, "%s not found", r->name);
                                    break;
                              }
                              localtest = 1;
                              q = tmxgetmtime(&st);
                              if (tmxsec(q) == tmxsec(t))
                                    break;
                              localskew = tmxsns(tmxsec(q)-tmxsec(t),0);
                              error(state.regress ? -1 : 1, "the utime(2) or utimes(2) system call is botched for the filesystem containing %s (the current time is adjusted by %lu seconds) -- the state may be out of sync", r->name, tmxsec(localskew));

                              /*
                               * the botch may only be for times near "now"
                               * localskew=1s handles this
                               */

                              if (localskew > tmxsns(1,0))
                              {
                                    t = CURTIME + tmxsns(1,0);
                                    t = tmxsns(tmxsec(t),0);
                                    if (!tmxtouch(r->name, TMX_NOTIME, t, TMX_NOTIME, 0) && !stat(r->name, &st) && tmxgetmtime(&st) == t)
                                          localskew = tmxsns(1,0);
                              }
                        }
                  }
            }
      }
      if (!s)
            s = staterule(RULE, r, NiL, 1);
      if (sync)
      {
            s->dynamic |= D_built;
            s->attribute = r->attribute;
            s->action = r->action;
            if (s->prereqs != r->prereqs)
            {
                  if ((r->property & (P_joint|P_target)) != (P_joint|P_target))
                        freelist(s->prereqs);
                  s->prereqs = r->prereqs;
            }
            state.savestate = 1;
      }
      if (!skip && (s->time != tmxgetmtime(&st) || zerostate && s->time))
      {
            s->dynamic &= ~D_lowres;
            s->time = zerostate ? 0 : tmxgetmtime(&st);
            s->event = CURTIME;
            state.savestate = 1;
      }
      return s->time;
}

/*
 * return 1 if rule r time matches state s time modulo
 * tolerance and low resolution time state
 */

int
statetimeq(Rule_t* r, Rule_t* s)
{
      long        d;

      static int  warned;

      if (r->time == s->time)
            return 1;
      if (state.tolerance || (s->dynamic & D_lowres))
      {
            if (!(d = tmxsec(r->time) - tmxsec(s->time)) && (s->dynamic & D_lowres))
            {
                  s->dynamic &= ~D_lowres;
                  s->time = r->time;
                  state.savestate = 1;
                  return 1;
            }
            if (d >= -state.tolerance && d <= state.tolerance)
                  return 1;
      }
      else if (r->time < s->time && !(r->property & P_state) && tmxsec(r->time) == tmxsec(s->time) && !tmxnsec(r->time))
      {
            if (!warned)
            {
                  warned = 1;
                  if (state.warn)
                        error(1, "file timestamp subsecond truncation");
            }
            return 1;
      }
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index