# define INCLUDE_FILE_IO # define INCLUDE_CTYPE # include "dgd.h" # include "str.h" # include "array.h" # include "object.h" # include "interpret.h" # include "data.h" typedef struct _objplane_ objplane; typedef struct _objpatch_ { objplane *plane; /* plane that patch is on */ struct _objpatch_ *prev; /* previous patch */ struct _objpatch_ *next; /* next in linked list */ object obj; /* new object value */ } objpatch; # define OPCHUNKSZ 32 typedef struct _opchunk_ { struct _opchunk_ *next; /* next in linked list */ objpatch op[OPCHUNKSZ]; /* object patches */ } opchunk; typedef struct { opchunk *chunk; /* object patch chunk */ unsigned short chunksz; /* size of object patch chunk */ objpatch *flist; /* free list of object patches */ objpatch *op[OBJPATCHHTABSZ]; /* hash table of object patches */ } optable; struct _objplane_ { hashtab *htab; /* object name hash table */ optable *optab; /* object patch table */ unsigned long clean; /* list of objects to clean */ unsigned long upgrade; /* list of upgrade objects */ uindex destruct; /* destructed object list */ uindex free; /* free object list */ uindex nobjects; /* number of objects in object table */ uindex nfreeobjs; /* number of objects in free list */ Uint ocount; /* object creation count */ bool swap, dump, stop; /* state vars */ struct _objplane_ *prev; /* previous object plane */ }; object *otable; /* object table */ char *ocmap; /* object change map */ bool obase; /* object base plane flag */ bool swap, dump, stop; /* global state vars */ static uindex otabsize; /* size of object table */ static objplane baseplane; /* base object plane */ static objplane *oplane; /* current object plane */ static object *upgraded; /* list of upgraded objects */ Uint odcount; /* objects destructed count */ /* * NAME: object->init() * DESCRIPTION: initialize the object tables */ void o_init(n) register unsigned int n; { otable = ALLOC(object, otabsize = n); memset(otable, '\0', n * sizeof(object)); ocmap = ALLOC(char, (n + 7) >> 3); memset(ocmap, '\0', (n + 7) >> 3); for (n = 4; n < otabsize; n <<= 1) ; baseplane.htab = ht_new(n >> 2, OBJHASHSZ, FALSE); baseplane.optab = (optable *) NULL; baseplane.upgrade = baseplane.clean = OBJ_NONE; baseplane.destruct = baseplane.free = OBJ_NONE; baseplane.nobjects = 0; baseplane.nfreeobjs = 0; baseplane.ocount = 2; baseplane.swap = baseplane.dump = baseplane.stop = FALSE; oplane = &baseplane; upgraded = (object *) NULL; odcount = 1; obase = TRUE; } /* * NAME: objpatch->init() * DESCRIPTION: initialize objpatch table */ static void op_init(plane) objplane *plane; { memset(plane->optab = ALLOC(optable, 1), '\0', sizeof(optable)); } /* * NAME: objpatch->clean() * DESCRIPTION: free objpatch table */ static void op_clean(plane) objplane *plane; { register opchunk *c, *f; c = plane->optab->chunk; while (c != (opchunk *) NULL) { f = c; c = c->next; FREE(f); } FREE(plane->optab); plane->optab = (optable *) NULL; } /* * NAME: objpatch->new() * DESCRIPTION: create a new object patch */ static objpatch *op_new(plane, o, prev, obj) objplane *plane; objpatch **o, *prev; object *obj; { register optable *tab; register objpatch *op; /* allocate */ tab = plane->optab; if (tab->flist != (objpatch *) NULL) { /* from free list */ op = tab->flist; tab->flist = op->next; } else { /* newly allocated */ if (tab->chunk == (opchunk *) NULL || tab->chunksz == OPCHUNKSZ) { register opchunk *cc; /* create new chunk */ cc = ALLOC(opchunk, 1); cc->next = tab->chunk; tab->chunk = cc; tab->chunksz = 0; } op = &tab->chunk->op[tab->chunksz++]; } /* initialize */ op->plane = plane; op->prev = prev; op->obj = *obj; /* add to hash table */ op->next = *o; return *o = op; } /* * NAME: objpatch->del() * DESCRIPTION: delete an object patch */ static void op_del(plane, o) objplane *plane; register objpatch **o; { register objpatch *op; optable *tab; /* remove from hash table */ op = *o; *o = op->next; /* add to free list */ tab = plane->optab; op->next = tab->flist; tab->flist = op; } /* * NAME: object->oaccess() * DESCRIPTION: access to object from atomic code */ static object *o_oaccess(index, access) register unsigned int index; int access; { register objpatch *o, **oo; register object *obj; if (BTST(ocmap, index)) { /* * object already patched */ oo = &oplane->optab->op[index % OBJPATCHHTABSZ]; for (o = *oo; o->obj.index != index; o = o->next) ; if (access == OACC_READ || o->plane == oplane) { return &o->obj; } /* create new patch on current plane */ return &op_new(oplane, oo, o, &o->obj)->obj; } else { /* * first patch for object */ BSET(ocmap, index); if (oplane->optab == (optable *) NULL) { op_init(oplane); } oo = &oplane->optab->op[index % OBJPATCHHTABSZ]; obj = &op_new(oplane, oo, (objpatch *) NULL, OBJ(index))->obj; if (obj->chain.name != (char *) NULL) { char *name; hte **h; /* copy object name to higher plane */ strcpy(name = ALLOC(char, strlen(obj->chain.name) + 1), obj->chain.name); obj->chain.name = name; if (obj->count != 0) { if (oplane->htab == (hashtab *) NULL) { oplane->htab = ht_new(OBJPATCHHTABSZ, OBJHASHSZ, FALSE); } h = ht_lookup(oplane->htab, name, FALSE); obj->chain.next = *h; *h = (hte *) obj; } } return obj; } } /* * NAME: object->oread() * DESCRIPTION: read access to object in patch table */ object *o_oread(index) unsigned int index; { return o_oaccess(index, OACC_READ); } /* * NAME: object->owrite() * DESCRIPTION: write access to object in atomic code */ object *o_owrite(index) unsigned int index; { return o_oaccess(index, OACC_MODIFY); } /* * NAME: object->space() * DESCRIPTION: check if there's space for another object */ bool o_space() { return (oplane->free != OBJ_NONE) ? TRUE : (oplane->nobjects != otabsize); } /* * NAME: object->alloc() * DESCRIPTION: allocate a new object */ static object *o_alloc() { register uindex n; register object *obj; if (oplane->free != OBJ_NONE) { /* get space from free object list */ n = oplane->free; obj = OBJW(n); oplane->free = obj->prev; --oplane->nfreeobjs; } else { /* use new space in object table */ if (oplane->nobjects == otabsize) { error("Too many objects"); } n = oplane->nobjects++; obj = OBJW(n); } OBJ(n)->index = OBJ_NONE; obj->index = n; obj->ctrl = (control *) NULL; obj->data = (dataspace *) NULL; obj->cfirst = SW_UNUSED; obj->dfirst = SW_UNUSED; return obj; } /* * NAME: object->new_plane() * DESCRIPTION: create a new object plane */ void o_new_plane() { register objplane *p; p = ALLOC(objplane, 1); if (oplane->optab == (optable *) NULL) { p->htab = (hashtab *) NULL; p->optab = (optable *) NULL; } else { p->htab = oplane->htab; p->optab = oplane->optab; } p->clean = oplane->clean; p->upgrade = oplane->upgrade; p->destruct = oplane->destruct; p->free = oplane->free; p->nobjects = oplane->nobjects; p->nfreeobjs = oplane->nfreeobjs; p->ocount = oplane->ocount; p->swap = oplane->swap; p->dump = oplane->dump; p->stop = oplane->stop; p->prev = oplane; oplane = p; obase = FALSE; } /* * NAME: object->commit_plane() * DESCRIPTION: commit the current object plane */ void o_commit_plane() { register objplane *prev; register objpatch **t, **o, *op; register int i; register object *obj; prev = oplane->prev; if (oplane->optab != (optable *) NULL) { for (i = OBJPATCHHTABSZ, t = oplane->optab->op; --i >= 0; t++) { o = t; while (*o != (objpatch *) NULL && (*o)->plane == oplane) { op = *o; if (op->prev != (objpatch *) NULL) { obj = &op->prev->obj; } else { obj = OBJ(op->obj.index); } if (op->obj.count == 0 && obj->count != 0) { /* remove object from stackframe above atomic function */ i_odest(cframe, obj); } if (prev == &baseplane) { /* * commit to base plane */ if (op->obj.chain.name != (char *) NULL) { hte **h; if (obj->chain.name == (char *) NULL) { char *name; /* * make object name static */ m_static(); name = ALLOC(char, strlen(op->obj.chain.name) + 1); m_dynamic(); strcpy(name, op->obj.chain.name); FREE(op->obj.chain.name); op->obj.chain.name = name; if (op->obj.count != 0) { /* put name in static hash table */ h = ht_lookup(prev->htab, name, FALSE); op->obj.chain.next = *h; *h = (hte *) obj; } } else { /* * same name */ FREE(op->obj.chain.name); op->obj.chain.name = obj->chain.name; if (op->obj.count != 0) { /* keep this name */ op->obj.chain.next = obj->chain.next; } else if (obj->count != 0) { /* remove from hash table */ h = ht_lookup(prev->htab, obj->chain.name, FALSE); if (*h != (hte *) obj) { /* new object was compiled also */ h = &(*h)->next; } *h = obj->chain.next; } } } if (obj->count != 0) { op->obj.update = obj->update; } BCLR(ocmap, op->obj.index); *obj = op->obj; op_del(oplane, o); } else { /* * commit to previous plane */ if (op->prev == (objpatch *) NULL || op->prev->plane != prev) { /* move to previous plane */ op->plane = prev; o = &op->next; } else { /* * copy onto previous plane */ if (op->obj.chain.name != (char *) NULL && op->obj.count != 0) { /* move name to previous plane */ *ht_lookup(oplane->htab, op->obj.chain.name, FALSE) = (hte *) obj; } *obj = op->obj; op_del(oplane, o); } } } } if (prev != &baseplane) { prev->htab = oplane->htab; prev->optab = oplane->optab; } else { if (oplane->htab != (hashtab *) NULL) { ht_del(oplane->htab); } op_clean(oplane); } } prev->clean = oplane->clean; prev->upgrade = oplane->upgrade; prev->destruct = oplane->destruct; prev->free = oplane->free; prev->nobjects = oplane->nobjects; prev->nfreeobjs = oplane->nfreeobjs; prev->ocount = oplane->ocount; prev->swap = oplane->swap; prev->dump = oplane->dump; prev->stop = oplane->stop; FREE(oplane); oplane = prev; obase = (prev == &baseplane); } /* * NAME: object->discard_plane() * DESCRIPTION: discard the current object plane without committing it */ void o_discard_plane() { register objpatch **o, *op; register int i; register object *obj, *clist; register objplane *p; if (oplane->optab != (optable *) NULL) { clist = (object *) NULL; for (i = OBJPATCHHTABSZ, o = oplane->optab->op; --i >= 0; o++) { while (*o != (objpatch *) NULL && (*o)->plane == oplane) { op = *o; if (op->prev != (objpatch *) NULL) { obj = &op->prev->obj; } else { BCLR(ocmap, op->obj.index); obj = OBJ(op->obj.index); } if (op->obj.chain.name != (char *) NULL) { if (obj->chain.name == (char *) NULL || op->prev == (objpatch *) NULL) { /* * remove new name */ if (op->obj.count != 0) { /* remove from hash table */ *ht_lookup(oplane->htab, op->obj.chain.name, FALSE) = op->obj.chain.next; } FREE(op->obj.chain.name); } else { hte **h; if (op->obj.count != 0) { /* * move name to previous plane */ h = ht_lookup(oplane->htab, obj->chain.name, FALSE); obj->chain.next = op->obj.chain.next; *h = (hte *) obj; } else if (obj->count != 0) { /* * put name back in hashtable */ h = ht_lookup(oplane->htab, obj->chain.name, FALSE); obj->chain.next = *h; *h = (hte *) obj; } } } if (obj->index == OBJ_NONE) { /* * discard newly created object */ if ((op->obj.flags & O_MASTER) && op->obj.ctrl != (control *) NULL) { op->obj.chain.next = (hte *) clist; clist = &op->obj; } if (op->obj.data != (dataspace *) NULL) { /* discard new data block */ d_del_dataspace(op->obj.data); } } else { /* pass on control block and dataspace */ obj->ctrl = op->obj.ctrl; obj->data = op->obj.data; } op_del(oplane, o); } } /* discard new control blocks */ while (clist != (object *) NULL) { obj = clist; clist = (object *) obj->chain.next; d_del_control(obj->ctrl); } } p = oplane; oplane = p->prev; if (p->optab != (optable *) NULL) { if (oplane != &baseplane) { oplane->htab = p->htab; oplane->optab = p->optab; } else { if (p->htab != (hashtab *) NULL) { ht_del(p->htab); } op_clean(p); } } FREE(p); obase = (oplane == &baseplane); } /* * NAME: object->new() * DESCRIPTION: create a new object */ object *o_new(name, ctrl) char *name; register control *ctrl; { register object *o; register dinherit *inh; register int i; hte **h; /* allocate object */ o = o_alloc(); /* put object in object name hash table */ if (obase) { m_static(); } strcpy(o->chain.name = ALLOC(char, strlen(name) + 1), name); if (obase) { m_dynamic(); } else if (oplane->htab == (hashtab *) NULL) { oplane->htab = ht_new(OBJPATCHHTABSZ, OBJHASHSZ, FALSE); } h = ht_lookup(oplane->htab, name, FALSE); o->chain.next = *h; *h = (hte *) o; o->flags = O_MASTER; o->cref = 0; o->prev = OBJ_NONE; o->count = ++oplane->ocount; o->update = 0; o->ctrl = ctrl; ctrl->inherits[ctrl->ninherits - 1].oindex = ctrl->oindex = o->index; /* add reference to all inherited objects */ o->u_ref = 0; /* increased to 1 in following loop */ inh = ctrl->inherits; for (i = ctrl->ninherits, inh = ctrl->inherits; i > 0; --i, inh++) { OBJW(inh->oindex)->u_ref++; } return o; } /* * NAME: object->clone() * DESCRIPTION: clone an object */ object *o_clone(master) register object *master; { register object *o; /* allocate object */ o = o_alloc(); o->chain.name = (char *) NULL; o->flags = 0; o->count = ++oplane->ocount; o->update = master->update; o->u_master = master->index; /* add reference to master object */ master->cref++; master->u_ref++; return o; } /* * NAME: object->lwobj() * DESCRIPTION: create light-weight instance of object */ void o_lwobj(obj) object *obj; { obj->flags |= O_LWOBJ; } /* * NAME: object->delete() * DESCRIPTION: the last reference to a master object was removed */ static void o_delete(o, f) register object *o; register frame *f; { register control *ctrl; register dinherit *inh; register int i; ctrl = (O_UPGRADING(o)) ? OBJR(o->prev)->ctrl : o_control(o); /* put in deleted list */ o->cref = oplane->destruct; oplane->destruct = o->index; /* callback to the system */ PUSH_STRVAL(f, str_new(NULL, strlen(o->chain.name) + 1L)); f->sp->u.string->text[0] = '/'; strcpy(f->sp->u.string->text + 1, o->chain.name); PUSH_INTVAL(f, ctrl->compiled); PUSH_INTVAL(f, o->index); if (i_call_critical(f, "remove_program", 3, TRUE)) { i_del_value(f->sp++); } /* remove references to inherited objects too */ for (i = ctrl->ninherits, inh = ctrl->inherits; --i > 0; inh++) { o = OBJW(inh->oindex); if (--(o->u_ref) == 0) { o_delete(OBJW(inh->oindex), f); } } } /* * NAME: object->upgrade() * DESCRIPTION: upgrade an object to a new program */ void o_upgrade(obj, ctrl, f) register object *obj; control *ctrl; register frame *f; { register object *tmpl; register dinherit *inh; register int i; /* allocate upgrade object */ tmpl = o_alloc(); tmpl->chain.name = (char *) NULL; tmpl->flags = O_MASTER; tmpl->count = 0; tmpl->update = obj->update; tmpl->ctrl = ctrl; ctrl->inherits[ctrl->ninherits - 1].oindex = tmpl->dfirst = obj->index; /* add reference to inherited objects */ for (i = ctrl->ninherits, inh = ctrl->inherits; --i > 0; inh++) { OBJW(inh->oindex)->u_ref++; } /* add to upgrades list */ tmpl->chain.next = (hte *) oplane->upgrade; oplane->upgrade = tmpl->index; /* mark as upgrading */ obj->cref += 2; tmpl->prev = obj->prev; obj->prev = tmpl->index; /* remove references to old inherited objects */ ctrl = o_control(obj); for (i = ctrl->ninherits, inh = ctrl->inherits; --i > 0; inh++) { obj = OBJW(inh->oindex); if (--(obj->u_ref) == 0) { o_delete(OBJW(inh->oindex), f); } } } /* * NAME: object->upgraded() * DESCRIPTION: an object has been upgraded */ void o_upgraded(tmpl, new) register object *tmpl, *new; { # ifdef DEBUG if (new->count == 0) { fatal("upgrading destructed object"); } # endif if (!(new->flags & O_MASTER)) { new->update = OBJ(new->u_master)->update; } if (tmpl->count == 0) { tmpl->chain.next = (hte *) upgraded; upgraded = tmpl; } tmpl->count++; } /* * NAME: object->del() * DESCRIPTION: delete an object */ void o_del(obj, f) register object *obj; frame *f; { if (obj->count == 0) { /* can happen if object selfdestructs in close()-on-destruct */ error("Destructing destructed object"); } i_odest(f, obj); /* wipe out occurrances on the stack */ obj->count = 0; odcount++; if (obj->flags & O_MASTER) { /* remove from object name hash table */ *ht_lookup(oplane->htab, obj->chain.name, FALSE) = obj->chain.next; if (--(obj->u_ref) == 0) { o_delete(obj, f); } } else { object *master; master = OBJW(obj->u_master); master->cref--; if (--(master->u_ref) == 0) { o_delete(OBJW(master->index), f); } } /* put in clean list */ obj->chain.next = (hte *) oplane->clean; oplane->clean = obj->index; } /* * NAME: object->name() * DESCRIPTION: return the name of an object */ char *o_name(name, o) char *name; register object *o; { if (o->chain.name != (char *) NULL) { return o->chain.name; } else { char num[12]; register char *p; register uindex n; /* * return the name of the master object with the index appended */ n = o->index; p = num + 11; *p = '\0'; do { *--p = '0' + n % 10; n /= 10; } while (n != 0); *--p = '#'; strcpy(name, OBJR(o->u_master)->chain.name); strcat(name, p); return name; } } /* * NAME: object->find() * DESCRIPTION: find an object by name */ object *o_find(name, access) char *name; int access; { register object *o; register unsigned long number; char *hash; hash = strchr(name, '#'); if (hash != (char *) NULL) { register char *p; object *m; /* * Look for a cloned object, which cannot be found directly in the * object name hash table. * The name must be of the form filename#1234, where 1234 is the * decimal representation of the index in the object table. */ p = hash + 1; if (*p == '\0' || (p[0] == '0' && p[1] != '\0')) { /* don't accept "filename#" or "filename#01" */ return (object *) NULL; } /* convert the string to a number */ number = 0; do { if (!isdigit(*p)) { return (object *) NULL; } number = number * 10 + *p++ - '0'; if (number >= oplane->nobjects) { return (object *) NULL; } } while (*p != '\0'); o = OBJR(number); if (o->count == 0 || (o->flags & O_MASTER) || strncmp(name, (m=OBJR(o->u_master))->chain.name, hash-name) != 0 || m->chain.name[hash - name] != '\0') { /* * no entry, not a clone, or object name doesn't match */ return (object *) NULL; } } else { /* look it up in the hash table */ if (oplane->htab == (hashtab *) NULL || (o = (object *) *ht_lookup(oplane->htab, name, TRUE)) == (object *) NULL) { if (oplane != &baseplane) { o = (object *) *ht_lookup(baseplane.htab, name, FALSE); if (o != (object *) NULL) { number = o->index; o = (access == OACC_READ)? OBJR(number) : OBJW(number); if (o->count != 0) { return o; } } } return (object *) NULL; } number = o->index; } return (access == OACC_READ) ? o : OBJW(number); } /* * NAME: object->control() * DESCRIPTION: return the control block for an object */ control *o_control(obj) object *obj; { register object *o; o = obj; if (!(o->flags & O_MASTER)) { /* get control block of master object */ o = OBJR(o->u_master); } if (o->ctrl == (control *) NULL) { o->ctrl = d_load_control(o); /* reload */ } else { d_ref_control(o->ctrl); } return obj->ctrl = o->ctrl; } /* * NAME: object->dataspace() * DESCRIPTION: return the dataspace block for an object */ dataspace *o_dataspace(o) register object *o; { if (o->data == (dataspace *) NULL) { /* load dataspace block */ o->data = d_load_dataspace(o); } else { d_ref_dataspace(o->data); } return o->data; } /* * NAME: object->clean_upgrades() * DESCRIPTION: clean up upgrade templates */ static void o_clean_upgrades() { register object *o, *next; register Uint count; while ((next=upgraded) != (object *) NULL) { upgraded = (object *) next->chain.next; count = next->count; next->count = 0; do { o = next; next = OBJ(o->cref); o->ref -= count; if (o->ref == 0) { # ifdef DEBUG if (o->prev != OBJ_NONE) { fatal("removing issue in middle of list"); } # endif /* remove from template list */ if (next->prev == o->index) { next->prev = OBJ_NONE; } else { OBJ(next->prev)->prev = OBJ_NONE; } /* put in delete list */ o->cref = baseplane.destruct; baseplane.destruct = o->index; } } while (o->dfirst != next->index); } } /* * NAME: object->purge_upgrades() * DESCRIPTION: purge the LW dross from upgrade templates */ static bool o_purge_upgrades(o) register object *o; { bool purged; purged = FALSE; while (o->prev != OBJ_NONE && ((o=OBJ(o->prev))->flags & O_LWOBJ)) { o->flags &= ~O_LWOBJ; if (--o->ref == 0) { o->cref = baseplane.destruct; baseplane.destruct = o->index; purged = TRUE; } } return purged; } /* * NAME: object->clean() * DESCRIPTION: clean up the object table */ void o_clean() { register object *o; while (baseplane.clean != OBJ_NONE) { o = OBJ(baseplane.clean); baseplane.clean = (unsigned long) o->chain.next; /* free dataspace block (if it exists) */ if (o->data == (dataspace *) NULL && o->dfirst != SW_UNUSED) { /* reload dataspace block (sectors are needed) */ o->data = d_load_dataspace(o); } if (o->data != (dataspace *) NULL) { d_del_dataspace(o->data); } if (o->flags & O_MASTER) { /* remove possible upgrade templates */ if (o_purge_upgrades(o)) { o->prev = OBJ_NONE; } } else { register object *tmpl; /* check if clone still had to be upgraded */ tmpl = OBJW(o->u_master); if (tmpl->update != o->update) { /* non-upgraded clone of old issue */ do { tmpl = OBJW(tmpl->prev); } while (tmpl->update != o->update); if (tmpl->count == 0) { tmpl->chain.next = (hte *) upgraded; upgraded = tmpl; } tmpl->count++; } /* put clone in free list */ o->prev = baseplane.free; baseplane.free = o->index; baseplane.nfreeobjs++; } } o_clean_upgrades(); /* 1st time */ while (baseplane.upgrade != OBJ_NONE) { register object *up; register control *ctrl; o = OBJ(baseplane.upgrade); baseplane.upgrade = (unsigned long) o->chain.next; up = OBJ(o->dfirst); if (up->u_ref == 0) { /* no more instances of object around */ o->cref = baseplane.destruct; baseplane.destruct = o->index; } else { /* upgrade objects */ up->flags &= ~O_COMPILED; up->cref -= 2; o->u_ref = up->cref; if (up->flags & O_LWOBJ) { o->flags |= O_LWOBJ; o->u_ref++; } if (up->count != 0 && O_HASDATA(up)) { o->u_ref++; } ctrl = up->ctrl; if (ctrl->vmapsize != 0 && o->u_ref != 0) { /* * upgrade variables */ o->cref = up->index; if (o->prev != OBJ_NONE) { OBJ(o->prev)->cref = o->index; } up->update++; if (up->count != 0 && up->data == (dataspace *) NULL && up->dfirst != SW_UNUSED) { /* load dataspace (with old control block) */ up->data = d_load_dataspace(up); } } else { /* no variable upgrading */ up->prev = o->prev; o->cref = baseplane.destruct; baseplane.destruct = o->index; } /* swap control blocks */ up->ctrl = o->ctrl; up->ctrl->oindex = up->index; o->ctrl = ctrl; ctrl->oindex = o->index; o->cfirst = up->cfirst; up->cfirst = SW_UNUSED; if (ctrl->ndata != 0) { /* upgrade all dataspaces in memory */ d_upgrade_mem(o, up); } } } o_clean_upgrades(); /* 2nd time */ while (baseplane.destruct != OBJ_NONE) { o = OBJ(baseplane.destruct); baseplane.destruct = o->cref; /* free control block */ d_del_control(o_control(o)); if (o->chain.name != (char *) NULL) { /* free object name */ FREE(o->chain.name); o->chain.name = (char *) NULL; } o->u_ref = 0; /* put object in free list */ o->prev = baseplane.free; baseplane.free = o->index; baseplane.nfreeobjs++; } swap = baseplane.swap; dump = baseplane.dump; stop = baseplane.stop; baseplane.swap = baseplane.dump = baseplane.stop = FALSE; } /* * NAME: object->count() * DESCRIPTION: return the number of objects in use */ uindex o_count() { return oplane->nobjects - oplane->nfreeobjs; } typedef struct { uindex free; /* free object list */ uindex nobjects; /* # objects */ uindex nfreeobjs; /* # free objects */ Uint onamelen; /* length of all object names */ } dump_header; static char dh_layout[] = "uuui"; # define CHUNKSZ 16384 /* * NAME: object->dump() * DESCRIPTION: dump the object table */ bool o_dump(fd) int fd; { register uindex i; register object *o; register unsigned int len, buflen; dump_header dh; char buffer[CHUNKSZ]; /* prepare header */ dh.free = baseplane.free; dh.nobjects = baseplane.nobjects; dh.nfreeobjs = baseplane.nfreeobjs; dh.onamelen = 0; for (i = baseplane.nobjects, o = otable; i > 0; --i, o++) { if (o->chain.name != (char *) NULL) { dh.onamelen += strlen(o->chain.name) + 1; } } /* write header and objects */ if (P_write(fd, (char *) &dh, sizeof(dump_header)) < 0 || P_write(fd, (char *) otable, baseplane.nobjects * sizeof(object)) < 0) { return FALSE; } /* write object names */ buflen = 0; for (i = baseplane.nobjects, o = otable; i > 0; --i, o++) { if (o->chain.name != (char *) NULL) { len = strlen(o->chain.name) + 1; if (buflen + len > CHUNKSZ) { if (P_write(fd, buffer, buflen) < 0) { return FALSE; } buflen = 0; } memcpy(buffer + buflen, o->chain.name, len); buflen += len; } } return (buflen == 0 || P_write(fd, buffer, buflen) >= 0); } /* * NAME: object->restore() * DESCRIPTION: restore the object table */ void o_restore(fd, rlwobj) int fd; unsigned int rlwobj; { register uindex i; register object *o; register unsigned int len, buflen; register char *p; dump_header dh; char buffer[CHUNKSZ]; /* * Free object names of precompiled objects. */ for (i = baseplane.nobjects, o = otable; i > 0; --i, o++) { *ht_lookup(baseplane.htab, o->chain.name, FALSE) = o->chain.next; FREE(o->chain.name); } /* read header and object table */ conf_dread(fd, (char *) &dh, dh_layout, (Uint) 1); if (dh.nobjects > otabsize) { error("Too many objects in restore file"); } conf_dread(fd, (char *) otable, OBJ_LAYOUT, (Uint) dh.nobjects); baseplane.free = dh.free; baseplane.nobjects = dh.nobjects; baseplane.nfreeobjs = dh.nfreeobjs; /* read object names, and patch all objects and control blocks */ buflen = 0; for (i = 0, o = otable; i < baseplane.nobjects; i++, o++) { if (rlwobj != 0) { o->flags &= ~O_LWOBJ; } if (o->chain.name != (char *) NULL) { /* * restore name */ if (buflen == 0 || (char *) memchr(p, '\0', buflen) == (char *) NULL) { /* move remainder to beginning, and refill buffer */ if (buflen != 0) { memcpy(buffer, p, buflen); } len = (dh.onamelen > CHUNKSZ - buflen) ? CHUNKSZ - buflen : dh.onamelen; if (P_read(fd, buffer + buflen, len) != len) { fatal("cannot restore object names"); } dh.onamelen -= len; buflen += len; p = buffer; } m_static(); strcpy(o->chain.name = ALLOC(char, len = strlen(p) + 1), p); m_dynamic(); if (o->count != 0) { register hte **h; /* add name to lookup table */ h = ht_lookup(baseplane.htab, p, FALSE); o->chain.next = *h; *h = (hte *) o; /* fix O_LWOBJ */ if (o->cref & rlwobj) { o->flags |= O_LWOBJ; o->cref &= ~rlwobj; } } p += len; buflen -= len; } else if (o->ref & rlwobj) { o->flags |= O_LWOBJ; o->ref &= ~rlwobj; o->ref++; } if (o->count != 0) { /* there are no user or editor objects after a restore */ if ((o->flags & O_SPECIAL) != O_SPECIAL) { o->flags &= ~O_SPECIAL; } } /* check memory */ if (!m_check()) { m_purge(); } } } static int cmp P((cvoid*, cvoid*)); /* * NAME: cmp() * DESCRIPTION: compare two objects */ static int cmp(cv1, cv2) cvoid *cv1, *cv2; { register object *o1, *o2; /* non-objects first, then objects sorted by count */ o1 = OBJ(*((Uint *) cv1)); o2 = OBJ(*((Uint *) cv2)); if (o1->count == 0) { if (o2->count == 0) { return (o1 <= o2) ? (o1 < o2) ? -1 : 0 : 1; } return -1; } else if (o2->count == 0) { return 1; } else { return (o1->count <= o2->count) ? (o1->count < o2->count) ? -1 : 0 : 1; } } /* * NAME: object->conv() * DESCRIPTION: convert all objects, creating a new swap file */ void o_conv(conv_callouts, conv_lwos, conv_ctrls) int conv_callouts, conv_lwos, conv_ctrls; { register Uint *counts, *sorted; register uindex i; register object *o; if (baseplane.nobjects != 0) { /* * create new object count table */ counts = ALLOCA(Uint, baseplane.nobjects); sorted = ALLOCA(Uint, baseplane.nobjects) + baseplane.nobjects; i = baseplane.nobjects; while (i != 0) { *--sorted = --i; } /* sort by count */ qsort(sorted, baseplane.nobjects, sizeof(Uint), cmp); /* skip destructed objects */ for (i = 0; i < baseplane.nobjects; i++) { if (OBJ(*sorted)->count != 0) { break; } sorted++; } /* fill in counts table */ while (i < baseplane.nobjects) { counts[*sorted++] = ++i + 1; } AFREE(sorted - i); baseplane.ocount = i + 2; /* * convert all control blocks */ for (i = baseplane.nobjects, o = otable; i > 0; --i, o++) { if ((o->count != 0 || ((o->flags & O_MASTER) && o->u_ref != 0)) && o->cfirst != SW_UNUSED) { d_conv_control(o->index, conv_ctrls); } } /* * convert all data blocks */ for (i = baseplane.nobjects, o = otable; i > 0; --i, o++) { if (o->count != 0 && o->dfirst != SW_UNUSED) { d_conv_dataspace(o, counts, conv_callouts); o_clean_upgrades(); d_swapout(1); } } /* * clean up object upgrade templates */ for (i = baseplane.nobjects, o = otable; i > 0; --i, o++) { if (o->count != 0 && o->cfirst != SW_UNUSED && (o->flags & O_LWOBJ) && o_purge_upgrades(o)) { o->prev = OBJ_NONE; } } o_clean(); /* * last pass over all objects: * fix count and update fields, handle special objects */ for (i = baseplane.nobjects, o = otable; i > 0; --i, o++, counts++) { if (o->count != 0) { o->count = *counts; } o->update = 0; if ((o->flags & O_SPECIAL) == O_SPECIAL && ext_restore != (void (*) P((object*))) NULL) { (*ext_restore)(o); d_swapout(1); } } AFREE(counts - baseplane.nobjects); } } /* * NAME: swapout() * DESCRIPTION: indicate that objects are to be swapped out */ void swapout() { oplane->swap = TRUE; } /* * NAME: dump_state() * DESCRIPTION: indicate that the state must be dumped */ void dump_state() { oplane->dump = TRUE; } /* * NAME: finish() * DESCRIPTION: indicate that the program must finish */ void finish() { oplane->stop = TRUE; }