1 /******************************************************************************
2   Copyright (c) 1995, 1996 Xerox Corporation.  All rights reserved.
3   Portions of this code were written by Stephen White, aka ghond.
4   Use and copying of this software and preparation of derivative works based
5   upon this software are permitted.  Any distribution of this software or
6   derivative works must comply with all applicable United States export
7   control laws.  This software is made available AS IS, and Xerox Corporation
8   makes no warranty about the software, its performance or its conformity to
9   any specification.  Any person obtaining a copy of this software is requested
10   to send their name and post office or electronic mail address to:
11     Pavel Curtis
12     Xerox PARC
13     3333 Coyote Hill Rd.
14     Palo Alto, CA 94304
15     Pavel@Xerox.Com
16  *****************************************************************************/
17 
18 /*****************************************************************************
19  * Routines for manipulating DB objects
20  *****************************************************************************/
21 
22 #include "config.h"
23 #include "db.h"
24 #include "db_private.h"
25 #include "list.h"
26 #include "program.h"
27 #include "storage.h"
28 #include "utils.h"
29 
30 static Object **objects;
31 static int num_objects = 0;
32 static int max_objects = 0;
33 
34 static Var all_users;
35 
36 
37 /*********** Objects qua objects ***********/
38 
39 Object *
dbpriv_find_object(Objid oid)40 dbpriv_find_object(Objid oid)
41 {
42     if (oid < 0 || oid >= num_objects)
43 	return 0;
44     else
45 	return objects[oid];
46 }
47 
48 int
valid(Objid oid)49 valid(Objid oid)
50 {
51     return dbpriv_find_object(oid) != 0;
52 }
53 
54 Objid
db_last_used_objid(void)55 db_last_used_objid(void)
56 {
57     return num_objects - 1;
58 }
59 
60 void
db_reset_last_used_objid(void)61 db_reset_last_used_objid(void)
62 {
63     while (!objects[num_objects - 1])
64 	num_objects--;
65 }
66 
67 static void
ensure_new_object(void)68 ensure_new_object(void)
69 {
70     if (max_objects == 0) {
71 	max_objects = 100;
72 	objects = mymalloc(max_objects * sizeof(Object *), M_OBJECT_TABLE);
73     }
74     if (num_objects >= max_objects) {
75 	int i;
76 	Object **new;
77 
78 	new = mymalloc(max_objects * 2 * sizeof(Object *), M_OBJECT_TABLE);
79 	for (i = 0; i < max_objects; i++)
80 	    new[i] = objects[i];
81 	myfree(objects, M_OBJECT_TABLE);
82 	objects = new;
83 	max_objects *= 2;
84     }
85 }
86 
87 Object *
dbpriv_new_object(void)88 dbpriv_new_object(void)
89 {
90     Object *o;
91 
92     ensure_new_object();
93     o = objects[num_objects] = mymalloc(sizeof(Object), M_OBJECT);
94     o->id = num_objects;
95     num_objects++;
96 
97     return o;
98 }
99 
100 void
dbpriv_new_recycled_object(void)101 dbpriv_new_recycled_object(void)
102 {
103     ensure_new_object();
104     objects[num_objects++] = 0;
105 }
106 
107 Objid
db_create_object(void)108 db_create_object(void)
109 {
110     Object *o;
111     Objid oid;
112 
113     o = dbpriv_new_object();
114     oid = o->id;
115 
116     o->name = str_dup("");
117     o->flags = 0;
118     o->parent = o->child = o->sibling = NOTHING;
119     o->location = o->contents = o->next = NOTHING;
120 
121     o->propval = 0;
122 
123     o->propdefs.max_length = 0;
124     o->propdefs.cur_length = 0;
125     o->propdefs.l = 0;
126 
127     o->verbdefs = 0;
128 
129     return oid;
130 }
131 
132 void
db_destroy_object(Objid oid)133 db_destroy_object(Objid oid)
134 {
135     Object *o = dbpriv_find_object(oid);
136     Verbdef *v, *w;
137     int i;
138 
139     db_priv_affected_callable_verb_lookup();
140 
141     if (!o)
142 	panic("DB_DESTROY_OBJECT: Invalid object!");
143 
144     if (o->location != NOTHING || o->contents != NOTHING
145 	|| o->parent != NOTHING || o->child != NOTHING)
146 	panic("DB_DESTROY_OBJECT: Not a barren orphan!");
147 
148     if (is_user(oid)) {
149 	Var t;
150 
151 	t.type = TYPE_OBJ;
152 	t.v.obj = oid;
153 	all_users = setremove(all_users, t);
154     }
155     free_str(o->name);
156 
157     for (i = 0; i < o->propdefs.cur_length; i++) {
158 	/* As an orphan, the only properties on this object are the ones
159 	 * defined on it directly, so these two arrays must be the same length.
160 	 */
161 	free_str(o->propdefs.l[i].name);
162 	free_var(o->propval[i].var);
163     }
164     if (o->propval)
165 	myfree(o->propval, M_PVAL);
166     if (o->propdefs.l)
167 	myfree(o->propdefs.l, M_PROPDEF);
168 
169     for (v = o->verbdefs; v; v = w) {
170 	if (v->program)
171 	    free_program(v->program);
172 	free_str(v->name);
173 	w = v->next;
174 	myfree(v, M_VERBDEF);
175     }
176 
177     myfree(objects[oid], M_OBJECT);
178     objects[oid] = 0;
179 }
180 
181 Objid
db_renumber_object(Objid old)182 db_renumber_object(Objid old)
183 {
184     Objid new;
185     Object *o;
186 
187     db_priv_affected_callable_verb_lookup();
188 
189     for (new = 0; new < old; new++) {
190 	if (objects[new] == 0) {
191 	    /* Change the identity of the object. */
192 	    o = objects[new] = objects[old];
193 	    objects[old] = 0;
194 	    objects[new]->id = new;
195 
196 	    /* Fix up the parent/children hierarchy */
197 	    {
198 		Objid oid, *oidp;
199 
200 		if (o->parent != NOTHING) {
201 		    oidp = &objects[o->parent]->child;
202 		    while (*oidp != old && *oidp != NOTHING)
203 			oidp = &objects[*oidp]->sibling;
204 		    if (*oidp == NOTHING)
205 			panic("Object not in parent's children list");
206 		    *oidp = new;
207 		}
208 		for (oid = o->child;
209 		     oid != NOTHING;
210 		     oid = objects[oid]->sibling)
211 		    objects[oid]->parent = new;
212 	    }
213 
214 	    /* Fix up the location/contents hierarchy */
215 	    {
216 		Objid oid, *oidp;
217 
218 		if (o->location != NOTHING) {
219 		    oidp = &objects[o->location]->contents;
220 		    while (*oidp != old && *oidp != NOTHING)
221 			oidp = &objects[*oidp]->next;
222 		    if (*oidp == NOTHING)
223 			panic("Object not in location's contents list");
224 		    *oidp = new;
225 		}
226 		for (oid = o->contents;
227 		     oid != NOTHING;
228 		     oid = objects[oid]->next)
229 		    objects[oid]->location = new;
230 	    }
231 
232 	    /* Fix up the list of users, if necessary */
233 	    if (is_user(new)) {
234 		int i;
235 
236 		for (i = 1; i <= all_users.v.list[0].v.num; i++)
237 		    if (all_users.v.list[i].v.obj == old) {
238 			all_users.v.list[i].v.obj = new;
239 			break;
240 		    }
241 	    }
242 	    /* Fix the owners of verbs, properties and objects */
243 	    {
244 		Objid oid;
245 
246 		for (oid = 0; oid < num_objects; oid++) {
247 		    Object *o = objects[oid];
248 		    Verbdef *v;
249 		    Pval *p;
250 		    int i, count;
251 
252 		    if (!o)
253 			continue;
254 
255 		    if (o->owner == new)
256 			o->owner = NOTHING;
257 		    else if (o->owner == old)
258 			o->owner = new;
259 
260 		    for (v = o->verbdefs; v; v = v->next)
261 			if (v->owner == new)
262 			    v->owner = NOTHING;
263 			else if (v->owner == old)
264 			    v->owner = new;
265 
266 		    count = dbpriv_count_properties(oid);
267 		    p = o->propval;
268 		    for (i = 0; i < count; i++)
269 			if (p[i].owner == new)
270 			    p[i].owner = NOTHING;
271 			else if (p[i].owner == old)
272 			    p[i].owner = new;
273 		}
274 	    }
275 
276 	    return new;
277 	}
278     }
279 
280     /* There are no recycled objects less than `old', so keep its number. */
281     return old;
282 }
283 
284 int
db_object_bytes(Objid oid)285 db_object_bytes(Objid oid)
286 {
287     Object *o = objects[oid];
288     int i, len, count;
289     Verbdef *v;
290 
291     count = sizeof(Object) + sizeof(Object *);
292     count += strlen(o->name) + 1;
293 
294     for (v = o->verbdefs; v; v = v->next) {
295 	count += sizeof(Verbdef);
296 	count += strlen(v->name) + 1;
297 	if (v->program)
298 	    count += program_bytes(v->program);
299     }
300 
301     count += sizeof(Propdef) * o->propdefs.cur_length;
302     for (i = 0; i < o->propdefs.cur_length; i++)
303 	count += strlen(o->propdefs.l[i].name) + 1;
304 
305     len = dbpriv_count_properties(oid);
306     count += (sizeof(Pval) - sizeof(Var)) * len;
307     for (i = 0; i < len; i++)
308 	count += value_bytes(o->propval[i].var);
309 
310     return count;
311 }
312 
313 
314 /*********** Object attributes ***********/
315 
316 Objid
db_object_owner(Objid oid)317 db_object_owner(Objid oid)
318 {
319     return objects[oid]->owner;
320 }
321 
322 void
db_set_object_owner(Objid oid,Objid owner)323 db_set_object_owner(Objid oid, Objid owner)
324 {
325     objects[oid]->owner = owner;
326 }
327 
328 const char *
db_object_name(Objid oid)329 db_object_name(Objid oid)
330 {
331     return objects[oid]->name;
332 }
333 
334 void
db_set_object_name(Objid oid,const char * name)335 db_set_object_name(Objid oid, const char *name)
336 {
337     Object *o = objects[oid];
338 
339     if (o->name)
340 	free_str(o->name);
341     o->name = name;
342 }
343 
344 Objid
db_object_parent(Objid oid)345 db_object_parent(Objid oid)
346 {
347     return objects[oid]->parent;
348 }
349 
350 int
db_count_children(Objid oid)351 db_count_children(Objid oid)
352 {
353     Objid c;
354     int i = 0;
355 
356     for (c = objects[oid]->child; c != NOTHING; c = objects[c]->sibling)
357 	i++;
358 
359     return i;
360 }
361 
362 int
db_for_all_children(Objid oid,int (* func)(void *,Objid),void * data)363 db_for_all_children(Objid oid, int (*func) (void *, Objid), void *data)
364 {
365     Objid c;
366 
367     for (c = objects[oid]->child; c != NOTHING; c = objects[c]->sibling)
368 	if (func(data, c))
369 	    return 1;
370 
371     return 0;
372 }
373 
374 #define LL_REMOVE(where, listname, what, nextname) { \
375     Objid lid; \
376     if (objects[where]->listname == what) \
377 	objects[where]->listname = objects[what]->nextname; \
378     else { \
379 	for (lid = objects[where]->listname; lid != NOTHING; \
380 	      lid = objects[lid]->nextname) { \
381 	    if (objects[lid]->nextname == what) { \
382 		objects[lid]->nextname = objects[what]->nextname; \
383 		break; \
384 	    } \
385 	} \
386     } \
387     objects[what]->nextname = NOTHING; \
388 }
389 
390 #define LL_APPEND(where, listname, what, nextname) { \
391     Objid lid; \
392     if (objects[where]->listname == NOTHING) { \
393 	objects[where]->listname = what; \
394     } else { \
395 	for (lid = objects[where]->listname; \
396 	     objects[lid]->nextname != NOTHING; \
397 	     lid = objects[lid]->nextname) \
398 	    ; \
399 	objects[lid]->nextname = what; \
400     } \
401     objects[what]->nextname = NOTHING; \
402 }
403 
404 int
db_change_parent(Objid oid,Objid parent)405 db_change_parent(Objid oid, Objid parent)
406 {
407     Objid old_parent;
408 
409     if (!dbpriv_check_properties_for_chparent(oid, parent))
410 	return 0;
411 
412     if (objects[oid]->child == NOTHING && objects[oid]->verbdefs == NULL) {
413 	/* Since this object has no children and no verbs, we know that it
414 	   can't have had any part in affecting verb lookup, since we use first
415 	   parent with verbs as a key in the verb lookup cache. */
416 	/* The "no kids" rule is necessary because potentially one of the kids
417 	   could have verbs on it--and that kid could have cache entries for
418 	   THIS object's parentage. */
419 	/* In any case, don't clear the cache. */
420 	;
421     } else {
422 	db_priv_affected_callable_verb_lookup();
423     }
424 
425     old_parent = objects[oid]->parent;
426 
427     if (old_parent != NOTHING)
428 	LL_REMOVE(old_parent, child, oid, sibling);
429 
430     if (parent != NOTHING)
431 	LL_APPEND(parent, child, oid, sibling);
432 
433     objects[oid]->parent = parent;
434     dbpriv_fix_properties_after_chparent(oid, old_parent);
435 
436     return 1;
437 }
438 
439 Objid
db_object_location(Objid oid)440 db_object_location(Objid oid)
441 {
442     return objects[oid]->location;
443 }
444 
445 int
db_count_contents(Objid oid)446 db_count_contents(Objid oid)
447 {
448     Objid c;
449     int i = 0;
450 
451     for (c = objects[oid]->contents; c != NOTHING; c = objects[c]->next)
452 	i++;
453 
454     return i;
455 }
456 
457 int
db_for_all_contents(Objid oid,int (* func)(void *,Objid),void * data)458 db_for_all_contents(Objid oid, int (*func) (void *, Objid), void *data)
459 {
460     Objid c;
461 
462     for (c = objects[oid]->contents; c != NOTHING; c = objects[c]->next)
463 	if (func(data, c))
464 	    return 1;
465 
466     return 0;
467 }
468 
469 void
db_change_location(Objid oid,Objid location)470 db_change_location(Objid oid, Objid location)
471 {
472     Objid old_location = objects[oid]->location;
473 
474     if (valid(old_location))
475 	LL_REMOVE(old_location, contents, oid, next);
476 
477     if (valid(location))
478 	LL_APPEND(location, contents, oid, next);
479 
480     objects[oid]->location = location;
481 }
482 
483 int
db_object_has_flag(Objid oid,db_object_flag f)484 db_object_has_flag(Objid oid, db_object_flag f)
485 {
486     return (objects[oid]->flags & (1 << f)) != 0;
487 }
488 
489 void
db_set_object_flag(Objid oid,db_object_flag f)490 db_set_object_flag(Objid oid, db_object_flag f)
491 {
492     objects[oid]->flags |= (1 << f);
493     if (f == FLAG_USER) {
494 	Var v;
495 
496 	v.type = TYPE_OBJ;
497 	v.v.obj = oid;
498 	all_users = setadd(all_users, v);
499     }
500 }
501 
502 void
db_clear_object_flag(Objid oid,db_object_flag f)503 db_clear_object_flag(Objid oid, db_object_flag f)
504 {
505     objects[oid]->flags &= ~(1 << f);
506     if (f == FLAG_USER) {
507 	Var v;
508 
509 	v.type = TYPE_OBJ;
510 	v.v.obj = oid;
511 	all_users = setremove(all_users, v);
512     }
513 }
514 
515 int
db_object_allows(Objid oid,Objid progr,db_object_flag f)516 db_object_allows(Objid oid, Objid progr, db_object_flag f)
517 {
518     return (progr == db_object_owner(oid)
519 	    || is_wizard(progr)
520 	    || db_object_has_flag(oid, f));
521 }
522 
523 int
is_wizard(Objid oid)524 is_wizard(Objid oid)
525 {
526     return valid(oid) && db_object_has_flag(oid, FLAG_WIZARD);
527 }
528 
529 int
is_programmer(Objid oid)530 is_programmer(Objid oid)
531 {
532     return valid(oid) && db_object_has_flag(oid, FLAG_PROGRAMMER);
533 }
534 
535 int
is_user(Objid oid)536 is_user(Objid oid)
537 {
538     return valid(oid) && db_object_has_flag(oid, FLAG_USER);
539 }
540 
541 Var
db_all_users(void)542 db_all_users(void)
543 {
544     return all_users;
545 }
546 
547 void
dbpriv_set_all_users(Var v)548 dbpriv_set_all_users(Var v)
549 {
550     all_users = v;
551 }
552 
553 char rcsid_db_objects[] = "$Id: db_objects.c,v 1.4 1998/12/14 13:17:36 nop Exp $";
554 
555 /*
556  * $Log: db_objects.c,v $
557  * Revision 1.4  1998/12/14 13:17:36  nop
558  * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
559  *
560  * Revision 1.3  1997/07/07 03:24:53  nop
561  * Merge UNSAFE_OPTS (r5) after extensive testing.
562  *
563  * Revision 1.2.2.2  1997/07/07 01:40:20  nop
564  * Because we use first-parent-with-verbs as a verb cache key, we can skip
565  * a generation bump if the target of a chparent has no kids and no verbs.
566  *
567  * Revision 1.2.2.1  1997/03/20 07:26:01  nop
568  * First pass at the new verb cache.  Some ugly code inside.
569  *
570  * Revision 1.2  1997/03/03 04:18:29  nop
571  * GNU Indent normalization
572  *
573  * Revision 1.1.1.1  1997/03/03 03:44:59  nop
574  * LambdaMOO 1.8.0p5
575  *
576  * Revision 2.5  1996/04/08  00:42:11  pavel
577  * Adjusted computation in `db_object_bytes()' to account for change in the
578  * definition of `value_bytes()'.  Release 1.8.0p3.
579  *
580  * Revision 2.4  1996/02/08  07:18:13  pavel
581  * Updated copyright notice for 1996.  Release 1.8.0beta1.
582  *
583  * Revision 2.3  1996/01/16  07:23:45  pavel
584  * Fixed object-array overrun when a recycled object is right on the boundary.
585  * Release 1.8.0alpha6.
586  *
587  * Revision 2.2  1996/01/11  07:30:53  pavel
588  * Fixed memory-smash bug in db_renumber_object().  Release 1.8.0alpha5.
589  *
590  * Revision 2.1  1995/12/11  08:08:28  pavel
591  * Added `db_object_bytes()'.  Release 1.8.0alpha2.
592  *
593  * Revision 2.0  1995/11/30  04:20:51  pavel
594  * New baseline version, corresponding to release 1.8.0alpha1.
595  *
596  * Revision 1.1  1995/11/30  04:20:41  pavel
597  * Initial revision
598  */
599