Logo Search packages:      
Sourcecode: ksh version File versions

rule.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 rule support
 *
 * the attribute names defined in this file must agree with ATTRNAME
 */

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

#define ANON(name,flags)      (void)rinternal(name,P_attribute|(flags))
#define ASOC(field,name,flags)      internal.field=rassociate(name,flags)
#define ATTR(field,name,flags)      internal.field=rinternal(name,P_attribute|(flags))
#define FUNC(name,func)       ((setvar(name,NiL,V_builtin|V_functional))->builtin=(func))
#define INIT(field,name,flags)      internal.field=rinternal(name,flags)

/*
 * return the NAME_* type for name
 */

int
nametype(const char* name, char** e)
{
      register const char*    s;
      register int            t;
      register int            q;

      t = 0;
      s = name;
      switch (*s)
      {
      case 0:
            return 0;
      case MARK_CONTEXT:
            q = NAME_context;
            break;
      case '.':
            q = NAME_variable|NAME_intvar;
            break;
      case '-':
      case '+':
            return NAME_option;
      case '(':
            t = NAME_staterule;
            q = 0;
            break;
      default:
            q = istype(*s, C_ID1) ? (NAME_identifier|NAME_variable) : istype(*s, C_VARIABLE1) ? NAME_variable : 0;
            break;
      }
      for (;;)
      {
            switch (*s++)
            {
            case 0:
                  s -= 2;
                  break;
            case '/':
                  q |= NAME_path;
                  q &= ~(NAME_identifier|NAME_variable);
                  continue;
            case '=':
                  if (q & (NAME_identifier|NAME_variable))
                        q |= NAME_assignment;
                  continue;
            case '+':
                  if (*s != '=')
                        q &= ~(NAME_identifier|NAME_variable);
                  continue;
            case '&':
                  if (*s == '=')
                        continue;
                  /*FALLTHROUGH*/
            case '*':
            case '?':
            case '[':
            case ']':
            case '|':
                  q |= NAME_glob;
                  q &= ~(NAME_identifier|NAME_variable);
                  continue;
            case '@':
            case '!':
            case '}':
                  if (*s == '(')
                        t |= NAME_glob;
                  q &= ~(NAME_identifier|NAME_variable);
                  continue;
            case '$':
                  if (*s == '(')
                        t |= NAME_dynamic;
                  q &= ~(NAME_identifier|NAME_variable);
                  continue;
            case '(':
                  if (s > name + 1)
                  {
                        t &= ~NAME_staterule;
                        q &= ~NAME_staterule;
                  }
                  continue;
            case ')':
                  if (t & NAME_staterule)
                  {
                        t &= ~NAME_staterule;
                        q |= NAME_staterule;
                  }
                  else
                  {
                        q &= ~NAME_staterule;
                        q |= t & (NAME_glob|NAME_dynamic);
                  }
                  q &= ~(NAME_identifier|NAME_variable);
                  continue;
            default:
                  if ((q & NAME_variable) && !istype(*(s - 1), C_VARIABLE1|C_VARIABLE2))
                        q &= ~(NAME_identifier|NAME_variable);
                  else if ((q & NAME_identifier) && !istype(*(s - 1), C_ID1|C_ID2))
                        q &= ~NAME_identifier;
                  continue;
            }
            break;
      }
      if ((q & NAME_context) && *s == MARK_CONTEXT)
      {
            if (e)
                  *e = (char*)(s + 1);
            if (e)
                  *e = (char*)(s + 1);
            return NAME_context;
      }
      if (q & NAME_staterule)
      {
            if (*s == ')')
                  return NAME_statevar;
            if (*(name + 1) == '+')
                  return NAME_altstate;
            return NAME_staterule;
      }
      if (q & NAME_dynamic)
            return NAME_dynamic;
      if (q & NAME_assignment)
            return NAME_assignment;
      if (q & NAME_glob)
            return NAME_glob;
      if (q & NAME_identifier)
            return NAME_identifier;
      if (q & NAME_variable)
            return (q & NAME_intvar) && *s == '.' ? NAME_intvar : NAME_variable;
      return q & NAME_path;
}

/*
 * map name s to rule r
 * previous mappings must be reconciled with the prereq lists
 */

char*
maprule(char* s, Rule_t* r)
{
      register List_t*  p;
      Rule_t*                 q;
      Rule_t*                 o;
      Hash_position_t*  pos;

      static unsigned char    warned;

      if ((o = getrule(s)) == r)
            return r->name;
      s = putrule(0, r);
      if (o && (pos = hashscan(table.rule, 0)))
      {
            if (!warned)
            {
                  warned++;
                  if (state.warn)
                        error(1, "%d maprule() calls -- should not happen", UCHAR_MAX+1);
            }
            while (hashnext(pos))
            {
                  q = (Rule_t*)pos->bucket->value;
                  if (q == o)
                        pos->bucket->value = (char*)r;
                  for (p = q->prereqs; p; p = p->next)
                        if (p->rule == o)
                              p->rule = r;
            }
            hashdone(pos);
      }
      return s;
}

/*
 * return a pointer to a rule given its name,
 * creating the rule if necessary
 */

Rule_t*
makerule(register char* name)
{
      register Rule_t*  r;
      int               n;

      if (name)
      {
            if (r = getrule(name))
                  return r;
            if (((n = nametype(name, NiL)) & NAME_path) && (table.rule->flags & HASH_ALLOCATE))
            {
                  pathcanon(name, 0);
                  if (r = getrule(name))
                        return r;
            }
      }
      newrule(r);
      r->name = putrule(0, r);
      if (state.compnew)
            (*state.compnew)(r->name, (char*)r, state.comparg);
      if (!name)
            n = nametype(r->name, NiL);
      if (n & (NAME_staterule|NAME_altstate))
      {
            r->dynamic |= D_compiled;
            r->property |= P_state|P_staterule;
      }
      else if (n & NAME_statevar)
      {
            r->dynamic |= D_compiled;
            r->property |= P_state|P_statevar;
      }
      else if (state.init || state.readonly)
            r->dynamic |= D_compiled;
      else
            r->dynamic &= ~D_compiled;
      r->status = NOTYET;
      r->preview = state.maxview + 1;
#if DEBUG
      message((-13, "adding %s %s", (r->property & P_state) ? "state variable" : "atom", r->name));
#endif
      return r;
}

/*
 * check if a rule name is special
 * special names are ignored in determining the default main target(s)
 */

int
special(register Rule_t* r)
{
      register char*    s;

      if (r->property & (P_functional|P_metarule|P_operator|P_repeat|P_state))
            return 1;
      if ((s = r->name) && !istype(*s, C_ID2))
            for (;;)
                  switch (*s++)
                  {
                  case 0:
                        return 1;
                  case '/':
                  case '$':
                        return 0;
                  }
      return 0;
}

/*
 * return joint list pointer for r
 * static single element list returned for non-joint target
 */

List_t*
joint(register Rule_t* r)
{
      static List_t           tmp;

      if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
            return r->prereqs->rule->prereqs;
      tmp.rule = r;
      return &tmp;
}

/*
 * add a single non-duplicated prerequisite x to rule r
 * op = {PREREQ_APPEND,PREREQ_DELETE,PREREQ_INSERT,PREREQ_LENGTH}
 */

void
addprereq(register Rule_t* r, register Rule_t* x, int op)
{
      register List_t*  p;
      register List_t*  q;

      if (x != r)
      {
            if (p = r->prereqs)
            {
                  q = 0;
                  while (p)
                  {
                        if (x == p->rule)
                        {
                              if (op == PREREQ_DELETE)
                              {
                                    if (q)
                                          q->next = p->next;
                                    else
                                          r->prereqs = p->next;
                              }
                              if (!(x->property & P_multiple))
                                    break;
                        }
                        if (!p->next)
                        {
                              if (op != PREREQ_DELETE)
                              {
                                    if (r->property & P_state)
                                          state.savestate = 1;
                                    if (!state.init && !state.readonly)
                                          r->dynamic &= ~D_compiled;
                                    if (op == PREREQ_LENGTH)
                                    {
                                          register int      n;

                                          n = strlen(x->name);
                                          p = r->prereqs;
                                          q = 0;
                                          for (;;)
                                          {
                                                if (!p || strlen(p->rule->name) < n)
                                                {
                                                      if (q)
                                                            q->next = cons(x, p);
                                                      else
                                                            r->prereqs = cons(x, r->prereqs);
                                                      break;
                                                }
                                                q = p;
                                                p = p->next;
                                          }
                                    }
                                    else if (op == PREREQ_INSERT)
                                          r->prereqs = cons(x, r->prereqs);
                                    else p->next = cons(x, NiL);
                              }
                              break;
                        }
                        q = p;
                        p = p->next;
                  }
            }
            else if (op != PREREQ_DELETE)
            {
                  if (r->property & P_state)
                        state.savestate = 1;
                  if (!state.init && !state.readonly)
                        r->dynamic &= ~D_compiled;
                  r->prereqs = cons(x, NiL);
            }
      }
}

/*
 * return the pattern association rule with prefix a that matches r
 * if r is 0 then s is the rule name
 * if pos is not 0 then it is the last match position
 * *pos must be 0 on first call
 * *pos undefined when 0 returned
 */

Rule_t*
associate(register Rule_t* a, register Rule_t* r, register char* s, List_t** pos)
{
      register List_t*  p;
      register Rule_t*  x;
      register Rule_t*  z;
      List_t*                 u;

      if (r)
      {
            if (r->property & (P_attribute|P_readonly))
                  return 0;
            s = r->name;
      }
      do
      {
            u = 0;
            for (p = pos && *pos ? (*pos)->next : a->prereqs; p; p = p->next)
                  if ((x = p->rule) != r)
                  {
                        if (x->property & P_attribute)
                        {
                              if (r && (hasattribute(r, x, NiL) || !r->scan && x->scan && (z = staterule(RULE, r, NiL, -1)) && z->scan == x->scan))
                                    break;
                        }
                        else if (x->name[0] == '%' && !x->name[1])
                              u = p;
                        else if (metamatch(NiL, s, x->name) || r && r->uname && !(r->property & P_state) && metamatch(NiL, r->uname, x->name))
                              break;
                  }
            if (p || (p = u))
            {
                  if (pos)
                        *pos = p;
                  return (p->rule->property & P_attribute) ?
                        catrule(a->name, "%", p->rule->name, 0) :
                        catrule(a->name, p->rule->name, NiL, 0);
            }
      } while (r && s == r->name && r->uname && !(r->property & P_state) && (s = r->uname) != r->name);
      return 0;
}

/*
 * check if r's prerequisite list or named attributes have changed
 *
 * NOTE: IGNORECHANGE(r,q) prerequisites are ignored in the comparison
 */

#define IGNORECHANGE(r,q)     (((q)->property & (P_joint|P_ignore)) || ((q)->dynamic & D_alias) && getrule((q)->name) == r)

int
prereqchange(register Rule_t* r, register List_t* newprereqs, Rule_t* o, register List_t* oldprereqs)
{
      register List_t*  p;

      if ((r->property & P_accept) || state.accept)
            return 0;
      if ((r->property & (P_joint|P_target)) == (P_joint|P_target) && r != r->prereqs->rule->prereqs->rule)
            return 0;
      if ((r->attribute ^ o->attribute) & ~internal.accept->attribute)
      {
            reason((1, "%s named attributes changed", r->name));
            return 1;
      }
 more:
      for (;;)
      {
            if (newprereqs)
            {
                  if (IGNORECHANGE(r, newprereqs->rule))
                        newprereqs = newprereqs->next;
                  else if (oldprereqs)
                  {
                        if (IGNORECHANGE(r, oldprereqs->rule))
                              oldprereqs = oldprereqs->next;
                        else if (newprereqs->rule == oldprereqs->rule || ((newprereqs->rule->dynamic ^ oldprereqs->rule->dynamic) & (D_alias|D_bound)) && getrule(newprereqs->rule) == getrule(oldprereqs->rule))
                        {
                              newprereqs = newprereqs->next;
                              oldprereqs = oldprereqs->next;
                        }
                        else
                              break;
                  }
                  else
                        break;
            }
            else if (oldprereqs && IGNORECHANGE(r, oldprereqs->rule))
                  oldprereqs = oldprereqs->next;
            else
                  break;
      }
      if (newprereqs)
      {
            if ((r->dynamic & (D_entries|D_regular)) == D_entries || EXPLAIN)
            {
                  for (p = oldprereqs; p && (newprereqs->rule != p->rule || !((newprereqs->rule->dynamic ^ p->rule->dynamic) & (D_alias|D_bound)) || getrule(newprereqs->rule) != getrule(p->rule)); p = p->next);
                  if (p)
                  {
                        if ((r->dynamic & (D_entries|D_regular)) == D_entries)
                              goto more;
                        reason((1, "%s prerequisite %s re-ordered", r->name, newprereqs->rule->name));
                  }
                  else
                        reason((1, "%s prerequisite %s added", r->name, newprereqs->rule->name));
            }
            return 1;
      }
      if (oldprereqs)
      {
            reason((1, "%s prerequisite %s deleted", r->name, oldprereqs->rule->name));
            return 1;
      }
      return 0;
}

/*
 * initialize immediate rule info
 */

static void
getimmediate(register Rule_t* r, List_t** prereqs, char** action)
{
      if (r->dynamic & D_dynamic)
            dynamic(r);
      if (*action = r->action)
            r->action = 0;
      if (*prereqs = r->prereqs)
            r->prereqs = 0;
}

/*
 * reset all rule info as if we just started
 */

static int
reset(const char* s, char* v, void* h)
{
      register Rule_t*  r = (Rule_t*)v;
      Stat_t                  st;

      if (!(r->property & P_state))
      {
            if ((r->status == EXISTS || r->status == FAILED) && !(r->property & P_virtual))
            {
                  r->status = NOTYET;
                  if (!stat(r->name, &st))
                        r->time = tmxgetmtime(&st);
                  r->dynamic &= ~(D_entries|D_hasafter|D_hasbefore|D_hasmake|D_hasscope|D_hassemaphore|D_scanned|D_triggered);
            }
            else
                  r->dynamic &= ~(D_entries|D_scanned);
      }
      return 0;
}

/*
 * check rule r for immediate action
 * should only be called if r->immediate==1 and r->target==1
 */

void
immediate(register Rule_t* r)
{
      register List_t*  p;
      register Rule_t*  x;
      List_t*                 prereqs;
      char*             action;
      char*             e;
      Var_t*                  v;
      int               i;
      int               g;
      int               u;
      Flags_t                 a;
      Seconds_t         t;

      if (r == internal.retain || r == internal.state)
      {
            getimmediate(r, &prereqs, &action);
            a = r == internal.retain ? V_retain : V_scan;
            for (p = prereqs; p; p = p->next)
                  if (v = varstate(p->rule, -1))
                  {
                        if (a == V_scan)
                              setvar(v->name, v->value, V_scan);
                        else
                              v->property |= a;
                  }
      }
      else if (r == internal.rebind || r == internal.accept)
      {
            getimmediate(r, &prereqs, &action);
            i = r == internal.accept;
            for (p = prereqs; p; p = p->next)
                  rebind(p->rule, i);
      }
      else if (r == internal.unbind)
      {
            getimmediate(r, &prereqs, &action);
            for (p = prereqs; p; p = p->next)
                  if ((p->rule->dynamic & (D_bound|D_scanned)) && (!(p->rule->mark & M_bind) || (state.questionable & 0x02000000)))
                        p->rule->mark |= M_mark;
            hashwalk(table.rule, 0, unbind, r);
            hashwalk(table.rule, 0, unbind, NiL);
      }
      else if (r == internal.bind || r == internal.force)
      {
            getimmediate(r, &prereqs, &action);
            for (p = prereqs; p; p = p->next)
            {
                  x = p->rule;
                  message((-2, "bind(%s)", x->name));
                  x = bind(x);
                  if (r == internal.force)
                  {
                        if (x->time || !(x->property & P_dontcare))
                              x->time = CURTIME;
                        x->property |= P_force;
                  }
            }
      }
      else if (r == internal.always || r == internal.local || r == internal.make || r == internal.run)
      {
            int         errors;
            Time_t            now;
            Time_t            tm = 0;

            getimmediate(r, &prereqs, &action);
            i = !prereqs;
            now = CURTIME;
            errors = 0;
            for (p = prereqs; p; p = p->next)
            {
                  if ((p->rule->status == UPDATE || p->rule->status == MAKING) && !(p->rule->property & P_repeat))
                        p->rule = internal.empty;
                  else
                  {
                        errors += make(p->rule, &tm, NiL, 0);
                        if (tm >= now)
                              i = 1;
                  }
            }
            if (r != internal.run)
            {
                  if (prereqs)
                        errors += complete(NiL, prereqs, &tm, 0);
                  if (tm >= now)
                        i = 1;
                  if (action)
                  {
                        if (!errors && i)
                        {
                              r->status = UPDATE;
                              trigger(r, NiL, action, 0);
                              complete(r, NiL, NiL, 0);
                        }
                  }
                  if (r == internal.make)
                        r->property &= ~(P_always|P_local);
            }
            r->property &= ~(P_foreground|P_make|P_read);
      }
      else if (r == internal.include)
      {
            getimmediate(r, &prereqs, &action);
            i = COMP_INCLUDE;
            g = state.global;
            u = state.user;
            for (p = prereqs; p; p = p->next)
            {
                  x = p->rule;
                  if (streq(x->name, "-"))
                        i ^= COMP_DONTCARE;
                  else if (streq(x->name, "+"))
                  {
                        state.global = 1;
                        state.user = 0;
                  }
                  else if (x->property & P_use)
                        action = x->action;
                  else
                        readfile(x->name, i, action);
            }
            state.global = g;
            state.user = u;
      }
      else if (r == internal.alarm)
      {
            getimmediate(r, &prereqs, &action);
            if (p = prereqs)
            {
                  t = strelapsed(p->rule->name, &e, 1);
                  if (*e)
                        t = 0;
                  else
                        p = p->next;
            }
            else
                  t = 0;
            wakeup(t, p);
      }
      else if (r == internal.sync)
      {
            getimmediate(r, &prereqs, &action);
            if (!prereqs)
                  savestate();
            else if (state.compile < COMPILED)
                  error(2, "%s: cannot sync until make object compiled", prereqs->rule->name);
            else
                  compile(prereqs->rule->name, prereqs->next ? prereqs->next->rule->name : (char*)0);
      }
      else if (r == internal.reset)
      {
            getimmediate(r, &prereqs, &action);
            hashwalk(table.rule, 0, reset, NiL);
      }
      else if (r == internal.wait)
      {
            getimmediate(r, &prereqs, &action);
            complete(NiL, prereqs, NiL, 0);
      }
      else if (r == internal.freeze)
      {
            getimmediate(r, &prereqs, &action);
            for (p = prereqs; p; p = p->next)
                  if (v = getvar(p->rule->name))
                        v->property |= V_frozen;
      }
      else if ((r->property & P_attribute) && !r->attribute)
            return;
      else if (!state.op && state.reading && state.compileonly)
            return;
      else
      {
            maketop(r, 0, NiL);
            getimmediate(r, &prereqs, &action);
      }
      if (prereqs)
            freelist(prereqs);
      if (r->prereqs)
      {
            freelist(r->prereqs);
            r->prereqs = 0;
      }
      r->action = 0;
      if (r = staterule(RULE, r, NiL, 0))
      {
            r->prereqs = 0;
            r->action = 0;
      }
}

/*
 * remove duplicate prerequisites from prerequisite list p
 */

void
remdup(register List_t* p)
{
      register List_t*  q;
      register List_t*  x;

      for (x = p, q = 0; p; p = p->next)
      {
            if (p->rule->mark & M_mark)
            {
                  if (q)
                        q->next = p->next;
#if DEBUG
                  else
                  {
                        dumprule(sfstderr, p->rule);
                        error(PANIC, "stray mark on %s", p->rule->name);
                  }
#endif
            }
            else
            {
                  if (!(p->rule->property & P_multiple))
                        p->rule->mark |= M_mark;
                  q = p;
            }
      }
      while (x)
      {
            x->rule->mark &= ~M_mark;
            x = x->next;
      }
}

/*
 * do dynamic expansion of rule r's prerequisites
 * the prerequisite list is permanently modified
 */

void
dynamic(register Rule_t* r)
{
      register char*          s;
      register List_t*  p;
      register List_t*  q;
      register List_t*  t;
      char**                  v;
      char*             buf;
      int               added;
      int               flags;
      Rule_t*                 x;
      Rule_t*                 u;
      Frame_t*          oframe;
      Frame_t                 frame;
      Sfio_t*                 tmp;
      char*             vec[2];

      tmp = sfstropen();
      oframe = state.frame;
      if ((r->property & P_use) && !(state.frame = r->active))
      {
            zero(frame);
            frame.target = r;
            state.frame = frame.parent = &frame;
      }
      vec[1] = 0;
      added = 0;
      for (p = r->prereqs, q = 0; p; p = p->next)
      {
            if (isdynamic(p->rule->name))
            {
                  if (q)
                        q->next = p->next;
                  else
                        r->prereqs = p->next;
                  u = p->rule;
                  expand(tmp, u->name);
                  buf = sfstruse(tmp);
                  flags = 0;
                  while (s = getarg(&buf, &flags))
                  {
                        added = 1;
                        if (isglob(s))
                              v = globv(NiL, s);
                        else
                              *(v = vec) = s;
                        while (s = *v++)
                        {
                              x = makerule(s);
                              if (x->dynamic & D_alias)
                                    x = makerule(x->name);
                              if (flags & A_scope)
                                    x->dynamic |= D_scope;

                              /*
                               * merge u into x
                               */

                              merge(u, x, MERGE_ALL|MERGE_SCANNED);

                              /*
                               * replace u with x
                               */

                              t = cons(x, p->next);
                              if (q)
                                    q->next = t;
                              else
                                    r->prereqs = t;
                              q = t;
                        }
                  }
            }
            else q = p;
      }
      if (added)
            remdup(r->prereqs);
      r->dynamic &= ~D_dynamic;
      state.frame = oframe;
      sfstrclose(tmp);
}

/*
 * return non-zero if r has builtin attribute a
 * if x!=0 then check if merge(x,r,MERGE_ATTR) would have attribute
 */

int
hasattribute(register Rule_t* r, register Rule_t* a, register Rule_t* x)
{
      register Flags_t  n;
      register int            attrname;
      List_t*                 p;

      attrname = *a->name == ATTRNAME;
      if (a->property & P_attribute)
      {
            n = r->property;
            if (x && !(n & P_readonly))
            {
                  n |= x->property & ~(P_attribute|P_immediate|P_implicit|P_internal|P_operator|P_readonly|P_state|P_staterule|P_statevar|P_target);
                  if ((x->property & P_implicit) || (x->property & (P_metarule|P_terminal)) == P_terminal)
                        n &= ~P_implicit;
            }

            /*
             * the first group may conflict with a->attribute
             */

            if (attrname)
            {
                  if (a == internal.accept) return n & P_accept;
                  if (a == internal.attribute) return n & P_attribute;
                  if (a == internal.ignore) return n & P_ignore;
            }
            if (a != internal.retain)
            {
                  if (a->attribute & r->attribute) return 1;
                  if (x && (a->attribute & x->attribute & ~internal.ignore->attribute) && ((r->property & (P_attribute|P_use)) != P_attribute || r == internal.accept || r == internal.ignore || r == internal.retain)) return 1;
            }
            if (attrname)
            {
                  /*
                   * the rest have no a->attribute conflicts
                   */

                  if (a == internal.make) return n & P_make;
                  if (a == internal.readonly) return n & P_readonly;
                  if (a == internal.scan) return r->scan || x && x->scan;
                  if (a == internal.semaphore) return r->semaphore;
                  if (a == internal.target) return n & P_target;

                  /*
                   * the rest have a corresponding P_*
                   */

                  if (a->property & n & ~(P_accept|P_attribute|P_ignore|P_internal|P_metarule|P_readonly|P_staterule|P_statevar|P_target))
                  {
                        if (a == internal.after) return n & P_after;
                        if (a == internal.always) return n & P_always;
                        if (a == internal.archive) return n & P_archive;
                        if (a == internal.before) return n & P_before;
                        if (a == internal.command) return n & P_command;
                        if (a == internal.dontcare) return n & P_dontcare;
                        if (a == internal.force) return n & P_force;
                        if (a == internal.foreground) return n & P_foreground;
                        if (a == internal.functional) return n & P_functional;
                        if (a == internal.immediate) return n & P_immediate;
                        if (a == internal.implicit) return n & P_implicit;
                        if (a == internal.joint) return n & P_joint;
                        if (a == internal.local) return n & P_local;
                        if (a == internal.multiple) return n & P_multiple;
                        if (a == internal.op) return n & P_operator;
                        if (a == internal.parameter) return n & P_parameter;
                        if (a == internal.read) return n & P_read;
                        if (a == internal.repeat) return n & P_repeat;
                        if (a == internal.state) return n & P_state;
                        if (a == internal.terminal) return n & P_terminal;
                        if (a == internal.use) return n & P_use;
                        if (a == internal.virt) return n & P_virtual;
                  }
            }
            if (a->scan) return a->scan == r->scan || !r->scan && x && x->scan == a->scan;
      }
      else
      {
            if (attrname)
            {
                  /*
                   * r->dynamic readonly attributes
                   */

                  n = r->dynamic;
                  if (a == internal.bound) return n & D_bound;
                  if (a == internal.built) return n & D_built;
                  if (a == internal.entries) return n & D_entries;
                  if (a == internal.global) return n & D_global;
                  if (a == internal.member) return n & (D_member|D_membertoo);
                  if (a == internal.regular) return n & D_regular;
                  if (a == internal.scanned) return n & D_scanned;
                  if (a == internal.source) return n & D_source;
                  if (a == internal.triggered) return n & D_triggered;
                  if (a == internal.file) return (n & D_bound) && !(r->property & (P_state|P_virtual)) && r->time;

                  /*
                   * r->property readonly attributes
                   */

                  n = r->property;
                  if (a == internal.internal) return n & P_internal;
                  if (a == internal.metarule) return n & P_metarule;
                  if (a == internal.staterule) return n & P_staterule;
                  if (a == internal.statevar) return n & P_statevar;

                  /*
                   * r->status readonly attributes
                   */

                  n = r->status;
                  if (a == internal.exists) return n == EXISTS;
                  if (a == internal.failed) return n == FAILED;
                  if (a == internal.making) return n == MAKING;
                  if (a == internal.notyet) return n == NOTYET;

                  /*
                   * other readonly attributes
                   */

                  if (a == internal.active) return r->active != 0;
            }

            /*
             * r prerequisites as pseudo attributes
             */

            for (p = r->prereqs; p; p = p->next)
                  if (a == p->rule)
                        return 1;
      }
      return 0;
}

/*
 * return nonzero if r has an after prerequisite with exact property
 */

int
hasafter(register Rule_t* r, register Flags_t property)
{
      register List_t*  p;

      if (r->dynamic & D_hasafter)
            for (p = r->prereqs; p; p = p->next)
                  if ((p->rule->property & P_failure) == property)
                        return 1;
      return 0;
}

/*
 * merge <from> into <to> according to op
 */

void
merge(register Rule_t* from, register Rule_t* to, int op)
{
      register List_t*  p;

      if (from->name)
      {
            if (from == to || to->status != NOTYET && (to->status != UPDATE || !(from->property & P_use)))
                  return;
#if DEBUG
            if (to->name)
                  debug((-4, "merging %s%s into %s", (op & MERGE_ATTR) ? "attributes of " : null, from->name, to->name));
#endif
      }
      if (!(to->dynamic & D_bound) || (op & (MERGE_ASSOC|MERGE_FORCE)))
            to->property |= from->property & (P_accept|P_after|P_always|P_archive|P_before|P_command|P_dontcare|P_force|P_foreground|P_functional|P_ignore|P_implicit|P_joint|P_local|P_make|P_multiple|P_parameter|P_read|P_repeat|P_terminal|P_virtual);
      else
            to->property |= from->property & (P_accept|P_after|P_always|P_archive|P_before|P_command|P_force|P_foreground|P_functional|P_implicit|P_joint|P_local|P_make|P_multiple|P_parameter|P_read|P_repeat|P_terminal|P_virtual);
      if (from->property & P_implicit)
            to->property &= ~P_terminal;
      if ((from->property & (P_metarule|P_terminal)) == P_terminal)
            to->property &= ~P_implicit;
      if (op & MERGE_ALL)
      {
            if (!to->action)
                  to->action = from->action;
            to->attribute |= from->attribute;
            to->property |= from->property & (P_accept|P_immediate|P_target|P_use);
            to->dynamic |= from->dynamic & (D_dynamic|D_global|D_regular);
            if (!(op & MERGE_SCANNED))
                  to->dynamic |= from->dynamic & (D_entries|D_scanned);
            if (from->scan && from->scan != SCAN_NULL)
                  to->scan = from->scan;
            if (to->status == NOTYET)
                  to->status = from->status;
            for (p = from->prereqs; p; p = p->next)
                  addprereq(to, p->rule, PREREQ_APPEND);
            if (!(to->property & P_state))
            {
                  if (op & MERGE_BOUND)
                  {
                        from->mark |= M_bind;
                        to->mark |= M_bind;
                  }
                  mergestate(from, to);
                  if (op & MERGE_BOUND)
                  {
                        from->mark &= ~M_bind;
                        to->mark &= ~M_bind;
                  }
            }
      }
      else if (op & MERGE_FORCE)
      {
            if (from->attribute && from != internal.accept && from != internal.ignore && from != internal.retain && ((to->property & (P_attribute|P_use)) != P_attribute || to == internal.accept || to == internal.ignore || to == internal.retain))
                  to->attribute |= from->attribute;
            if (from->scan)
                  to->scan = from->scan;
      }
      else
      {
            if (from->attribute && from != internal.accept && from != internal.ignore && from != internal.retain && ((to->property & (P_attribute|P_use)) != P_attribute || to == internal.accept || to == internal.ignore || to == internal.retain))
                  to->attribute |= from->attribute & ~internal.ignore->attribute;
            if (!to->scan)
                  to->scan = from->scan;
      }
}

/*
 * merge <from> state rules into <to>
 */

void
mergestate(Rule_t* from, Rule_t* to)
{
      register int            i;
      register Rule_t*  fromstate;
      register Rule_t*  tostate;
      Rule_t*                 t;
      char*             s;

      /*
       * if RULE merges then RULE+1..STATERULES also merge
       */

      if (fromstate = staterule(RULE, from, NiL, 0))
            tostate = staterule(RULE, to, NiL, 1);
      else if ((from->dynamic & D_alias) && (tostate = staterule(RULE, to, NiL, 0)))
            fromstate = staterule(RULE, from, NiL, 1);
      else
            return;
#if DEBUG
      if (state.test & 0x00000800)
      {
            error(2, "MERGESTATE from: %s: %s time=[%s] event=[%s]", from->name, fromstate->name, timestr(fromstate->time), timestr(fromstate->event));
            error(2, "MERGESTATE   to: %s: %s time=[%s] event=[%s]", to->name, tostate->name, timestr(tostate->time), timestr(tostate->event));
      }
#endif
      if ((from->dynamic & D_alias) && fromstate->time && !statetimeq(fromstate, tostate))
      {
            /*
             * the solution is conservative but ok
             * since aliases probably don't change
             * very often -- every target depending
             * on <to> will be forced out of date rather
             * than those that just depend on <from>
             */

            reason((1, "%s alias has changed to %s", unbound(from), unbound(to)));
            to->dynamic |= D_aliaschanged;
      }
      if (fromstate->event != tostate->event)
      {
            if (fromstate->event < tostate->event)
            {
                  /*
                   * merge in the other direction
                   */

                  t = from;
                  from = to;
                  to = t;
                  t = fromstate;
                  fromstate = tostate;
                  tostate = t;
            }
            s = tostate->name;
            *tostate = *fromstate;
            tostate->prereqs = listcopy(fromstate->prereqs);
            tostate->name = s;
            for (i = RULE + 1; i <= STATERULES; i++)
                  if ((fromstate = staterule(i, from, NiL, 0)) && !staterule(i, to, NiL, 0))
                  {
                        tostate = staterule(i, to, NiL, 1);
                        s = tostate->name;
                        *tostate = *fromstate;
                        tostate->prereqs = listcopy(fromstate->prereqs);
                        tostate->name = s;
                  }
      }
}

/*
 * negate <from> attributes in <to>
 */

void
negate(register Rule_t* from, register Rule_t* to)
{
      to->attribute &= ~from->attribute;
      to->property &= ~(from->property & (P_accept|P_after|P_always|P_archive|P_before|P_command|P_dontcare|P_force|P_functional|P_ignore|P_immediate|P_implicit|P_local|P_make|P_multiple|P_parameter|P_repeat|P_target|P_terminal|P_use|P_virtual));
      to->dynamic &= ~(from->dynamic & (D_dynamic|D_entries|D_regular));
      if (from->scan)
            to->scan = 0;
      if (from->semaphore)
            to->semaphore = 0;
}

/*
 * make an internal rule pointer
 */

static Rule_t*
rinternal(char* s, register int flags)
{
      register Rule_t*  r;

      r = makerule(s);
      r->property |= flags;
      if (!r->prereqs && !r->action)
            r->dynamic |= D_compiled;
      return r;
}

/*
 * make an internal pattern association rule pointer
 * NOTE: this is required to sync pre 2001-05-09 make objects 
 */

static Rule_t*
rassociate(char* s, register int flags)
{
      register Rule_t*  r;
      register Rule_t*  a;
      register List_t*  p;

      r = rinternal(s, flags);
      for (p = r->prereqs; p; p = p->next)
            if (!(p->rule->property & P_attribute) && p->rule->name[0] == '%' && p->rule->name[1] == ATTRNAME && (a = getrule(p->rule->name + 1)) && (a->property & P_attribute))
                  p->rule = a;
      return r;
}

/*
 * maintain atomic dir-rule names
 */

static int
diratom(const char* s, char* v, void* h)
{
      Dir_t*            d = (Dir_t*)v;

      NoP(s);
      NoP(h);
      d->name = getrule(d->name)->name;
      return 0;
}

/*
 * # outstanding jobs builtin
 */

static char*
b_outstanding(char** args)
{
      sfprintf(internal.val, "%d", state.coshell ? state.coshell->outstanding : 0);
      return sfstruse(internal.val);
}

/*
 * getconf() builtin
 */

static char*
b_getconf(char** args)
{
      char* name;
      char* path;
      char* value;

      if (name = *args)
            args++;
      if (path = *args)
      {
            if (path[0] == '-' && !path[1])
                  path = 0;
            args++;
      }
      if ((value = *args) && value[0] == '-' && !value[1])
            value = 0;
      return astconf(name, path, value);
}

typedef int (*Systab_f)(void);

typedef struct Systab_s
{
      const char* name;
      Systab_f    call;
} Systab_t;

static const Systab_t   systab[] =
{
      "getegid",  (Systab_f)getegid,
      "geteuid",  (Systab_f)geteuid,
      "getgid",   (Systab_f)getgid,
      "getpid",   (Systab_f)getpid,
      "getppid",  (Systab_f)getppid,
      "getuid",   (Systab_f)getuid,
      {0}
};

/*
 * void arg system call catchall builtin
 */

static char*
b_syscall(char** args)
{
      Systab_t*   call;

      if (!*args)
            return null;
      sfprintf(internal.val, "%d", (call = (Systab_t*)strlook(systab, sizeof(systab[0]), *args)) ? (*call->call)() : -1);
      return sfstruse(internal.val);
}

/*
 * external engine name initialization -- the rest are in initrule()
 *
 * NOTE: version.c may reference some of these names, not to mention
 *     non-engine source makefiles
 */

External_t  external =
{
      /*
       * variable names
       */

      "MAKEARGS",
      "MAKECONVERT",
      "MAKEFILE",
      "MAKEFILES",
      "MAKEIMPORT",
      "MAKELIB",
      "MAKE",
      CO_ENV_PROC,
      "OLDMAKE",
      "PWD",
      "MAKERULES",
      "MAKESKIP",
      "MAKEVERSION",
      "MAKEPATH",
      "VPATH",

      /*
       * infrequently used engine interface names
       */

      ".COMPDONE",
      ".COMPINIT",
      ".DONE",
      ".INIT",
      ".INTERRUPT",
      ".JOBDONE",
      ".MAKEDONE",
      ".MAKEINIT",
      ".MAKEPROMPT",
      ".MAKERUN",
      ".MAMNAME.",
      ".MAMACTION.",
      ".ORDER",

      /*
       * related file suffixes
       */

      ".ml",
      ".mo",
      ".mk",
      ".ms",
      ".mt",
};

/*
 * initialize some attribute and internal rule pointers
 */

void
initrule(void)
{
      Rule_t*           r;
      int         i;

      static int  repeat;

      hashclear(table.rule, HASH_ALLOCATE);

      /*
       * dynamic rule attributes
       */

      ATTR(accept,            ".ACCEPT",  P_accept|P_immediate);
      ATTR(after,       ".AFTER",   P_after);
      ATTR(always,            ".ALWAYS",  P_always|P_immediate);
      ATTR(archive,           ".ARCHIVE", P_archive);
      ATTR(attribute,         ".ATTRIBUTE",     P_readonly);
      ATTR(before,            ".BEFORE",  P_before);
      ATTR(command,           ".COMMAND", P_command);
      ATTR(dontcare,          ".DONTCARE",      P_dontcare);
      ATTR(force,       ".FORCE",   P_force|P_immediate);
      ATTR(foreground,  ".FOREGROUND",    P_foreground);
      ATTR(functional,  ".FUNCTIONAL",    P_functional);
      ATTR(ignore,            ".IGNORE",  P_ignore);
      ATTR(immediate,         ".IMMEDIATE",     P_immediate);
      ATTR(implicit,          ".IMPLICIT",      P_implicit);
      ATTR(joint,       ".JOINT",   P_joint);
      ATTR(local,       ".LOCAL",   P_local|P_immediate);
      ATTR(make,        ".MAKE",    P_immediate);
      ATTR(multiple,          ".MULTIPLE",      P_multiple);
      ATTR(op,          ".OPERATOR",      P_operator);
      ATTR(parameter,         ".PARAMETER",     P_parameter);
      ATTR(read,        ".READ",    P_read);
      ATTR(readonly,          ".READONLY",      P_readonly);
      ATTR(repeat,            ".REPEAT",  P_repeat);
      ATTR(state,       ".STATE",   P_state|P_immediate);
      ATTR(terminal,          ".TERMINAL",      P_terminal);
      ATTR(use,         ".USE",           P_use);
      ATTR(virt,        ".VIRTUAL", P_virtual);

      /*
       * anonymous attributes (no internal handle)
       */

      ANON(             ".FAILURE", P_failure);

      /*
       * readonly rule attributes
       */

      INIT(active,            ".ACTIVE",  0);
      INIT(bound,       ".BOUND",   0);
      INIT(built,       ".BUILT",   0);
      INIT(entries,           ".ENTRIES", 0);
      INIT(exists,            ".EXISTS",  0);
      INIT(failed,            ".FAILED",  0);
      INIT(file,        ".FILE",    0);
      INIT(global,            ".GLOBAL",  0);
      INIT(member,            ".MEMBER",  0);
      INIT(notyet,            ".NOTYET",  0);
      INIT(regular,           ".REGULAR", 0);
      INIT(scanned,           ".SCANNED", 0);
      INIT(staterule,         ".STATERULE",     0);
      INIT(statevar,          ".STATEVAR",      0);
      INIT(target,            ".TARGET",  P_attribute|P_target);
      INIT(triggered,         ".TRIGGERED",     0);

      /*
       * special rules and names
       */

      INIT(alarm,       ".ALARM",   P_immediate);
      INIT(args,        ".ARGS",    P_internal);
      INIT(bind,        ".BIND",    P_immediate);
      INIT(clear,       ".CLEAR",   P_attribute);
      INIT(copy,        ".COPY",    P_attribute);
      INIT(delete,            ".DELETE",  P_attribute);
      INIT(dot,         ".",        0);
      INIT(empty,       "",         0);
      INIT(error,       ".ERROR",   0);
      INIT(exported,          ".EXPORT",  P_attribute|P_immediate);
      INIT(freeze,            ".FREEZE",  P_immediate);
      INIT(globalfiles, ".GLOBALFILES",   P_internal|P_readonly);
      INIT(include,           ".INCLUDE", P_immediate);
      INIT(insert,            ".INSERT",  P_attribute);
      INIT(internal,          ".INTERNAL",      P_internal|P_readonly);
      INIT(main,        ".MAIN",    0);
      INIT(makefiles,         ".MAKEFILES",     P_internal|P_readonly);
      INIT(making,            ".MAKING",  0);
      INIT(metarule,          ".METARULE",      0);
      INIT(null,        ".NULL",    P_attribute);
      INIT(preprocess,  ".PREPROCESS",    P_internal|P_readonly);
      INIT(query,       ".QUERY",   P_immediate|P_multiple|P_virtual);
      INIT(rebind,            ".REBIND",  P_immediate);
      INIT(reset,       ".RESET",   P_immediate);
      INIT(retain,            ".RETAIN",  P_immediate);
      INIT(run,         ".RUN",           P_immediate);
      INIT(scan,        ".SCAN",    P_attribute|P_readonly);
      INIT(semaphore,         ".SEMAPHORE",     P_attribute);
      INIT(source,            ".SOURCE",  0);
      INIT(special,           ".SPECIAL", P_attribute|P_internal|P_readonly);
      INIT(sync,        ".SYNC",    P_immediate);
      INIT(tmplist,           ".TMPLIST", P_internal|P_readonly);
      INIT(unbind,            ".UNBIND",  P_immediate);
      INIT(view,        ".VIEW",    P_internal|P_readonly);
      INIT(wait,        ".WAIT",    P_immediate);

      /*
       * pattern association rules
       */

      ASOC(append_p,          ".APPEND.", 0);
      ASOC(assert_p,          ".ASSERT.", 0);
      ASOC(assign_p,          ".ASSIGN.", 0);
      ASOC(attribute_p, ".ATTRIBUTE.",    0);
      ASOC(bind_p,            ".BIND.",   0);
      ASOC(dontcare_p,  ".DONTCARE.",     0);
      ASOC(insert_p,          ".INSERT.", 0);
      ASOC(require_p,         ".REQUIRE.",      0);
      ASOC(source_p,          ".SOURCE.", 0);

      /*
       * builtin functions
       */

      FUNC(             ".GETCONF", b_getconf);
      FUNC(             ".OUTSTANDING",   b_outstanding);
      FUNC(             ".SYSCALL", b_syscall);

#if DEBUG
      putrule(".DEBUG", internal.query);
#endif
      hashset(table.rule, HASH_ALLOCATE);

      /*
       * initialize the builtin attributes
       */

      if (!repeat)
      {
            static Frame_t          frame;

            frame.target = internal.internal;
            state.frame = frame.parent = &frame;
            internal.attribute->attribute = 1;
            internal.empty->dynamic |= D_bound;
            internal.empty->status = IGNORE;
            internal.empty->time = OLDTIME;
            internal.scan->scan = SCAN_USER;
            addprereq(internal.source, internal.dot, PREREQ_APPEND);
            addprereq(internal.special, internal.attribute, PREREQ_APPEND);
            addprereq(internal.special, internal.scan, PREREQ_APPEND);
            sfprintf(internal.tmp, "%s?(.*)", internal.source->name);
            internal.issource = strdup(sfstruse(internal.tmp));
      }
      initscan(repeat);
      initwakeup(repeat);

      /*
       * maintain atomic dir-rule names
       */

      hashwalk(table.dir, 0, diratom, NiL);
#if !BINDINDEX
      for (i = 0; i <= state.maxview; i++)
      {
            r = getrule(state.view[i].path);
            r->view = i;
            state.view[i].path = r->name;
      }
#endif

      /*
       * expand some dynamic values now for efficiency
       */

      if (internal.metarule->dynamic & D_dynamic)
            dynamic(internal.metarule);

      /*
       * some things are only done once
       */

      repeat++;
}

/*
 * low level for initview()
 */

static List_t*
view(register char* s, register char* d, List_t* p)
{
      register int            i;
      register Rule_t*  r;

      if (!d)
            d = s;
      i = pathcanon(d, 0) - d;
      if (i > 2 && d[i - 1] == '/')
            d[--i] = 0;
      r = makerule(d);
      if ((unique(r) || !r->time) && !streq(r->name, internal.dot->name) && !streq(r->name, internal.pwd))
      {
            if (state.maxview < MAXVIEW - 1)
            {
#if BINDINDEX
                  r->dynamic |= D_bindindex;
                  state.view[++state.maxview].path = r;
#else
                  state.view[++state.maxview].path = r->name;
#endif
                  state.view[state.maxview].pathlen = i;
                  r->view = state.maxview;
                  p = p->next = cons(r, NiL);
                  if (s != d)
                  {
                        i = pathcanon(s, 0) - s;
                        if (i > 2 && s[i - 1] == '/')
                              s[--i] = 0;
                        r = makerule(s);
                        r->view = state.maxview;
                  }
                  state.view[state.maxview].root = r->name;
                  state.view[state.maxview].rootlen = i;
#if BINDINDEX
                  message((-2, "view[%d]: %s %s", state.maxview, state.view[state.maxview].path->name, state.view[state.maxview].root));
#else
                  message((-2, "view[%d]: %s %s", state.maxview, state.view[state.maxview].path, state.view[state.maxview].root));
#endif
            }
            else
                  error(1, "view level %s ignored -- %d max", r->name, MAXVIEW);
      }
      return p;
}

/*
 * initialize the views
 */

void
initview(void)
{
      register char*    s;
      register char*    t;
      register int      n;
      int         c;
      int         pwdlen;
      char*       pwd;
      char*       tok;
      char*       u;
      List_t*           p;
      Rule_t*           r;
      Stat_t            top;
      Stat_t            bot;
      Sfio_t*           tmp;

      p = internal.view->prereqs = cons(internal.dot, NiL);
      tmp = sfstropen();
      if (fs3d(FS3D_TEST))
      {
            if ((n = (s = colonlist(tmp, external.viewnode, 1, ' ')) != 0) || (s = colonlist(tmp, external.viewdot, 1, ' ')))
            {
                  tok = tokopen(s, 0);
                  if (s = n ? tokread(tok) : ".")
                        while (t = tokread(tok))
                        {
                              message((-2, "vpath %s %s", s, t));
                              mount(t, s, FS3D_VIEW, NiL);
                              s = t;
                        }
                  tokclose(tok);
            }
            for (n = 1; n <= MAXVIEW; n++)
                  sfputr(tmp, "/...", -1);
            sfputc(tmp, 0);
            s = t = sfstrseek(tmp, 0, SEEK_CUR);
            while (!stat(t -= 4, &top))
            {
                  if (state.maxview >= MAXVIEW - 1)
                  {
                        error(1, "view levels past %s ignored -- %d max", t += 4, MAXVIEW);
                        break;
                  }
                  p = p->next = cons(r = makerule(t), NiL);
#if BINDINDEX
                  state.view[++state.maxview].path = r;
#else
                  state.view[++state.maxview].path = r->name;
#endif
                  state.view[state.maxview].pathlen = s - t;
                  message((-2, "view[%d]: %s", state.maxview, r->name));
            }
            state.fsview = 1;
            setvar(external.viewnode, NiL, 0);
            if (!stat(".", &top) && !stat("...", &bot) && top.st_ino == bot.st_ino && top.st_dev == bot.st_dev)
                  state.virtualdot = 1;
      }
      else
      {
            unique(internal.dot);
            if (s = colonlist(tmp, external.viewnode, 1, ' '))
            {
                  tok = tokopen(s, 1);
                  while (s = tokread(tok))
                  {
                        if (*s != '/')
                              sfprintf(internal.tmp, "%s/", internal.pwd);
                        sfputr(internal.tmp, s, -1);
                        t = sfstruse(internal.tmp);
                        s = pathcanon(t, 0);
                        if (*(s - 1) == '/')
                              *--s = 0;
                        n = s - t;
                        pwd = 0;
                        if (strncmp(internal.pwd, t, n) || (c = internal.pwd[n]) && c != '/')
                        {
                              /*
                               * ksh pwd and ast getcwd() are logical
                               * others are physical
                               * this gets PWD and VPATH in sync
                               * if they're not
                               */

                              if (!stat(t, &top))
                              {
                                    sfputr(internal.nam, internal.pwd, -1);
                                    u = sfstruse(internal.nam);
                                    c = 0;
                                    for (;;)
                                    {
                                          if (!stat(u, &bot) && bot.st_ino == top.st_ino && bot.st_dev == top.st_dev)
                                          {
                                                sfprintf(internal.nam, "%s%s", t, internal.pwd + (s - u));
                                                pwd = strdup(sfstruse(internal.nam));
                                                pwdlen = strlen(pwd);
                                                break;
                                          }
                                          if (!(s = strrchr(u, '/')))
                                                break;
                                          *s = 0;
                                          c = 1;
                                    }
                              }
                              if (!pwd)
                              {
                                    setvar(external.viewnode, NiL, 0);
                                    break;
                              }
                        }
                        if (pwd)
                        {
                              internal.pwd = pwd;
                              internal.pwdlen = pwdlen;
                              setvar(external.pwd, internal.pwd, V_import);
                        }
                        state.view[0].root = makerule(t)->name;
                        state.view[0].rootlen = n;
#if BINDINDEX
                        message((-2, "view[%d]: %s %s", state.maxview, state.view[state.maxview].path->name, state.view[state.maxview].root));
#else
                        message((-2, "view[%d]: %s %s", state.maxview, state.view[state.maxview].path, state.view[state.maxview].root));
#endif
                        while (s = tokread(tok))
                        {
                              if (!c)
                                    p = view(s, NiL, p);
                              else
                              {
                                    if (*s != '/')
                                          sfprintf(internal.tmp, "%s/", internal.pwd);
                                    sfprintf(internal.tmp, "%s%s", s, internal.pwd + n);
                                    p = view(s, sfstruse(internal.tmp), p);
                              }
                        }
                        if (!(optflag(OPT_strictview)->flags & OPT_READONLY))
                              state.strictview = 1;
                        break;
                  }
                  tokclose(tok);
            }
            if (s = colonlist(tmp, external.viewdot, 1, ' '))
            {
                  n = state.maxview;
                  tok = tokopen(s, 1);
                  while (s = tokread(tok))
                  {
                        pathcanon(s, 0);
                        if (!n || *s != '.' || *(s + 1) != '.' || *(s + 2) && *(s + 2) != '/')
                              p = view(s, NiL, p);
                        else for (c = 0; c <= n; c++)
                        {
#if BINDINDEX
                              sfprintf(internal.tmp, "%s/%s", c ? state.view[c].path->name : internal.pwd, s);
#else
                              sfprintf(internal.tmp, "%s/%s", c ? state.view[c].path : internal.pwd, s);
#endif
                              p = view(sfstruse(internal.tmp), NiL, p);
                        }
                  }
                  tokclose(tok);
            }
      }
      if (!state.maxview)
            state.believe = 0;
      sfstrclose(tmp);
}

Generated by  Doxygen 1.6.0   Back to index