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