Logo Search packages:      
Sourcecode: ksh version File versions

object.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
 *
 * compiler and loader for the architecture independent makefile object format
 *
 * The format fields are labeled by sequence number and type:
 *
 *    # sfputu()/sfgetu()
 *    $ size,string (no trailing 0)
 *    @ 0 terminated string
 *
 * The format is designed for per-section backward/forward compatible
 * additions to the header, rules, variables and trailer sections, with
 * the proviso that the section order is not changed and that sequence 1
 * fields must appear in all future formats.  This means that the
 * property, dynamic and status field bit values are permanently fixed
 * by each sequence.  Field addition semantics are controlled by the
 * sequence number.
 *
 * header:
 *
 *    1 4 magic   must match
 *    1 @ ident   identification string (information only)
 *    1 # size    header size
 *    1 # sequence      to label additions/changes
 *    1 # flags   OBJ_* flags
 *    1 # strings string table size
 *    1 # lists   number of lists
 *    1 # rules   number of rules
 *    1 # rulenum (RULENUM-MINRULENUM)
 *    1 # rulestr (RULESTR-MINRULESTR)
 *    1 # variables     number of variables
 *    1 # varnum  (VARNUM-MINVARNUM)
 *    1 # varstr  (VARSTR-MINVARSTR)
 *    1 4 magic   again for verification
 *    * # ...           [header number fields additions here]
 *
 * optional headers:
 *
 *    1 # size    header size
 *    1 # type    header type
 *    * * ...           header contents
 *
 * HEADER_PREREQS:
 *
 *    1 # type    {0:end COMP_*:type}
 *    1 # time    time
 *    1 @ name    unbound name
 *        ...
 *
 * variables:
 *
 *    1 # property
 *    * # ...           [variable number field additions here]
 *    1 $ name    name string
 *    1 $ value   value string
 *    * $ ...           [variable string field additions here]
 *
 * rules:
 *
 *    1 # property
 *    1 # dynamic
 *    1 # attribute
 *    1 # encoded status|semaphore|view|scan
 *    1 # prereqs prereq list index
 *    1 # time    rule time
 *    1 # nsec    rule nsec [2004-12-01]
 *    1 # eventnsec     event nsec [2004-12-01]
 *    * # ...           [rule number field additions here]
 *    1 $ name    name string
 *    1 $ action  action string
 *    1 $ data    event time or state string
 *    * $ ...           [rule string field additions here]
 *
 * lists:
 *
 *    1 # rule    rule index
 *
 * trailer:
 *
 *    1 @ options option string
 *    * @ ...           [trailer string fields additions here]
 *
 * NOTE: the old format compatibility code should probably be dropped in 95
 * NOTE: HA -- as of 1997-08-11 2.1 was still in production use
 */

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

#include <ccode.h>

/*
 * old rule load() replacement puns on struct rule
 *
 * rule.mark is not used as load() may be triggered
 * by staterule() while marks are in use
 */

#define getoldrule(r)   ((Rule_t*)r->action)
#define isoldrule(r)    (r->status==OLDRULE)
#define setoldrule(r,o) (r->status=OLDRULE,r->action=(char*)o,r->prereqs=(List_t*)oldrules,oldrules=r)

#define MAGIC           "\015\001\013\005"
#define MAGICSIZE (sizeof(MAGIC)-1)

#define SEQUENCE  1           /* track semantic diffs       */
#define HEADERSIZE      128         /* handle largest header      */

#define HEADER_PREREQS  1           /* prereqs optional header    */

#define MINRULENUM      6           /* min # rule number fields   */
#define MINRULESTR      3           /* min # rule string fields   */
#define MINVARNUM 1           /* min # variable number fields     */
#define MINVARSTR 2           /* min # variable string fields     */

#define RULENUM         (MINRULENUM+2)    /* # rule number fields       */
#define RULESTR         (MINRULESTR+0)    /* # rule string fields       */
#define VARNUM          (MINVARNUM+0)     /* # variable number fields   */
#define VARSTR          (MINVARSTR+0)     /* # variable string fields   */

typedef struct Compstate_s          /* compile state        */
{
      char*       sp;         /* string table pointer       */
      Sfio_t*           fp;         /* object file pointer        */
      unsigned long     lists;            /* list index                 */
      unsigned long     rules;            /* rule index                 */
      unsigned long     strings;    /* string index               */
      unsigned long     variables;  /* variable index       */
} Compstate_t;

typedef struct Loadstate_s          /* load state                 */
{
      char*       sp;         /* string table pointer       */
} Loadstate_t;

static struct Object_s              /* object global state        */
{
      Sfio_t*           pp;         /* prerequisite ref pointer   */
      unsigned char*    a2n;        /* CC_ASCII=>CC_NATIVE        */
      unsigned char*    n2a;        /* CC_NATIVE=>CC_ASCII        */
      unsigned long     garbage;    /* state garbage count        */
      unsigned long     rules;            /* state rule count           */
      int         initialized;      /* state initialized          */
      int         lowres;           /* low resolution time state  */
} object;

/*
 * old object format compatibility
 * frozen 1992-12-25
 */

#define OLD_MAGIC 0x0d010b05
#define OLD_OLD_MAGIC   0x0000ff5d
#define OLD_VERSION     "AT&T Bell Laboratories 08/11/89"
#define OLD_VERSION_2   "AT&T Bell Laboratories 01/24/89"
#define OLD_SEQUENCE    2
#define OLD_ALIGN 8

#define old_data  u2.u_data
#define old_event u2.u_event

struct OLD_list_s; typedef struct OLD_list_s OLD_list_t;

typedef struct OLD_header_s         /* old make object file header      */
{
      long        magic;            /* magic number               */
      char        version[32];      /* compiler/loader version    */
      unsigned char     null;       /* 0 byte for long version's  */
      unsigned char     sequence;   /* different still compatible */
      unsigned char     sizes[10];  /* misc size checks           */
} OLD_header_t;

typedef struct OLD_trailer_s        /* old make object file trailer     */
{
      long        magic;            /* magic number               */
      char*       options;    /* options for set()          */
      long        lists;            /* number of compiled lists   */
      long        rules;            /* number of compiled rules   */
      long        size;       /* total sizeof object file   */
      long        variables;  /* number of compiled variables     */
} OLD_trailer_t;

typedef struct OLD_rule_s           /* old rule             */
{
      char*       name;       /* rule name                  */

      union
      {
      Frame_t*    u_active;   /* active target frame        */
      unsigned long     u_complink; /* compilation link           */
      Rule_t*           u_freelink; /* free list link       */
      }           u1;

      union
      {
      char*       u_uname;    /* unbound name               */
      char*       u_data;           /* state value                */
      unsigned long     u_event;    /* state rule event time      */
      }           u2;

      OLD_list_t* prereqs;    /* prerequisites        */
      char*       action;           /* update action        */
      unsigned long     time;       /* modify time                */

      long        attribute;  /* external named attributes  */
      long        dynamic;    /* dynamic properties         */
      long        property;   /* stable properties          */

#define noswap          scan        /* 0 or char elts after here  */

      unsigned char     scan;       /* file scan strategy index   */
      unsigned char     semaphore;  /* semaphore + count          */
      unsigned char     status;           /* disposition                */
      unsigned char     view;       /* view bind index            */

#if BINDINDEX
      unsigned char     source;           /* source bind index          */
#else
      unsigned char     spare_1;    /* spare                */
#endif
      unsigned char     preview;    /* min prereq view            */

      unsigned short    must;       /* cancel if == 0       */

      char*       runtime;    /* run time info        */
} OLD_rule_t;

typedef struct OLD_var_s            /* old variable               */
{
      char*       name;       /* name                       */
      char*       value;            /* value                */
      long        property;   /* static and dynamic         */
      long        length;           /* maximum length of value    */
} OLD_var_t;

struct OLD_list_s             /* old rule cons cell         */
{
      OLD_list_t* next;       /* next in list               */
      OLD_rule_t* rule;       /* list item                  */
};

static OLD_header_t     old_stamp = /* old object header is fixed */
{
      OLD_MAGIC,
      OLD_VERSION,
      0,
      OLD_SEQUENCE,
      OLD_ALIGN,
      CHAR_BIT,
      sizeof(char),
      sizeof(short),
      sizeof(long),
      sizeof(char*),
      sizeof(OLD_list_t),
      sizeof(OLD_rule_t),
      sizeof(OLD_var_t),
      0,
};

/*
 * initialize the object ccode tables
 */

void
initcode(void)
{
      if (!object.initialized)
      {
            object.initialized = 1;
            object.a2n = ccmap(CC_ASCII, CC_NATIVE);
            object.n2a = ccmap(CC_NATIVE, CC_ASCII);
      }
}

/*
 * read canonical 0 terminated string from object file
 */

static char*
getstring(Sfio_t* sp)
{
      char* s;

      if (s = sfgetr(sp, 0, 0))
            ccmapstr(object.a2n, s, sfvalue(sp));
      return s;
}

/*
 * write canonical 0 terminated string to file
 */

static void
putstring(register Sfio_t* sp, register const char* s, int sep)
{
      register int            c;
      register unsigned char* map;

      if (map = object.n2a)
      {
            while (c = *(unsigned char*)s++)
                  sfputc(sp, map[c]);
            if (sep >= 0)
                  sfputc(sp, map[sep]);
      }
      else
            sfputr(sp, s, sep);
}

/*
 * recursively mark r and its prerequisites for compilation
 */

static void
markcompile(register Rule_t* r)
{
      register List_t*  p;

      r->dynamic &= ~D_compiled;
      r->mark |= M_compile;
      for (p = r->prereqs; p; p = p->next)
            if (!(p->rule->mark & M_compile))
                  markcompile(p->rule);
}

/*
 * mark state file garbage candidates
 */

static void
markgarbage(register Rule_t* r, int garbage)
{
      register List_t*  p;
      register int            i;
      Rule_t*                 x;

      r->mark |= M_compile;
      if (garbage)
            r->dynamic |= D_garbage;
      else
            r->dynamic &= ~D_garbage;
      for (p = r->prereqs; p; p = p->next)
            if (!(p->rule->mark & M_compile))
                  markgarbage(p->rule, garbage);
      for (i = RULE; i <= STATERULES; i++)
            if ((x = staterule(i, r, NiL, 0)) && !(x->mark & M_compile))
                  markgarbage(x, garbage);
}

/*
 * compile a string
 */

static void
compstring(register Compstate_t* cs, register char* s)
{
      register int            c;
      register unsigned char* map;

      if (s)
      {
            c = strlen(s) + 1;
            cs->strings += c;
            sfputu(cs->fp, c);
            if (map = object.n2a)
                  while (c = *s++)
                        sfputc(cs->fp, map[c]);
            else
                  sfwrite(cs->fp, s, c - 1);
      }
      else
            sfputu(cs->fp, 0);
}

/*
 * initialize rules for compilation
 * if h!=0 then all rules marked compiled
 */

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

      NoP(s);
      r->complink = 0;
      r->mark &= ~M_compile;
      if (r->dynamic & D_alias)
      {
            state.compnew = compinit;
            state.comparg = h;
            mergestate(makerule(r->name), r);
            state.compnew = 0;
      }
      if (h || (r->property & P_internal))
            r->dynamic |= D_compiled;
      return 0;
}

/*
 * mark selected rules and immediate prereqs for compilation
 *
 * NOTE: remember to clear r->mark (from markcompile())
 */

static int
compselect(const char* s, char* v, void* h)
{
      register Rule_t*  r = (Rule_t*)v;
      register char*          select = (char*)h;

      NoP(s);
      if (!(r->mark & M_compile) && (r->dynamic & D_compiled))
      {
            state.frame->target = r;
            expand(internal.met, select);
            if (sfstrtell(internal.met))
            {
                  sfstrseek(internal.met, 0, SEEK_SET);
                  markcompile(r);
            }
      }
      return 0;
}

/*
 * mark rules so that only state vars and immediate prereqs will be compiled
 *
 * NOTE: remember to clear r->mark (from markgarbage())
 */

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

      NoP(s);
      NoP(h);
      r->dynamic |= D_compiled;
      if (r->dynamic & D_garbage)
      {
            if (!object.garbage)
                  r->dynamic &= ~D_garbage;
            else if (!(r->mark & M_compile))
                  markgarbage(r, 1);
      }
      return 0;
}

/*
 * mark prerequisites for compilation
 *
 * NOTE: remember to clear r->mark (from markcompile())
 */

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

      NoP(s);
      NoP(h);
      r->complink = 0;
      if (state.stateview == 0)
      {
            if ((r->property & P_state) && !r->view && !(r->dynamic & (D_garbage|D_lower)))
            {
                  r->dynamic &= ~D_compiled;
                  for (p = r->prereqs; p; p = p->next)
                        p->rule->dynamic &= ~D_compiled;
            }
      }
      else if (!(r->dynamic & D_compiled) && !(r->mark & M_compile))
            markcompile(r);
      return 0;
}

/*
 * weed out the real garbage
 */

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

      NoP(s);
      NoP(h);
      if (!(r->mark & M_compile) && !(r->dynamic & D_garbage))
            markgarbage(r, 0);
      return 0;
}

/*
 * compile an individual rule
 */

static int
comprule(const char* s, char* v, void* h)
{
      register Rule_t*  r = (Rule_t*)v;
      register List_t*  p;
      register Compstate_t*   cs = (Compstate_t*)h;
      Rule_t                  x;

      NoP(s);

      /*
       * compile each rule only once
       */

      if ((r->dynamic & D_compiled) || s != r->name && !(r->dynamic & D_alias))
            return 0;
      r->dynamic |= D_compiled;
      r->mark |= M_compile;
#if DEBUG
      if (r->property & P_internal)
            error(1, "internal rule %s should not be compiled", r->name);
#endif

      /*
       * set the current rule index for prerequisite list compilation
       */

      r->complink = cs->rules++;
      x = *r;
      r = &x;

      /*
       * make sure the unbound rule name is compiled
       */

      if (!(r->property & P_state))
      {
            if (state.stateview == 0)
            {
                  r->prereqs = 0;
                  r->action = 0;
                  r->dynamic &= ~D_compiled;
            }
            if (r->uname && !(r->property & P_metarule))
            {
                  r->name = r->uname;
                  r->uname = 0;
            }
            r->time = 0;
#if BINDINDEX
            r->view = 0;
#endif
      }
#if !BINDINDEX
      r->view = 0;
#endif

      /*
       * compile the fields
       */

      sfputu(cs->fp, r->property);
      sfputu(cs->fp, r->dynamic & ~D_CLEAROBJECT);
      sfputu(cs->fp, r->attribute);
      sfputu(cs->fp, (r->semaphore<<16)|(r->view<<8)|(r->scan));
      if (p = r->prereqs)
      {
            sfputu(cs->fp, cs->lists);
            do cs->lists++; while (p = p->next);
      }
      else
            sfputu(cs->fp, 0);
      sfputu(cs->fp, tmxsec(r->time));

      /*
       * 2004-12-01
       */

      sfputu(cs->fp, tmxnsec(r->time));
      sfputu(cs->fp, (r->property & P_staterule) ? tmxnsec(r->event) : 0);

      compstring(cs, r->name);
      compstring(cs, r->action);
      if (r->property & P_staterule)
            sfputu(cs->fp, tmxsec(r->event));
      else
            compstring(cs, r->statedata);
      return 0;
}

/*
 * a final pass before complist() to catch any prereqs
 * that eluded comprule()
 */

static int
compcheck(const char* s, char* v, void* h)
{
      register Rule_t*        r = (Rule_t*)v;
      register List_t*        p;
      register Rule_t*        a;

      /*
       * ignore aliases and rules not set up by comprule()
       */

      if (!r->complink || !(r->mark & M_compile) || s != r->name && !(r->dynamic & D_alias) || state.stateview == 0 && !(r->property & P_state))
            return 0;
      if (p = r->prereqs)
      {
            do
            {
                  for (r = p->rule; !r->complink || !(r->dynamic & D_compiled); r = a)
                        if ((!(a = getrule(r->name)) || a == r) && ((r->property & P_state) || !r->uname || !(a = getrule(r->uname)) || a == r))
                        {
                              if (state.warn)
                                    error(1, "forcing %s %s prerequisite %s", s, a ? "duplicate" : "dangling", r->name);
                              r->dynamic &= ~D_compiled;
                              comprule(r->name, (char*)r, h);
                              if (!p->rule->complink)
                                    p->rule->complink = r->complink;
                              break;
                        }
            } while (p = p->next);
      }
      return 0;
}

/*
 * compile the prerequisite list for r
 */

static int
complist(const char* s, char* v, void* h)
{
      register Rule_t*  r = (Rule_t*)v;
      register List_t*  p;
      register Compstate_t*   cs = (Compstate_t*)h;

      /*
       * ignore aliases and rules not set up by comprule()
       */

      if (!r->complink || !(r->mark & M_compile) || s != r->name && !(r->dynamic & D_alias) || state.stateview == 0 && !(r->property & P_state))
            return 0;
      r->mark &= ~M_compile;
      if (p = r->prereqs)
      {
            do sfputu(cs->fp, p->rule->complink); while (p = p->next);
            sfputu(cs->fp, 0);
      }
      return 0;
}

/*
 * compile an individual variable
 */

static int
compvar(const char* s, char* u, void* h)
{
      register Var_t*         v = (Var_t*)u;
      register Compstate_t*   cs = (Compstate_t*)h;
      char*             t;
      unsigned long           property;
      char*             value;

      /*
       * compile each variable only once
       * don't compile command arg variable definitions
       */

      if ((v->property & V_compiled) || state.stateview < 0 && !(v->property & V_frozen) && ((v->property & V_import) || (v->property & (V_oldvalue|V_readonly)) == V_readonly) || state.stateview == 0 && !(v->property & V_retain))
            return 0;
      v->property |= V_compiled;
      property = v->property;
      value = v->value;

      /*
       * check for possible old value
       *
       * if v->oldvalue is set in load() then the
       * variable is frozen and the frozen value
       * is different than the makefile value
       */

      if (state.stateview == 0)
            property &= ~V_CLEAROBJECT;
      else
      {
            property &= ~V_CLEARSTATE;
            if (property & V_oldvalue)
            {
                  if (t = getold(v->name))
                  {
                        if (!(property & V_frozen))
                        {
                              value = t;
                              property &= ~V_oldvalue;
                        }
                        else if (streq(value, t))
                              property &= ~V_oldvalue;
                  }
#if DEBUG
                  else
                        error(PANIC, "%s->oldvalue set but not in table.oldvalue", s);
#endif
            }
            else if ((property & (V_frozen|V_readonly)) == (V_frozen|V_readonly))
                  property |= V_oldvalue;
      }

      /*
       * write the variable fields
       */

      sfputu(cs->fp, property);
      compstring(cs, v->name);
      compstring(cs, value);
      cs->variables++;
      return 0;
}

/*
 * clear temporary marks on r
 */

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

      NoP(s);
      NoP(h);
      r->complink = 0;
      r->mark &= ~M_compile;
      return 0;
}

/*
 * compile the current rules and variables into objfile
 */

void
compile(char* objfile, char* select)
{
      register Sfio_t*  sp;
      List_t*                 p;
      List_t*                 q;
      Rule_t*                 r;
      Compstate_t       cs;

      /*
       * initialize the object globals
       */

      zero(cs);
      cs.lists++;
      cs.rules++;

      /*
       * create the object temporary file
       */

      sp = sfstropen();
      edit(sp, objfile, KEEP, KEEP, external.tmp);
      state.tmpfile = strdup(sfstruse(sp));
      sfstrclose(sp);
      if (!(cs.fp = sfopen(NiL, state.tmpfile, "brw")))
      {
            error(ERROR_SYSTEM|1, "%s: cannot create temporary object file", state.tmpfile);
            return;
      }

      /*
       * skip the header until everything else is done
       */

      if (sfseek(cs.fp, (Sfoff_t)HEADERSIZE, SEEK_SET) != HEADERSIZE)
            error(ERROR_SYSTEM|3, "%s: object file header write error", state.tmpfile);

      /*
       * write the optional headers
       */

      if (sp = object.pp)
      {
            object.pp = 0;
            if (!state.base)
            {
                  sfputu(sp, COMP_OPTIONS);
                  sfputu(sp, 0);
                  for (p = internal.preprocess->prereqs; p; p = p->next)
                        putstring(sp, p->rule->name, p->next ? ' ' : -1);
                  sfputc(sp, 0);
                  sfputu(sp, 0);
                  sfputu(internal.tmp, HEADER_PREREQS);
                  sfputu(cs.fp, sfstrtell(internal.tmp) + sfstrtell(sp));
                  sfstrseek(internal.tmp, 0, SEEK_SET);
                  sfputu(cs.fp, HEADER_PREREQS);
                  sfwrite(cs.fp, sfstrbase(sp), sfstrtell(sp));
            }
            sfstrclose(sp);
      }
      sp = cs.fp;
      sfputu(sp, 0);
      if (sferror(sp))
            error(ERROR_SYSTEM|3, "%s: object file optional header write error", state.tmpfile);

      /*
       * mark the rules and prerequisites for compilation
       */

      if (select)
      {
            Sfio_t*     tmp;

            hashwalk(table.rule, 0, compinit, null);
            tmp = sfstropen();
            sfprintf(tmp, "$(<:V:%s)", select);
            r = state.frame->target;
            hashwalk(table.rule, 0, compselect, sfstruse(tmp));
            state.frame->target = r;
            sfstrclose(tmp);
      }
      else
      {
            /*
             * compile and write the variables
             */

            hashwalk(table.var, 0, compvar, &cs);
            if (sferror(sp))
                  error(ERROR_SYSTEM|3, "%s: object file variable write error", state.tmpfile);
            hashwalk(table.rule, 0, compinit, NiL);
            if (state.stateview == 0)
            {
                  /*
                   * check state file garbage collection
                   *
                   * NOTE: only the head of each garbage list is
                   *     counted so the percentage threshold
                   *     should probably be low
                   */

                  if ((100 * object.garbage / (object.rules ? object.rules : 1) < PCTGARBAGE && !(state.test & 0x00008000)))
                        object.garbage = 0;
                  hashwalk(table.rule, 0, compstate, &cs);
                  if (object.garbage)
                  {
                        hashwalk(table.rule, 0, clearmarks, &cs);
                        hashwalk(table.rule, 0, compkeep, &cs);
                  }
#if BINDINDEX
                  for (n = 1; n <= state.maxsource; n++)
                        markcompile(state.source[n].path);
                  for (n = 1; n <= state.maxview; n++)
                        markcompile(state.view[n].path);
#endif
            }
      }
      hashwalk(table.rule, 0, compmark, &cs);

      /*
       * some rules must always be compiled and/or appear first
       */

      for (p = internal.special->prereqs; p; p = p->next)
            markcompile(p->rule);
      for (p = internal.special->prereqs; p; p = p->next)
            for (q = p->rule->prereqs; q; q = q->next)
                  comprule(q->rule->name, (char*)q->rule, &cs);
#if BINDINDEX
      if (state.stateview == 0)
      {
            for (n = 1; n <= state.maxsource; n++)
            {
                  x = state.source[n].path;
                  comprule(x->name, x);
            }
            for (n = 1; n <= state.maxview; n++)
            {
                  x = state.view[n].path;
                  comprule(x->name, x);
            }
      }
#endif

      /*
       * compile and write the rules
       */

      hashwalk(table.rule, 0, comprule, &cs);
      if (sferror(sp))
            error(ERROR_SYSTEM|3, "%s: object file rule write error", state.tmpfile);

      /*
       * despite the effort a few elusive prereqs manage to avoid comprule()
       * the compcheck() pass sets up complink for these prereqs
       */

      for (p = internal.special->prereqs; p; p = p->next)
            for (q = p->rule->prereqs; q; q = q->next)
                  compcheck(q->rule->name, (char*)q->rule, &cs);
      hashwalk(table.rule, 0, compcheck, &cs);

      /*
       * compile and write the prerequisite lists
       */

      for (p = internal.special->prereqs; p; p = p->next)
            for (q = p->rule->prereqs; q; q = q->next)
                  complist(q->rule->name, (char*)q->rule, &cs);
      hashwalk(table.rule, 0, complist, &cs);
      if (sferror(sp))
            error(ERROR_SYSTEM|3, "%s: object file prerequisite write error", state.tmpfile);

      /*
       * write the trailer
       */

      if (state.stateview < 0)
      {
            /*
             * pre 2004-09-09 will just do "--"
             */

            putstring(sp, "--", 0);
            if (object.n2a)
            {
                  listops(internal.wrk, '@');
                  putstring(sp, sfstruse(internal.wrk), 0);
            }
            else
            {
                  listops(sp, '@');
                  sfputc(sp, 0);
            }
      }
      sfputc(sp, 0);

      /*
       * clear temporary marks
       */

      hashwalk(table.rule, 0, clearmarks, &cs);

      /*
       * write the real header
       */

      sfseek(sp, (Sfoff_t)0, SEEK_SET);
      sfwrite(sp, MAGIC, MAGICSIZE);
      putstring(sp, version, 0);
      sfputu(sp, HEADERSIZE);
      sfputu(sp, SEQUENCE);
      sfputu(sp, 0);
      sfputu(sp, cs.strings);
      sfputu(sp, cs.lists - 1);
      sfputu(sp, cs.rules - 1);
      sfputu(sp, RULENUM - MINRULENUM);
      sfputu(sp, RULESTR - MINRULESTR);
      sfputu(sp, cs.variables);
      sfputu(sp, VARNUM - MINVARNUM);
      sfputu(sp, VARSTR - MINVARSTR);
      sfwrite(sp, MAGIC, MAGICSIZE);
      if (sferror(sp))
            error(ERROR_SYSTEM|3, "%s: temporary object file header write error", state.tmpfile);

      /*
       * commit to the temporary object and clean up
       */

      sfclose(sp);
      remove(objfile);
      if (rename(state.tmpfile, objfile))
            error(1, "%s: object file not recompiled", objfile);
      remtmp(0);
      if (state.stateview == 0)
      {
            Stat_t            st;
            Time_t            t;
            Time_t            x;

            /*
             * set the state file times to the latest
             * possible event time modulo the file
             * system time precision
             */

            t = CURTIME;
            x = tmxsns(tmxsec(t), 999999999);
            if (!tmxtouch(objfile, x, x, TMX_NOTIME, 0) && !stat(objfile, &st))
            {
                  t += tmxsns(1,0) - tmxnsec(tmxgetmtime(&st));
                  tmxtouch(objfile, t, t, TMX_NOTIME, 0);
            }
      
      }
      r = bindfile(NiL, objfile, BIND_FORCE|BIND_DOT|BIND_RULE);
      r->dynamic |= D_built|D_regular;
      r->view = 0;
      if (state.mam.dynamic || state.mam.regress)
      {
            mampush(state.mam.out, r, P_force);
            sfprintf(state.mam.out, "%sexec %s : compile into %s object\n", state.mam.label, state.mam.dynamic ? mamname(r) : null, error_info.id);
            mampop(state.mam.out, r, 0);
      }
      if (state.stateview == 0 && object.garbage && object.garbage > cs.rules)
            message((-1, "%d%% [%d/%d] state file garbage collection recovery", (object.garbage - cs.rules) * 100 / object.garbage, object.garbage - cs.rules, object.garbage));
}

/*
 * register input file prerequisite
 */

void
compref(Rule_t* r, int type)
{
      if (object.pp)
      {
            if (r)
            {
                  /*
                   * COMP_NSEC for subsecond granularity
                   * and bind checks
                   * ignored by old implementations
                   */

                  sfputu(object.pp, COMP_NSEC);
                  sfputu(object.pp, tmxnsec(r->time));
                  putstring(object.pp, r->name, 0);
                  sfputu(object.pp, type);
                  sfputu(object.pp, tmxsec(r->time));
                  putstring(object.pp, unbound(r), 0);
            }
            else
            {
                  sfstrclose(object.pp);
                  object.pp = 0;
            }
      }
      else if (!r && !state.makefile)
            object.pp = sfstropen();
}

/*
 * promote lower view prereqs of top view state
 */

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

      NoP(s);
      NoP(h);
      if (r->mark & M_compile)
      {
            r->mark &= ~M_compile;
            for (p = r->prereqs; p; p = p->next)
            {
                  r = p->rule;
                  if (r->dynamic & D_lower)
                  {
                        unviewname(r->name);
                        p->rule = makerule(r->name);
                        viewname(r->name, r->view);
                  }
            }
      }
      return 0;
}

/*
 * associate one rule with each name
 */

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

      NoP(s);
      NoP(h);
      if (isoldrule(r))
      {
#if DEBUG
            error(PANIC, "old rule %s still in table.rule%s", r->name, r == getrule(r->name) ? null : " -- duplicate hash");
#endif
            return 0;
      }
      for (p = r->prereqs; p; p = p->next)
            if (isoldrule(p->rule))
                  p->rule = getoldrule(p->rule);
      return 0;
}

/*
 * repair prereq list corruption
 */

static int
repair(const char* s, char* v, void* h)
{
      register Rule_t*  r = (Rule_t*)v;
      register List_t*  p;
      register List_t*  q;

      NoP(s);
      NoP(h);
      p = 0;
      q = r->prereqs;
      while (q)
            if (q->rule)
            {
                  p = q;
                  q = q->next;
            }
            else if (p)
                  p->next = q = q->next;
            else
                  r->prereqs = q = q->next;
      return 0;
}

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

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

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

/*
 * remove temporary compilation files
 */

void
remtmp(int fatal)
{
      if (fatal && state.stateview == 0 && state.tmpfile)
            error(2, "%s: state file not updated", statefile());
      if (state.tmpfile)
      {
            if (remove(state.tmpfile) && errno != ENOENT)
                  error(ERROR_SYSTEM|1, "%s: temporary file not removed", state.tmpfile);
            free(state.tmpfile);
            state.tmpfile = 0;
      }
      if (fatal)
            lockstate(0);
}

/*
 * load a string
 */

static char*
loadstring(Loadstate_t* ls, Sfio_t* sp)
{
      register int      n;
      register char*    s;

      if (!(n = sfgetu(sp)) || sfeof(sp))
            return 0;
      s = ls->sp;
      ls->sp += n--;
      sfread(sp, s, n);
      ccmapstr(object.a2n, s, n);
      return s;
}

/*
 * initialize object state
 */

static void
loadinit(void)
{
      object.garbage = object.rules = 0;
}

/*
 * check if file is loadable object
 * if source!=0 then source prereqs are checked
 */

int
loadable(register Sfio_t* sp, register Rule_t* r, int source)
{
      register List_t*  p;
      char*             s;
      char*             u;
      char*             sn;
      long              n;
      Sfoff_t                 off;
      Time_t                  t;
      Time_t                  tm;
      Time_t                  tn;
      int               lowres;
      int               ok = 1;
      long              old = 0;
      Rule_t*                 x;
      Stat_t                  st;

      loadinit();
      if ((s = sfreserve(sp, 0, 0)) && (n = sfvalue(sp)) >= 0)
      {
            if (n >= MAGICSIZE && !memcmp(s, MAGIC, MAGICSIZE) || n >= sizeof(old) && ((old = OLD_MAGIC, swapop(s, &old, sizeof(old)) >= 0) || (old = OLD_OLD_MAGIC, swapop(s, &old, sizeof(old)) >= 0)))
            {
                  if (!source)
                        return state.base || !state.forceread || state.global || state.list;

                  /*
                   * check previous source prerequisites
                   */

                  if (!old && !sfseek(sp, (Sfoff_t)0, SEEK_SET) && sfseek(sp, (Sfoff_t)MAGICSIZE, SEEK_SET) == MAGICSIZE)
                  {
                        if (getstring(sp) && (off = sfgetu(sp)) && sfgetu(sp))
                              while (!sfeof(sp) && sfseek(sp, off, SEEK_SET) == off && (off = sfgetu(sp)))
                              {
                                    off += sfseek(sp, (Sfoff_t)0, SEEK_CUR);
                                    if (sfgetu(sp) == HEADER_PREREQS)
                                    {
                                          /*UNDENT...*/

      state.init++;
      lowres = 1;
      sn = 0;
      tn = 0;
      while (n = sfgetu(sp))
      {
            tm = sfgetu(sp);
            if (!(s = getstring(sp)))
                  break;
            if (n & (COMP_BASE|COMP_FILE|COMP_GLOBAL|COMP_INCLUDE))
            {
                  if (x = bindfile(NiL, s, BIND_MAKEFILE|BIND_RULE))
                  {
                        s = x->name;
                        t = x->time;

                        /*
                         * put bound makefile prereqs in state as a query courtesy
                         */

                        if (!(n & COMP_BASE) && *x->name != '/')
                              staterule(RULE, x, NiL, 1)->time = t;
                        if (!(x->dynamic & D_regular))
                              x->dynamic &= ~D_bound;
                  }
                  else
                        t = 0;
                  if (!t && sn && !stat(sn, &st))
                        t = tmxgetmtime(&st);
                  if (!t)
                  {
                        if (n & COMP_DONTCARE)
                              continue;
                        error(state.exec || state.mam.out ? -1 : 1, "%s: %s not found", r->name, s);
                        break;
                  }
                  if (tn && t == tmxsns(tm, 0))
                        tn = 0;
                  tm = (lowres && tm == tmxsec(t)) ? t : tmxsns(tm, tn);

                  /*
                   * check prerequisite file time with previous
                   */

                  debug((-4, "%s%s%s%s%sprerequisite %s [%s] state [%s]", (n & COMP_DONTCARE) ? "optional " : null, (n & COMP_BASE) ? "base " : null, (n & COMP_FILE) ? "-f " : null, (n & COMP_GLOBAL) ? "-g " : null, (n & COMP_INCLUDE) ? "include " : null, s, timestr(t), timestr(tm)));
                  if (t != tm)
                  {
                        if (sn && !streq(sn, x->name))
                              error(state.exec || state.mam.out ? -1 : 1, "%s: binding changed to %s from %s", s, x->name, sn);
                        else
                              error(state.exec || state.mam.out ? -1 : 1, "%s: out of date with %s", r->name, s);
                        break;
                  }
                  sn = 0;
                  tn = 0;

                  /*
                   * check that explicit prerequisite still specified
                   */

                  if (n & (COMP_FILE|COMP_GLOBAL))
                  {
                        for (p = ((n & COMP_FILE) ? internal.makefiles : internal.globalfiles)->prereqs; p; p = p->next)
                              if (!(p->rule->mark & M_compile))
                              {
                                    if (streq(p->rule->name, s) || p->rule->uname && streq(p->rule->uname, s))
                                          p->rule->mark |= M_compile;
                                    else
                                    {
                                          error(state.exec || state.mam.out ? -1 : 1, "%s: %sfile %s option order changed", r->name, (n & COMP_GLOBAL) ? "global " : null, s);
                                          goto nope;
                                    }
                                    break;
                              }
                        if (!p)
                        {
                              error(state.exec || state.mam.out ? -1 : 1, "%s: %sfile %s was specified last time", r->name, (n & COMP_GLOBAL) ? "global " : null, s);
                              break;
                        }
                  }
                  else if ((n & COMP_BASE) && !state.rules)
                  {
                        if (n & COMP_RULES)
                              state.explicitrules = 1;
                        state.rules = makerule(s)->name;
                  }
            }
            else if (n & COMP_NSEC)
            {
                  if (*s)
                  {
                        sfputr(internal.met, s, -1);
                        sn = sfstruse(internal.met);
                  }
                  tn = tm;
                  lowres = 0;
            }
            else if (n & COMP_OPTIONS)
            {
                  /*
                   * compare with current preprocessor options
                   */

                  for (p = internal.preprocess->prereqs; p; p = p->next)
                        sfputr(internal.nam, p->rule->name, p->next ? ' ' : -1);
                  u = sfstruse(internal.nam);
                  if (!streq(s, u))
                  {
                        error(state.exec || state.mam.out ? -1 : 1, "%s: options changed%s%s", r->name, *s ? " from " : null, s);
                        break;
                  }
            }
      }
 nope:
      state.init--;
      if (n)
            ok = 0;

      /*
       * check for explicit file prereqs not specified last time
       */

      for (p = internal.globalfiles->prereqs; p; p = p->next)
            if (p->rule->mark & M_compile)
                  p->rule->mark &= ~M_compile;
            else if (ok)
            {
                  ok = 0;
                  if (state.writeobject)
                        error(state.exec || state.mam.out ? -1 : 1, "%s: global file %s not specified last time", r->name, p->rule->name);
            }
      for (p = internal.makefiles->prereqs; p; p = p->next)
            if (p->rule->mark & M_compile)
                  p->rule->mark &= ~M_compile;
            else if (ok)
            {
                  ok = 0;
                  if (state.writeobject)
                        error(state.exec || state.mam.out ? -1 : 1, "%s: file %s not specified last time", r->name, p->rule->name);
            }
      if (ok && !sfseek(sp, (Sfoff_t)0, SEEK_SET))
            return 1;

                                          /*...INDENT*/
                                          break;
                                    }
                              }
                        sfseek(sp, (Sfoff_t)0, SEEK_SET);
                  }
            }
      }
      return 0;
}

/*
 * load compiled rules and variables from objfile
 * return:
 *    -1    partially loaded => punt
 *     0    not loaded
 *     1    loaded
 */

int
load(register Sfio_t* sp, const char* objfile, int ucheck)
{
      register int            n;
      register Rule_t*  r;
      register Var_t*         v;
      register List_t*  d;
      register char*          s;
      char*             p = 0;
      int               promoted = 0;
      int               recompile = 0;
      char*             corrupt = "2005-03-01";
      Rule_t*                 oldrules = 0;
      Rule_t*                 or;
      Rule_t*                 xr;
      Var_t*                  ov;
      Var_t*                  xv;
      Var_t*                  x;
      List_t*                 xd;
      List_t*                 a;
      int               flags;
      int               strings;
      int               lists;
      int               rules;
      int               rulenum;
      int               rulestr;
      int               variables;
      int               varnum;
      int               varstr;
      int               attrclash;
      int               garbage;
      int               oscan;
      int               scanclash;
      int               sequence;
      int               lowres;
      unsigned long           attr;
      unsigned long           attrclear;
      unsigned long           oattribute;
      unsigned long           ts;
      unsigned long           tn;
      Frame_t*          fp;
      Sfoff_t                 off;
      Stat_t                  st;
      unsigned long           attrmap[CHAR_BIT * sizeof(unsigned long)];
      unsigned char           scanmap[UCHAR_MAX + 1];
      char              ident[64];
      Loadstate_t       ls;

      int               old;
      int               old_swap;
      long              old_magic;
      OLD_rule_t        old_rule;
      OLD_var_t         old_var;
      OLD_list_t        old_list;
      OLD_header_t            old_header;
      OLD_trailer_t           old_trailer;

      loadinit();
      zero(ls);

      /*
       * empty object files are ok
       */

      if (fstat(sffileno(sp), &st))
      {
            error(1, "%s: cannot stat object file", objfile);
            return 0;
      }
      if (!st.st_size)
      {
            if (state.stateview >= 0)
            {
                  error(1, "%s: empty state file", objfile);
                  return 0;
            }
            return 1;
      }

      /*
       * check for other users within last ucheck minutes
       */

      if (ucheck && st.st_uid != geteuid() && (n = CURSECS - st.st_mtime) < ucheck * 60)
            error(1, "%s: another user was here %s ago", state.makefile, fmtelapsed(n, 1));

      /*
       * check the header
       */

      if (!(s = sfreserve(sp, MAGICSIZE, 0)))
            goto badmagic;
      errno = 0;
      if (memcmp(s, MAGIC, MAGICSIZE) || !(s = getstring(sp)) || streq(s, OLD_VERSION))
      {
            old = 1;
            if (sfseek(sp, (Sfoff_t)0, SEEK_SET) ||
                sfread(sp, (char*)&old_header, sizeof(old_header)) != sizeof(old_header) ||
                sfseek(sp, -(Sfoff_t)sizeof(old_trailer), SEEK_END) == -1 ||
                sfread(sp, (char*)&old_trailer, sizeof(old_trailer)) != sizeof(old_trailer))
                  goto badio;
            if ((old_magic = OLD_MAGIC, (old_swap = swapop(&old_header.magic, &old_magic, sizeof(old_magic)))) < 0 && (old_magic = OLD_OLD_MAGIC, (old_swap = swapop(&old_header.magic, &old_magic, sizeof(old_magic)))) < 0)
                  goto badmagic;
            sequence = old_header.sequence;
            old_header.sequence = old_stamp.sequence;
            if (old_swap)
            {
                  swapmem(old_swap, &old_header, &old_header, sizeof(old_header));
                  swapmem(old_swap, &old_trailer, &old_trailer, sizeof(old_trailer));
            }
            if ((st.st_size - sizeof(old_trailer)) & (OLD_ALIGN - 1))
                  goto badversion;
            strcpy(old_stamp.version, OLD_VERSION);
            if (memcmp(((char*)&old_header) + sizeof(old_header.magic), ((char*)&old_stamp) + sizeof(old_stamp.magic), sizeof(old_header) - sizeof(old_header.magic)))
            {
                  strcpy(old_stamp.version, OLD_VERSION_2);
                  if (memcmp(((char*)&old_header) + sizeof(old_header.magic), ((char*)&old_stamp) + sizeof(old_stamp.magic), sizeof(old_header) - sizeof(old_header.magic)))
                        goto badversion;
                  sequence = 2;
            }
            if (s = strrchr(old_stamp.version, ' '))
                  s++;
            else
                  s = old_stamp.version;
            strncopy(ident, s, sizeof(ident));
            if (old_trailer.magic != old_header.magic || old_trailer.size != st.st_size)
                  goto badmagic;
            if (state.exec && streq(objfile, state.objectfile))
                  return 0;
            lists = old_trailer.lists;
            rules = old_trailer.rules;
            variables = old_trailer.variables;
            off = sizeof(old_header) + rules * sizeof(old_rule) + lists * sizeof(old_list) + variables * sizeof(old_var);
            strings = st.st_size - sizeof(old_trailer) - off;
            if (sfeof(sp) || sfseek(sp, off, SEEK_SET) != off)
                  goto badio;
            lowres = state.stateview >= 0;
      }
      else
      {
            old = 0;
            if (s = strrchr(s, ' '))
                  s++;
            else
                  s = "old";
            strncopy(ident, s, sizeof(ident));
            off = sfgetu(sp);
            sequence = sfgetu(sp);
            flags = sfgetu(sp);
            NoP(flags);
            strings = sfgetu(sp);
            lists = sfgetu(sp);
            rules = sfgetu(sp);
            lowres = (rulenum = sfgetu(sp)) < 2 && state.stateview >= 0;
            rulestr = sfgetu(sp);
            variables = sfgetu(sp);
            varnum = sfgetu(sp);
            varstr = sfgetu(sp);
            if (!(s = sfreserve(sp, MAGICSIZE, 0)) || memcmp(s, MAGIC, MAGICSIZE))
                  goto badmagic;

            /*
             * read the optional headers
             */

            for (;;)
            {
                  if (sfeof(sp) || sfseek(sp, off, SEEK_SET) != off)
                        goto badio;
                  if (!sequence || !(off = sfgetu(sp)))
                        break;
                  off += sfseek(sp, (Sfoff_t)0, SEEK_CUR);
            }
      }
      message((-3, "%s sequence=%d lists=%d rules=%d variables=%d strings=%d", ident, sequence, lists, rules, variables, strings));
      if (lowres && !object.lowres)
      {
            object.lowres = 1;
            if (!state.silent)
                  error(1, "%s: low time resolution state file -- subsecond differences ignored", objfile);
      }

      /*
       * allocate strings and structs in one chunk
       * and compute pointers to the compiled data
       */

      if (!(p = newof(0, char, lists * sizeof(List_t) + rules * sizeof(Rule_t) + variables * sizeof(Var_t) + strings, 0)))
      {
            error(3, "out of space");
            goto bad;
      }
      r = or = (Rule_t*)p;
      d = (List_t*)((char*)r + rules * sizeof(Rule_t));
      v = ov = (Var_t*)((char*)d + lists * sizeof(List_t));
      s = ((char*)v + variables * sizeof(Var_t));

      /*
       * read the string table
       */

      if (!old)
            ls.sp = s;
      else if (sfread(sp, s, strings) != strings)
            goto badio;

      /*
       * load the variables and check for any frozen
       * variables that may have changed
       */

      if (old)
      {
            off = sizeof(old_header) + rules * sizeof(old_rule) + lists * sizeof(old_list);
            if (sfseek(sp, off, SEEK_SET) != off)
                  goto badio;
      }
      oattribute = internal.attribute->attribute;
      oscan = internal.scan->scan;
      attrclash = scanclash = 0;
      for (xv = v + variables; v < xv; v++)
      {
            if (old)
            {
                  if (sfread(sp, (char*)&old_var, sizeof(old_var)) != sizeof(old_var))
                        goto badio;
                  if (old_swap)
                        swapmem(old_swap, &old_var, &old_var, sizeof(old_var));
                  v->property = old_var.property;
                  if (old_var.name)
                        v->name = s + (unsigned long)old_var.name - 1;
                  if (old_var.value)
                        v->value = s + (unsigned long)old_var.value - 1;
                  switch (sequence)
                  {
                  case 2: /* 01/24/89 */
                        v->property = (v->property & 0x000003ffL);
                        break;
                  }
            }
            else
            {
                  v->property = sfgetu(sp);
#if !_HUH_1993_10_01 /* drop this eventually */
                  v->property &= ~V_free;
#endif

                  /*
                   * variable number field additions here
                   */

                  for (n = varnum; n > 0; n--)
                        sfgetu(sp);
                  v->name = loadstring(&ls, sp);
                  v->value = loadstring(&ls, sp);

                  /*
                   * variable string field additions here
                   */

                  for (n = varstr; n > 0; n--)
                        loadstring(&ls, sp);
            }
            if ((state.exec || !state.base || state.compileonly) && (v->property & V_frozen) && (!(x = getvar(v->name)) && ((v->property & V_oldvalue) || (v->property & V_import) && *v->value) || x && ((x->property & (V_append|V_readonly)) == (V_append|V_readonly) || ((v->property|x->property) & (V_import|V_readonly)) && !streq(v->value, x->value)) || (v->property & V_functional)))
            {
                  error((state.exec || state.mam.out) && !state.explain ? -1 : 1, "%s: frozen %svariable %s changed", objfile, ((v->property|(x ? x->property : 0)) & V_import) ? "environment " : ((x ? x->property : 0) & V_readonly) ? "command argument " : null, v->name);
                  recompile = 1;
                  v->property &= ~V_readonly;
            }
            else
                  v->property &= ~(V_oldvalue|V_readonly);
      }
      if (sfeof(sp))
            goto badio;
      if (recompile)
      {
            recompile = 0;
            goto bad;
      }
      recompile = -1;
      p = 0;
      v = ov;

      /*
       * enter the variables
       */

      hashclear(table.var, HASH_ALLOCATE);
      for (xv = v + variables; v < xv; v++)
      {
            if (!(x = getvar(v->name)) || !(x->property & (V_readonly|V_restored)) && (!(x->property & V_import) || !state.global))
            {
                  putvar(v->name, v);
                  if (x)
                        freevar(x);
            }
            else
            {
                  if ((x->property & (V_append|V_readonly)) == (V_append|V_readonly))
                  {
                        n = state.reading;
                        state.reading = 1;
                        setvar(x->name, v->value, 0);
                        state.reading = n;
                  }
                  x->property |= v->property & (V_functional|V_scan);
            }
            if ((v->property & V_retain) && state.stateview >= 0)
                  v->property |= V_restored;
      }
      hashset(table.var, HASH_ALLOCATE);

#if BINDINDEX
      /*
       * initialize the bind index maps
       */

      for (n = 0; n <= state.maxsource; n++)
            state.source[i].map = 0;
      for (n = 0; n <= state.maxview; n++)
            state.view[i].map = 0;
      state.view[0].map = state.stateview;
#endif

      /*
       * load and enter the rules
       */

      if (old)
      {
            off = sizeof(old_header);
            if (sfseek(sp, off, SEEK_SET) != off)
                  goto badio;
      }
      garbage = 0;
      hashclear(table.rule, HASH_ALLOCATE);
      for (xr = r + rules; r < xr; r++)
      {
            register Rule_t*  o;

            if (old)
            {
                  if (sfread(sp, (char*)&old_rule, sizeof(old_rule)) != sizeof(old_rule))
                        goto badio;
                  if (old_swap)
                        swapmem(old_swap, &old_rule, &old_rule, (char*)&old_rule.noswap - (char*)&old_rule);
                  r->property = old_rule.property;
                  r->dynamic = old_rule.dynamic;
                  r->attribute = old_rule.attribute;
                  switch (sequence)
                  {
                  case 2: /* 01/24/89 */
                        r->property =
                              ((r->property & 0x0000ffffL)) |
                              ((r->property & 0x3ffb0000L) << 1);
                        r->dynamic =
                              ((r->dynamic & 0x000003ffL)) |
                              ((r->dynamic & 0x00000800L) << 1) |
                              ((r->dynamic & 0x00034000L) << 2) |
                              ((r->dynamic & 0x00040000L) << 4);
                        break;
                  }
                  if (old_rule.name)
                        r->name = s + (unsigned long)old_rule.name - 1;
                  if (r->property & P_staterule)
                  {
                        r->dynamic |= D_lowres;
                        r->event = old_rule.old_event;
                  }
                  else if (old_rule.old_data)
                        r->statedata = s + (unsigned long)old_rule.old_data - 1;
                  if (old_rule.prereqs)
                        r->prereqs = d + (unsigned long)old_rule.prereqs / sizeof(old_list) - 1;
                  if (old_rule.action)
                        r->action = s + (unsigned long)old_rule.action - 1;
                  r->scan = old_rule.scan;
                  r->semaphore = old_rule.semaphore;
                  r->view = old_rule.view;
                  r->time = tmxsns(old_rule.time, 0);
                  switch (sequence)
                  {
                  case 0: /* 1989-09-11 */
                        if ((r->property & P_attribute) && (r->attribute && !(r->property & P_use) && !streq(r->name, internal.attribute->name) || r->scan && !streq(r->name, internal.scan->name)))
                              r->dynamic |= D_index;
                        /*FALLTHROUGH*/
                  case 1: /* 1991-07-17 */
                        if (r->property & P_staterule)
                        {
                              if (isaltstate(r->name))
                              {
                                    r->property |= P_implicit;
                                    r->time = 0;
                              }
                              r->event = r->time;
                        }
                        /*FALLTHROUGH*/
                  }
            }
            else
            {
                  r->property = sfgetu(sp);
                  r->dynamic = sfgetu(sp);
                  r->attribute = sfgetu(sp);
                  if (n = sfgetu(sp))
                  {
                        r->semaphore = (n>>16) & ((1<<8)-1);
                        r->view = (n>>8) & ((1<<8)-1);
                        r->scan = (n) & ((1<<8)-1);
                  }
                  if (n = sfgetu(sp))
                        r->prereqs = d + n - 1;
                  ts = sfgetu(sp);

                  /*
                   * 2004-12-01
                   */

                  if (n = rulenum)
                  {
                        n--;
                        tn = sfgetu(sp);
                  }
                  else
                        tn = 0;
                  r->time = tmxsns(ts, tn);

                  /*
                   * 2004-12-01
                   */

                  if ((r->property & P_staterule) && n)
                  {
                        n--;
                        tn = sfgetu(sp);
                  }
                  else
                        tn = 0;

                  /*
                   * rule number field additions here
                   */

                  while (n--)
                        sfgetu(sp);
                  r->name = loadstring(&ls, sp);
                  r->action = loadstring(&ls, sp);
                  if (r->property & P_staterule)
                  {
                        ts = sfgetu(sp);
                        r->event = tmxsns(ts, tn);
                        if (lowres)
                              r->dynamic |= D_lowres;
                  }
                  else
                        r->statedata = loadstring(&ls, sp);

                  /*
                   * rule string field additions here
                   */

                  for (n = rulestr; n > 0; n--)
                        loadstring(&ls, sp);
            }
            if (sfeof(sp))
                  goto badio;
            r->preview = state.maxview + 1;
            o = getrule(r->name);
            if (r->dynamic & D_index)
            {
                  /*
                   * check for index atom consistency
                   * remap inconsistent state file atoms
                   */

                  if (r->scan)
                  {
                        attr = ~0;
                        if (o && (o->property & P_attribute) && o->scan)
                        {
                              if (r->scan != o->scan)
                              {
                                    error((state.exec || state.mam.out) && !state.explain ? -1 : 1, "%s: %s %s definition changed", objfile, r->name, internal.scan->name);
                                    if (state.stateview < 0)
                                          return -1;
                                    attr = o->scan;
                              }
                        }
                        else
                        {
                              for (a = internal.scan->prereqs; a; a = a->next)
                                    if (r->scan == a->rule->scan)
                                    {
                                          error((state.exec || state.mam.out) && !state.explain ? -1 : 1, "%s: %s %s definition clashes with %s", objfile, r->name, internal.scan->name, a->rule->name);
                                          if (state.stateview < 0)
                                                return -1;
                                          attr = 0;
                                          break;
                                    }
                              if (state.stateview >= 0)
                                    attr = 0;
                        }
                        if (attr != ~0)
                        {
                              if (!scanclash)
                              {
                                    scanclash = 1;
                                    for (n = 0; n < elementsof(scanmap); n++)
                                          scanmap[n] = n;
                              }
                              if (!(r->scan = scanmap[r->scan] = attr))
                                    r->property &= ~P_attribute;
                              if (o)
                                    continue;
                        }
                  }
                  else if (r->attribute)
                  {
                        attr = ~0;
                        if (o && (o->property & P_attribute) && o->attribute)
                        {
                              if (r->attribute != o->attribute)
                              {
                                    error((state.exec || state.mam.out) && !state.explain ? -1 : 1, "%s: %s %s definition changed", objfile, r->name, internal.attribute->name);
                                    if (state.stateview < 0)
                                          return -1;
                                    attr = o->attribute;
                              }
                        }
                        else
                              for (a = internal.attribute->prereqs; a; a = a->next)
                                    if (r->attribute == a->rule->attribute)
                                    {
                                          error((state.exec || state.mam.out) && !state.explain ? -1 : 1, "%s: %s %s definition clashes with %s", objfile, r->name, internal.attribute->name, a->rule->name);
                                          if (state.stateview < 0)
                                                return -1;
                                          attr = 0;
                                    }
                        if (attr != ~0)
                        {
                              if (!attrclash)
                              {
                                    attrclash = 1;
                                    attrclear = 0;
                                    for (n = 0; n < CHAR_BIT * sizeof(unsigned long); n++)
                                          attrmap[n] = (1<<n);
                              }
                              attrclear |= r->attribute;
                              for (n = 0; n < CHAR_BIT * sizeof(unsigned long); n++)
                                    if (r->attribute == (1<<n))
                                    {
                                          attrmap[n] = attr;
                                          break;
                                    }
                              continue;
                        }
                  }
            }
            else
            {
                  if (attrclash)
                  {
                        attr = r->attribute & ~attrclear;
                        for (n = 0; n < CHAR_BIT * sizeof(unsigned long); n++)
                              if (r->attribute & (1<<n))
                                    attr |= attrmap[n];
                        r->attribute = attr;
                  }
                  if (scanclash)
                        r->scan = scanmap[r->scan];
            }
            if (r->dynamic & D_compiled)
            {
                  n = state.stateview > 0 && viewable(r);
                  if (n && (o || (state.questionable & 0x00000020)))
                  {
                        r->dynamic |= D_compiled|D_lower;
                        viewname(r->name, state.stateview);
                        r->view = state.stateview;
                        maprule(r->name, r);
                        if (o && (r->property & P_statevar) && !o->time)
                              o->time = r->time;
                  }
                  else
                  {
                        if (n)
                        {
                              r->mark |= M_compile;
                              promoted = 1;
                        }
                        if (!o)
                        {
                              if (state.stateview >= 0 && (r->property & (P_joint|P_target)) == P_target)
                              {
                                    r->dynamic |= D_garbage;
                                    garbage++;
                              }
                        }
                        else if (state.stateview >= 0 && (r->property & P_statevar) && (r->prereqs || r->action))
                        {
                              /*
                               * ignore state file prereqs and action
                               */

                              if (o->action && r->action && !streq(o->action, r->action))
                                    r->time = CURTIME;
                              o->time = r->time;
                              o->statedata = r->statedata;
                              setoldrule(r, o);
                              continue;
                        }
                        else if (state.stateview >= 0 && (r->property & P_staterule) && o->event > r->event)
                        {
                              /*
                               * o was updated after r was saved 
                               */

                              setoldrule(r, o);
                              continue;
                        }
                        else if (!isoldrule(o))
                        {
                              if (o->dynamic & (D_bound|D_scanned))
                              {
                                    r->statedata = o->statedata;
                                    r->time = o->time;
                                    r->status = o->status;
                                    r->view = o->view;
                                    r->property &= ~(P_parameter|P_state|P_staterule|P_statevar);
                                    r->property |= o->property & (P_parameter|P_state|P_staterule|P_statevar);
                                    r->dynamic &= ~(D_bound|D_entries|D_global|D_regular|D_scanned);
                                    r->dynamic |= o->dynamic & (D_bound|D_entries|D_global|D_regular|D_scanned);
                                    if (!(o->property & P_state) && o->uname)
                                    {
                                          r->uname = maprule(o->uname, r);
                                          getrule(o->name);
                                    }
                              }
                              else if ((r->property & (P_parameter|P_statevar)) == P_statevar && (o->property & (P_parameter|P_statevar)) == (P_parameter|P_statevar))
                                    r->property |= P_parameter;
                              setoldrule(o, r);
                        }
                        r->name = putrule(0, r);
                  }
            }
            else if (o)
            {
                  /*
                   * r is just a reference -- keep the old rule
                   * but retain some attributes
                   */

                  o->attribute |= r->attribute & internal.retain->attribute;
                  o->property |= r->property & (P_dontcare|P_ignore|P_terminal);
                  setoldrule(r, o);
            }
            else
            {
                  r->name = putrule(0, r);
                  if (!(r->property & P_state))
                        r->time = 0;
                  if (state.stateview >= 0)
                  {
                        if ((r->property & (P_joint|P_target)) == P_target)
                        {
                              r->dynamic |= D_garbage;
                              garbage++;
                        }

                        /*
                         * clear reference secondary attributes
                         */

                        r->attribute &= internal.retain->attribute;
                        r->property &= (P_attribute|P_dontcare|P_ignore|P_parameter|P_state|P_staterule|P_statevar|P_terminal);
                        r->dynamic &= D_garbage;
                  }
            }
#if BINDINDEX
            if (r->dynamic & D_bindindex)
            {
                  if (o)
                  {
                        if (o->dynamic & D_bindindex)
                        {
                              if (r->source)
                              {
                                    state.source[r->source].map = o->source;
                                    if (isoldrule(o))
                                          state.source[o->source].path = r;
                              }
                              else if (r->view)
                              {
                                    state.view[r->view].map = o->view;
                                    if (isoldrule(o))
                                          state.view[o->view].path = r;
                              }
                        }
                        else if (r->source)
                        {
                              if (++state.maxsource >= elementsof(state.source))
                                    error(3, "%s: too many %s directories -- %d max", r->name, internal.source->name, elementsof(state.source));
                              state.source[r->source].map = state.maxsource;
                              if (isoldrule(o))
                                    state.source[state.maxsource].path = r;
                              else
                              {
                                    o->dynamic |= D_bindindex;
                                    o->source = state.maxsource;
                                    state.source[o->source].path = o;
                              }
                        }
                  }
                  else if (r->source)
                  {
                        if (++state.maxsource >= elementsof(state.source))
                              error(3, "%s: too many %s directories -- %d max", r->name, internal.source->name, elementsof(state.source));
                        state.source[r->source].map = state.maxsource;
                        state.source[state.maxsource].path = r;
                  }
            }
            r->source = state.source[r->source].map;
            r->view = state.view[r->view].map;
#endif
      }
      hashset(table.rule, HASH_ALLOCATE);
      r = or;

      /*
       * load the prerequisite lists
       */

      if (old)
      {
            off = sizeof(old_header) + rules * sizeof(old_rule);
            if (sfseek(sp, off, SEEK_SET) != off)
                  goto badio;
      }
      if (lists)
      {
            for (xd = d + lists; d < xd;)
            {
                  if (old)
                  {
                        if (sfread(sp, (char*)&old_list, sizeof(old_list)) != sizeof(old_list))
                              goto badio;
                        if (old_swap)
                              swapmem(old_swap, &old_list, &old_list, sizeof(old_list));
                        if (old_list.rule)
                              d->rule = r + (unsigned long)old_list.rule / sizeof(old_rule) - 1;
                        if (old_list.next)
                              d->next = d + 1;
                        d++;
                  }
                  else if (!(n = sfgetu(sp)))
                        (d - 1)->next = 0;
                  else if (n < 0)
                  {
                        if (strcmp(ident, corrupt) < 0 && (((lists - (xd - d)) * 100) / lists) >= 90)
                        {
                              while (d < xd)
                              {
                                    (d - 1)->next = 0;
                                    d++;
                              }
                              hashwalk(table.rule, 0, repair, NiL);
                              error(1, "%s: pre-%s make object corruption repaired", objfile, corrupt);
                              corrupt = 0;
                        }
                        break;
                  }
                  else
                  {
                        d->rule = r + n - 1;
                        d->next = d + 1;
                        d++;
                  }
            }
            if (!old)
            {
                  sfgetu(sp);
                  (d - 1)->next = 0;
            }
      }
      if (sfeof(sp) && corrupt)
            goto badio;

      /*
       * collect state file garbage collection stats
       */

      if (state.stateview >= 0)
      {
            Time_t      t;
            Time_t      q;

            object.garbage += garbage;
            object.rules += rules;

            /*
             * handle low time resolution by making
             * sure the current time is at least as
             * recent as the state file time, previously
             * set in compile() to take into account
             * the file system time precision
             */

            t = tmxgetmtime(&st);
            if (state.tolerance)
                  t += tmxsns(state.tolerance, 0);
            q = CURTIME;
            if (q >= t)
                  t = 0;
            else if ((t -= q) > tmxsns(5,0))
                  t = tmxsns(5,0);
            if (t)
            {
                  error(state.tolerance ? 1 : -1, "%s: state time sync delay %lu.%09lu", objfile, tmxsec(t), tmxnsec(t));
                  tmxsleep(t);
            }
      }

      /*
       * readjust the internal rule pointers
       */

      initrule();

      /*
       * make sure top view state has top view prereqs
       */

      if (promoted)
            hashwalk(table.rule, 0, promote, NiL);

      /*
       * associate one rule with each name
       */
      
      if (oldrules)
      {
            hashwalk(table.rule, 0, atomize, NiL);
            fp = state.frame;
            for (;;)
            {
                  if (isoldrule(fp->target))
                  {
                        fp->target = getoldrule(fp->target);
                        fp->target->active = fp;
                  }
                  if (fp == fp->parent)
                        break;
                  fp = fp->parent;
            }
            do
            {
                  xr = (Rule_t*)oldrules->prereqs;
                  freerule(oldrules);
            } while (oldrules = xr);
      }

      /*
       * check special indices
       */

      if (internal.attribute->attribute == 1)
            internal.attribute->attribute = oattribute;
      if (internal.scan->scan == SCAN_USER)
            internal.scan->scan = oscan;

      /*
       * reset compiled options
       */

      if (!state.list)
      {
            state.loading = (char*)objfile;
            if (old)
            {
                  if (old_trailer.options)
                        set(s + (unsigned long)old_trailer.options - 1, 1, NiL);
            }
            else if ((s = getstring(sp)) && *s && (!streq(s, "--") || (s = getstring(sp)) && *s))
                  set(s, 1, NiL);
            if (state.stateview < 0)
            {
                  /*
                   * check for load time actions
                   *
                   * state.global++ enables setvar() V_import override
                   */

                  if (state.global)
                        state.global++;
                  for (xr = r + rules; r < xr; r++)
                        if ((r->property & (P_immediate|P_target)) == (P_immediate|P_target))
                              immediate(r);
                  if (state.global)
                        state.global--;
            }
            state.loading = 0;
      }
      return 1;
 badversion:
      if (strncmp(old_header.version, old_stamp.version, sizeof(old_header.version)))
      {
            /*
             * old old versions were only numbers
             */

            if (!isalpha(*old_header.version))
                  sfsprintf(old_header.version, sizeof(old_header.version), "%d", *((long*)old_header.version));
            error(1, "%s: old format (%s) incompatible with make loader (%s)", objfile, old_header.version, old_stamp.version);
      }
      else
            error(1, "%s: old format (%s) generated on an incompatible architecture", objfile, old_header.version);
      return 0;
 badio:
      error(ERROR_SYSTEM|2, "%s: object file io error", objfile);
      goto bad;
 badmagic:
      error(1, "%s: not a %s object file", objfile, version);
 bad:
      if (p)
            free(p);
      return recompile;
}

Generated by  Doxygen 1.6.0   Back to index