# 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" # define OBJ_NONE UINDEX_MAX object *otable; /* object table */ static uindex otabsize; /* size of object table */ static hashtab *htab; /* object name hash table */ static object *clean_obj; /* list of objects to clean */ static object *upgrade_obj; /* list of upgrade objects */ static uindex dest_obj; /* destructed object list */ static uindex free_obj; /* free object list */ static uindex nobjects; /* number of objects in object table */ static uindex nfreeobjs; /* number of objects in free list */ static Uint ocount; /* object creation count */ 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); for (n = 4; n < otabsize; n <<= 1) ; htab = ht_new(n >> 2, OBJHASHSZ); upgrade_obj = clean_obj = (object *) NULL; free_obj = dest_obj = OBJ_NONE; nobjects = 0; nfreeobjs = 0; ocount = odcount = 1; } /* * NAME: object->alloc() * DESCRIPTION: allocate a new object */ static object *o_alloc() { object *o; if (free_obj != OBJ_NONE) { /* get space from free object list */ o = &otable[free_obj]; free_obj = o->prev; --nfreeobjs; } else { /* use new space in object table */ if (nobjects == otabsize) { fatal("too many objects"); } o = &otable[nobjects++]; } o->data = (dataspace *) NULL; o->cfirst = SW_UNUSED; o->dfirst = SW_UNUSED; return o; } /* * 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 */ m_static(); strcpy(o->chain.name = ALLOC(char, strlen(name) + 1), name); m_dynamic(); h = ht_lookup(htab, name, FALSE); o->chain.next = *h; *h = (hte *) o; o->flags = O_MASTER; o->cref = 0; o->prev = OBJ_NONE; o->index = o - otable; o->count = ++ocount; o->update = 0; o->ctrl = ctrl; ctrl->inherits[ctrl->ninherits - 1].obj = ctrl->obj = o; /* add reference to all inherited objects */ o->u_ref = 0; /* increased to 1 in following loop */ inh = ctrl->inherits; for (i = ctrl->ninherits; i > 0; --i) { (inh++)->obj->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->index = o - otable; o->count = ++ocount; o->update = master->update; o->u_master = master->index; o->ctrl = (control *) NULL; o->data = d_new_dataspace(o); /* clones always have a dataspace */ /* add reference to master object */ master->cref++; master->u_ref++; return o; } /* * 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)) ? otable[o->prev].ctrl : o_control(o); /* put in deleted list */ o->u_master = dest_obj; dest_obj = o - otable; /* callback to the system */ (--f->sp)->type = T_STRING; str_ref(f->sp->u.string = str_new(NULL, strlen(o->chain.name) + 1L)); f->sp->u.string->text[0] = '/'; strcpy(f->sp->u.string->text + 1, o->chain.name); (--f->sp)->type = T_INT; f->sp->u.number = ctrl->compiled; (--f->sp)->type = T_INT; f->sp->u.number = o->index; if (i_call_critical(f, "remove_program", 3, TRUE)) { i_del_value(f->sp++); } /* remove references to inherited objects too */ inh = ctrl->inherits; i = ctrl->ninherits; while (--i > 0) { o = (inh++)->obj; if (--(o->u_ref) == 0) { o_delete(o, f); } } } /* * NAME: object->upgrade() * DESCRIPTION: upgrade an object to a new program */ void o_upgrade(obj, ctrl, f) object *obj; control *ctrl; register frame *f; { register object *o; register dinherit *inh; register int i; /* allocate upgrade object */ o = o_alloc(); o->chain.name = (char *) NULL; o->flags = O_MASTER; o->count = 0; o->u_master = obj->index; o->ctrl = ctrl; ctrl->inherits[ctrl->ninherits - 1].obj = obj; /* add reference to inherited objects */ inh = ctrl->inherits; i = ctrl->ninherits; while (--i > 0) { (inh++)->obj->u_ref++; } /* add to upgrades list */ o->chain.next = (hte *) upgrade_obj; upgrade_obj = o; /* mark as upgrading */ obj->cref += 2; o->prev = obj->prev; obj->prev = o - otable; /* remove references to old inherited objects */ ctrl = o_control(obj); inh = ctrl->inherits; i = ctrl->ninherits; while (--i > 0) { o = (inh++)->obj; if (--(o->u_ref) == 0) { o_delete(o, f); } } } /* * NAME: object->upgraded() * DESCRIPTION: an object has been upgraded */ void o_upgraded(old, new) register object *old, *new; { if (new->count != 0) { if (!(new->flags & O_MASTER)) { new->update = otable[new->u_master].update; new = &otable[new->u_master]; new->cref++; new->u_ref++; } while (--(old->u_ref) == 0) { /* put in deleted list */ old->u_master = dest_obj; dest_obj = old - otable; # ifdef DEBUG if (old->prev != OBJ_NONE) { fatal("removing issue in middle of list"); } # endif /* remove from issue list */ old = &otable[old->cref]; old->prev = OBJ_NONE; if (old == new) { new->cref--; new->u_ref--; break; } } } else if (!(new->flags & O_MASTER)) { new->update = otable[new->u_master].update; } } /* * NAME: object->del() * DESCRIPTION: delete an object */ void o_del(o, f) register object *o; frame *f; { if (o->count == 0) { /* can happen if object selfdestructs in close()-on-destruct */ error("Destructing destructed object"); } o->count = 0; odcount++; if (o->flags & O_MASTER) { /* remove from object name hash table */ *ht_lookup(htab, o->chain.name, FALSE) = o->chain.next; if (--(o->u_ref) == 0) { o_delete(o, f); } } else { register object *m; /* clone */ m = &otable[o->u_master]; if (m->update == o->update) { m->cref--; if (--(m->u_ref) == 0) { o_delete(m, f); } } else { /* non-upgraded clone of old issue */ do { m = &otable[m->prev]; } while (m->update != o->update); if (--(m->u_ref) == 0) { /* put old issue in deleted list */ m->u_master = dest_obj; dest_obj = m - otable; m = &otable[o->u_master]; if (--(m->u_ref) == 0) { o_delete(m, f); } } } } /* put in clean list */ o->chain.next = (hte *) clean_obj; clean_obj = o; } /* * NAME: object->name() * DESCRIPTION: return the name of an object */ char *o_name(o) register object *o; { if (o->chain.name != (char *) NULL) { return o->chain.name; } else { static char name[STRINGSZ + 12]; 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, otable[o->u_master].chain.name); strcat(name, p); return name; } } /* * NAME: object->find() * DESCRIPTION: find an object by name */ object *o_find(name) char *name; { char *hash; hash = strchr(name, '#'); if (hash != (char *) NULL) { register char *p; register unsigned long number; register object *o; /* * 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 >= nobjects) { return (object *) NULL; } } while (*p != '\0'); o = &otable[number]; if (o->count == 0 || (o->flags & O_MASTER) || strncmp(name, otable[o->u_master].chain.name, hash - name) != 0 || otable[o->u_master].chain.name[hash - name] != '\0') { /* * no entry, not a clone, or object name doesn't match */ return (object *) NULL; } return o; } else { /* look it up in the hash table */ return (object *) *ht_lookup(htab, name, TRUE); } } /* * 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)) { o = &otable[o->u_master]; /* get control block of master object */ } 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) { if (o->dfirst == SW_UNUSED) { /* create new dataspace block */ o->data = d_new_dataspace(o); } else { /* load dataspace block */ o->data = d_load_dataspace(o); } } else { d_ref_dataspace(o->data); } return o->data; } /* * NAME: object->clean() * DESCRIPTION: clean up the object table */ void o_clean() { register object *o; for (o = clean_obj; o != (object *) NULL; o = (object *) 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)) { /* put clone in free list */ o->prev = free_obj; free_obj = o - otable; nfreeobjs++; } } clean_obj = (object *) NULL; for (o = upgrade_obj; o != (object *) NULL; o = (object *) o->chain.next) { register object *up; register control *ctrl; up = &otable[o->u_master]; up->cref -= 2; up->prev = o->prev; if (up->count == 0 && up->cref == 0) { /* also remove upgrader */ o->u_master = dest_obj; dest_obj = o - otable; } else { /* upgrade objects */ up->flags &= ~O_COMPILED; o->u_ref = up->u_ref; ctrl = up->ctrl; if (ctrl->vmapsize != 0 && (up->data != (dataspace *) NULL || up->dfirst != SW_UNUSED || up->count == 0 || --(o->u_ref) != 0)) { /* upgrade variables */ o->cref = o->index = up->index; if (o->prev != OBJ_NONE) { otable[o->prev].cref = o - otable; } o->update = up->update; up->prev = o - otable; up->cref = 1; up->u_ref = 1; up->update++; if (up->count != 0) { up->u_ref++; if (up->data == (dataspace *) NULL && up->dfirst != SW_UNUSED) { /* load dataspace (with old control block) */ up->data = d_load_dataspace(up); } } } else { /* no variable upgrading */ o->u_master = dest_obj; dest_obj = o - otable; } /* swap control blocks */ up->ctrl = o->ctrl; up->ctrl->obj = up; o->ctrl = ctrl; ctrl->obj = o; o->cfirst = up->cfirst; up->cfirst = SW_UNUSED; if (ctrl->ndata != 0) { /* upgrade all dataspaces in memory */ d_upgrade_all(o, up); } } } upgrade_obj = (object *) NULL; while (dest_obj != OBJ_NONE) { o = &otable[dest_obj]; /* 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; } /* put object in free list */ o->prev = free_obj; free_obj = o - otable; nfreeobjs++; dest_obj = o->u_master; o->u_ref = 0; } } /* * NAME: object->count() * DESCRIPTION: return the number of objects in use */ uindex o_count() { return nobjects - nfreeobjs; } typedef struct { uindex free_obj; /* 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_obj = free_obj; dh.nobjects = nobjects; dh.nfreeobjs = nfreeobjs; dh.onamelen = 0; for (i = 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 (write(fd, (char *) &dh, sizeof(dump_header)) < 0 || write(fd, (char *) otable, nobjects * sizeof(object)) < 0) { return FALSE; } /* write object names */ buflen = 0; for (i = nobjects, o = otable; i > 0; --i, o++) { if (o->chain.name != (char *) NULL) { len = strlen(o->chain.name) + 1; if (buflen + len > CHUNKSZ) { if (write(fd, buffer, buflen) < 0) { return FALSE; } buflen = 0; } memcpy(buffer + buflen, o->chain.name, len); buflen += len; } } return (buflen == 0 || write(fd, buffer, buflen) >= 0); } /* * NAME: object->restore() * DESCRIPTION: restore the object table */ void o_restore(fd) int fd; { 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 = nobjects, o = otable; i > 0; --i, o++) { *ht_lookup(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); free_obj = dh.free_obj; nobjects = dh.nobjects; nfreeobjs = dh.nfreeobjs; /* read object names, and patch all objects and control blocks */ buflen = 0; for (i = nobjects, o = otable; i > 0; --i, o++) { 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 (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; h = ht_lookup(htab, p, FALSE); o->chain.next = *h; *h = (hte *) o; } p += len; buflen -= len; } if (o->count != 0) { /* there are no user or editor objects after a restore */ o->flags &= ~(O_USER | O_EDITOR | O_PENDIO); } /* 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 = &otable[*((Uint *) cv1)]; o2 = &otable[*((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() { register Uint *counts, *sorted; register uindex i; register object *o; if (nobjects != 0) { /* * create new object count table */ counts = ALLOCA(Uint, nobjects); sorted = ALLOCA(Uint, nobjects) + nobjects; i = nobjects; while (i != 0) { *--sorted = --i; } /* sort by count */ qsort(sorted, nobjects, sizeof(Uint), cmp); /* skip destructed objects */ for (i = 0; i < nobjects; i++) { if (otable[*sorted].count != 0) { break; } sorted++; } /* fill in counts table */ while (i < nobjects) { counts[*sorted++] = ++i + 1; } AFREE(sorted - i); ocount = i + 1; /* * convert all control blocks */ for (i = 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); } } /* * convert all data blocks */ for (i = nobjects, o = otable; i > 0; --i, o++) { if (o->count != 0 && o->dfirst != SW_UNUSED) { d_conv_dataspace(o, counts); d_swapout(1); } } /* * fix count and update fields of all objects */ for (i = nobjects, o = otable; i > 0; --i, o++, counts++) { if (o->count != 0) { o->count = *counts; } o->update = 0; } AFREE(counts - nobjects); o_clean(); } }