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 properties on DB objects
20 *****************************************************************************/
21
22 #include "config.h"
23 #include "db.h"
24 #include "db_private.h"
25 #include "list.h"
26 #include "storage.h"
27 #include "utils.h"
28
29 Propdef
dbpriv_new_propdef(const char * name)30 dbpriv_new_propdef(const char *name)
31 {
32 Propdef newprop;
33
34 newprop.name = str_ref(name);
35 newprop.hash = str_hash(name);
36 return newprop;
37 }
38
39 int
dbpriv_count_properties(Objid oid)40 dbpriv_count_properties(Objid oid)
41 {
42 Object *o;
43 int nprops = 0;
44
45 for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent))
46 nprops += o->propdefs.cur_length;
47
48 return nprops;
49 }
50
51 static int
property_defined_at_or_below(const char * pname,int phash,Objid oid)52 property_defined_at_or_below(const char *pname, int phash, Objid oid)
53 {
54 /* Return true iff some descendant of OID defines a property named PNAME.
55 */
56 Objid c;
57 Proplist *props = &dbpriv_find_object(oid)->propdefs;
58 int length = props->cur_length;
59 int i;
60
61 for (i = 0; i < length; i++)
62 if (props->l[i].hash == phash
63 && !mystrcasecmp(props->l[i].name, pname))
64 return 1;
65
66 for (c = dbpriv_find_object(oid)->child;
67 c != NOTHING;
68 c = dbpriv_find_object(c)->sibling)
69 if (property_defined_at_or_below(pname, phash, c))
70 return 1;
71
72 return 0;
73 }
74
75 static void
insert_prop(Objid oid,int pos,Pval pval)76 insert_prop(Objid oid, int pos, Pval pval)
77 {
78 Pval *new_propval;
79 Object *o;
80 int i, nprops;
81
82 nprops = dbpriv_count_properties(oid);
83 new_propval = mymalloc(nprops * sizeof(Pval), M_PVAL);
84
85 o = dbpriv_find_object(oid);
86
87 for (i = 0; i < pos; i++)
88 new_propval[i] = o->propval[i];
89
90 new_propval[pos] = pval;
91 new_propval[pos].var = var_ref(pval.var);
92 if (new_propval[pos].perms & PF_CHOWN)
93 new_propval[pos].owner = o->owner;
94
95 for (i = pos + 1; i < nprops; i++)
96 new_propval[i] = o->propval[i - 1];
97
98 if (o->propval)
99 myfree(o->propval, M_PVAL);
100 o->propval = new_propval;
101 }
102
103 static void
insert_prop_recursively(Objid root,int root_pos,Pval pv)104 insert_prop_recursively(Objid root, int root_pos, Pval pv)
105 {
106 Objid c;
107
108 insert_prop(root, root_pos, pv);
109 pv.var.type = TYPE_CLEAR; /* do after initial insert_prop so only
110 children will be TYPE_CLEAR */
111 for (c = dbpriv_find_object(root)->child;
112 c != NOTHING;
113 c = dbpriv_find_object(c)->sibling) {
114 int new_prop_count = dbpriv_find_object(c)->propdefs.cur_length;
115
116 insert_prop_recursively(c, new_prop_count + root_pos, pv);
117 }
118 }
119
120 int
db_add_propdef(Objid oid,const char * pname,Var value,Objid owner,unsigned flags)121 db_add_propdef(Objid oid, const char *pname, Var value, Objid owner,
122 unsigned flags)
123 {
124 Object *o;
125 Pval pval;
126 int i;
127 db_prop_handle h;
128
129 h = db_find_property(oid, pname, 0);
130
131 if (h.ptr || property_defined_at_or_below(pname, str_hash(pname), oid))
132 return 0;
133
134 o = dbpriv_find_object(oid);
135 if (o->propdefs.cur_length == o->propdefs.max_length) {
136 Propdef *old_props = o->propdefs.l;
137 int new_size = (o->propdefs.max_length == 0
138 ? 8 : 2 * o->propdefs.max_length);
139
140 o->propdefs.l = mymalloc(new_size * sizeof(Propdef), M_PROPDEF);
141 for (i = 0; i < o->propdefs.max_length; i++)
142 o->propdefs.l[i] = old_props[i];
143 o->propdefs.max_length = new_size;
144
145 if (old_props)
146 myfree(old_props, M_PROPDEF);
147 }
148 o->propdefs.l[o->propdefs.cur_length++] = dbpriv_new_propdef(pname);
149
150 pval.var = value;
151 pval.owner = owner;
152 pval.perms = flags;
153
154 insert_prop_recursively(oid, o->propdefs.cur_length - 1, pval);
155
156 return 1;
157 }
158
159 int
db_rename_propdef(Objid oid,const char * old,const char * new)160 db_rename_propdef(Objid oid, const char *old, const char *new)
161 {
162 Proplist *props = &dbpriv_find_object(oid)->propdefs;
163 int hash = str_hash(old);
164 int count = props->cur_length;
165 int i;
166 db_prop_handle h;
167
168 for (i = 0; i < count; i++) {
169 Propdef p;
170
171 p = props->l[i];
172 if (p.hash == hash && !mystrcasecmp(p.name, old)) {
173 if (mystrcasecmp(old, new) != 0) { /* Not changing just the case */
174 h = db_find_property(oid, new, 0);
175 if (h.ptr
176 || property_defined_at_or_below(new, str_hash(new), oid))
177 return 0;
178 }
179 free_str(props->l[i].name);
180 props->l[i].name = str_ref(new);
181 props->l[i].hash = str_hash(new);
182
183 return 1;
184 }
185 }
186
187 return 0;
188 }
189
190 static void
remove_prop(Objid oid,int pos)191 remove_prop(Objid oid, int pos)
192 {
193 Pval *new_propval;
194 Object *o;
195 int i, nprops;
196
197 o = dbpriv_find_object(oid);
198 nprops = dbpriv_count_properties(oid);
199
200 free_var(o->propval[pos].var); /* free deleted property */
201
202 if (nprops) {
203 new_propval = mymalloc(nprops * sizeof(Pval), M_PVAL);
204 for (i = 0; i < pos; i++)
205 new_propval[i] = o->propval[i];
206 for (i = pos; i < nprops; i++)
207 new_propval[i] = o->propval[i + 1];
208 } else
209 new_propval = 0;
210
211 if (o->propval)
212 myfree(o->propval, M_PVAL);
213 o->propval = new_propval;
214 }
215
216 static void
remove_prop_recursively(Objid root,int root_pos)217 remove_prop_recursively(Objid root, int root_pos)
218 {
219 Objid c;
220
221 remove_prop(root, root_pos);
222 for (c = dbpriv_find_object(root)->child;
223 c != NOTHING;
224 c = dbpriv_find_object(c)->sibling) {
225 int new_prop_count = dbpriv_find_object(c)->propdefs.cur_length;
226
227 remove_prop_recursively(c, new_prop_count + root_pos);
228 }
229 }
230
231 int
db_delete_propdef(Objid oid,const char * pname)232 db_delete_propdef(Objid oid, const char *pname)
233 {
234 Proplist *props = &dbpriv_find_object(oid)->propdefs;
235 int hash = str_hash(pname);
236 int count = props->cur_length;
237 int max = props->max_length;
238 int i, j;
239
240 for (i = 0; i < count; i++) {
241 Propdef p;
242
243 p = props->l[i];
244 if (p.hash == hash && !mystrcasecmp(p.name, pname)) {
245 if (p.name)
246 free_str(p.name);
247
248 if (max > 8 && props->cur_length <= ((max * 3) / 8)) {
249 int new_size = max / 2;
250 Propdef *new_props;
251
252 new_props = mymalloc(new_size * sizeof(Propdef), M_PROPDEF);
253
254 for (j = 0; j < i; j++)
255 new_props[j] = props->l[j];
256 for (j = i + 1; j < count; j++)
257 new_props[j - 1] = props->l[j];
258
259 myfree(props->l, M_PROPDEF);
260 props->l = new_props;
261 props->max_length = new_size;
262 } else
263 for (j = i + 1; j < count; j++)
264 props->l[j - 1] = props->l[j];
265
266 props->cur_length--;
267 remove_prop_recursively(oid, i);
268
269 return 1;
270 }
271 }
272
273 return 0;
274 }
275
276 int
db_count_propdefs(Objid oid)277 db_count_propdefs(Objid oid)
278 {
279 return dbpriv_find_object(oid)->propdefs.cur_length;
280 }
281
282 int
db_for_all_propdefs(Objid oid,int (* func)(void *,const char *),void * data)283 db_for_all_propdefs(Objid oid, int (*func) (void *, const char *), void *data)
284 {
285 int i;
286 Object *o = dbpriv_find_object(oid);
287 int len = o->propdefs.cur_length;
288
289 for (i = 0; i < len; i++)
290 if (func(data, o->propdefs.l[i].name))
291 return 1;
292
293 return 0;
294 }
295
296 struct contents_data {
297 Var r;
298 int i;
299 };
300
301 static int
add_to_list(void * data,Objid c)302 add_to_list(void *data, Objid c)
303 {
304 struct contents_data *d = data;
305
306 d->i++;
307 d->r.v.list[d->i].type = TYPE_OBJ;
308 d->r.v.list[d->i].v.obj = c;
309
310 return 0;
311 }
312
313 static void
get_bi_value(db_prop_handle h,Var * value)314 get_bi_value(db_prop_handle h, Var * value)
315 {
316 Objid oid = *((Objid *) h.ptr);
317
318 switch (h.built_in) {
319 case BP_NAME:
320 value->type = TYPE_STR;
321 value->v.str = str_ref(db_object_name(oid));
322 break;
323 case BP_OWNER:
324 value->type = TYPE_OBJ;
325 value->v.obj = db_object_owner(oid);
326 break;
327 case BP_PROGRAMMER:
328 value->type = TYPE_INT;
329 value->v.num = db_object_has_flag(oid, FLAG_PROGRAMMER);
330 break;
331 case BP_WIZARD:
332 value->type = TYPE_INT;
333 value->v.num = db_object_has_flag(oid, FLAG_WIZARD);
334 break;
335 case BP_R:
336 value->type = TYPE_INT;
337 value->v.num = db_object_has_flag(oid, FLAG_READ);
338 break;
339 case BP_W:
340 value->type = TYPE_INT;
341 value->v.num = db_object_has_flag(oid, FLAG_WRITE);
342 break;
343 case BP_F:
344 value->type = TYPE_INT;
345 value->v.num = db_object_has_flag(oid, FLAG_FERTILE);
346 break;
347 case BP_LOCATION:
348 value->type = TYPE_OBJ;
349 value->v.obj = db_object_location(oid);
350 break;
351 case BP_CONTENTS:
352 {
353 struct contents_data d;
354
355 d.r = new_list(db_count_contents(oid));
356 d.i = 0;
357 db_for_all_contents(oid, add_to_list, &d);
358
359 *value = d.r;
360 }
361 break;
362 default:
363 panic("Unknown built-in property in GET_BI_VALUE!");
364 }
365 }
366
367 db_prop_handle
db_find_property(Objid oid,const char * name,Var * value)368 db_find_property(Objid oid, const char *name, Var * value)
369 {
370 static struct {
371 const char *name;
372 enum bi_prop prop;
373 int hash;
374 } ptable[] = {
375 {
376 "name", BP_NAME, 0
377 },
378 {
379 "owner", BP_OWNER, 0
380 },
381 {
382 "programmer", BP_PROGRAMMER, 0
383 },
384 {
385 "wizard", BP_WIZARD, 0
386 },
387 {
388 "r", BP_R, 0
389 },
390 {
391 "w", BP_W, 0
392 },
393 {
394 "f", BP_F, 0
395 },
396 {
397 "location", BP_LOCATION, 0
398 },
399 {
400 "contents", BP_CONTENTS, 0
401 }
402 };
403 static int ptable_init = 0;
404 int i, n;
405 db_prop_handle h;
406 int hash = str_hash(name);
407 Object *o;
408
409 if (!ptable_init) {
410 for (i = 0; i < Arraysize(ptable); i++)
411 ptable[i].hash = str_hash(ptable[i].name);
412 ptable_init = 1;
413 }
414 for (i = 0; i < Arraysize(ptable); i++) {
415 if (ptable[i].hash == hash && !mystrcasecmp(name, ptable[i].name)) {
416 static Objid ret;
417
418 ret = oid;
419 h.built_in = ptable[i].prop;
420 h.definer = NOTHING;
421 h.ptr = &ret;
422 if (value)
423 get_bi_value(h, value);
424 return h;
425 }
426 }
427
428 h.built_in = BP_NONE;
429 n = 0;
430 for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent)) {
431 Proplist *props = &(o->propdefs);
432 Propdef *defs = props->l;
433 int length = props->cur_length;
434
435 for (i = 0; i < length; i++, n++) {
436 if (defs[i].hash == hash
437 && !mystrcasecmp(defs[i].name, name)) {
438 Pval *prop;
439
440 h.definer = o->id;
441 o = dbpriv_find_object(oid);
442 prop = h.ptr = o->propval + n;
443
444 if (value) {
445 while (prop->var.type == TYPE_CLEAR) {
446 n -= o->propdefs.cur_length;
447 o = dbpriv_find_object(o->parent);
448 prop = o->propval + n;
449 }
450 *value = prop->var;
451 }
452 return h;
453 }
454 }
455 }
456
457 h.ptr = 0;
458 return h;
459 }
460
461 Var
db_property_value(db_prop_handle h)462 db_property_value(db_prop_handle h)
463 {
464 Var value;
465
466 if (h.built_in)
467 get_bi_value(h, &value);
468 else {
469 Pval *prop = h.ptr;
470
471 value = prop->var;
472 }
473
474 return value;
475 }
476
477 void
db_set_property_value(db_prop_handle h,Var value)478 db_set_property_value(db_prop_handle h, Var value)
479 {
480 if (!h.built_in) {
481 Pval *prop = h.ptr;
482
483 free_var(prop->var);
484 prop->var = value;
485 } else {
486 Objid oid = *((Objid *) h.ptr);
487 db_object_flag flag;
488
489 switch (h.built_in) {
490 case BP_NAME:
491 if (value.type != TYPE_STR)
492 goto complain;
493 db_set_object_name(oid, value.v.str);
494 break;
495 case BP_OWNER:
496 if (value.type != TYPE_OBJ)
497 goto complain;
498 db_set_object_owner(oid, value.v.obj);
499 break;
500 case BP_PROGRAMMER:
501 flag = FLAG_PROGRAMMER;
502 goto finish_flag;
503 case BP_WIZARD:
504 flag = FLAG_WIZARD;
505 goto finish_flag;
506 case BP_R:
507 flag = FLAG_READ;
508 goto finish_flag;
509 case BP_W:
510 flag = FLAG_WRITE;
511 goto finish_flag;
512 case BP_F:
513 flag = FLAG_FERTILE;
514 finish_flag:
515 if (is_true(value))
516 db_set_object_flag(oid, flag);
517 else
518 db_clear_object_flag(oid, flag);
519 free_var(value);
520 break;
521 case BP_LOCATION:
522 case BP_CONTENTS:
523 complain:
524 panic("Inappropriate value in DB_SET_PROPERTY_VALUE!");
525 break;
526 default:
527 panic("Unknown built-in property in DB_SET_PROPERTY_VALUE!");
528 }
529 }
530 }
531
532 Objid
db_property_owner(db_prop_handle h)533 db_property_owner(db_prop_handle h)
534 {
535 if (h.built_in) {
536 panic("Built-in property in DB_PROPERTY_OWNER!");
537 return NOTHING;
538 } else {
539 Pval *prop = h.ptr;
540
541 return prop->owner;
542 }
543 }
544
545 void
db_set_property_owner(db_prop_handle h,Objid oid)546 db_set_property_owner(db_prop_handle h, Objid oid)
547 {
548 if (h.built_in)
549 panic("Built-in property in DB_SET_PROPERTY_OWNER!");
550 else {
551 Pval *prop = h.ptr;
552
553 prop->owner = oid;
554 }
555 }
556
557 unsigned
db_property_flags(db_prop_handle h)558 db_property_flags(db_prop_handle h)
559 {
560 if (h.built_in) {
561 panic("Built-in property in DB_PROPERTY_FLAGS!");
562 return 0;
563 } else {
564 Pval *prop = h.ptr;
565
566 return prop->perms;
567 }
568 }
569
570 void
db_set_property_flags(db_prop_handle h,unsigned flags)571 db_set_property_flags(db_prop_handle h, unsigned flags)
572 {
573 if (h.built_in)
574 panic("Built-in property in DB_SET_PROPERTY_FLAGS!");
575 else {
576 Pval *prop = h.ptr;
577
578 prop->perms = flags;
579 }
580 }
581
582 int
db_property_allows(db_prop_handle h,Objid progr,db_prop_flag flag)583 db_property_allows(db_prop_handle h, Objid progr, db_prop_flag flag)
584 {
585 return ((db_property_flags(h) & flag)
586 || progr == db_property_owner(h)
587 || is_wizard(progr));
588 }
589
590 static void
fix_props(Objid oid,int parent_local,int old,int new,int common)591 fix_props(Objid oid, int parent_local, int old, int new, int common)
592 {
593 Object *me = dbpriv_find_object(oid);
594 Object *parent = dbpriv_find_object(me->parent);
595 Pval *new_propval;
596 int local = parent_local;
597 int i;
598 Objid c;
599
600 local += me->propdefs.cur_length;
601
602 for (i = local; i < local + old; i++)
603 free_var(me->propval[i].var);
604
605 if (local + new + common != 0) {
606 new_propval = mymalloc((local + new + common) * sizeof(Pval), M_PVAL);
607 for (i = 0; i < local; i++)
608 new_propval[i] = me->propval[i];
609 for (i = 0; i < new; i++) {
610 Pval pv;
611
612 pv = parent->propval[parent_local + i];
613 new_propval[local + i] = pv;
614 new_propval[local + i].var.type = TYPE_CLEAR;
615 if (pv.perms & PF_CHOWN)
616 new_propval[local + i].owner = me->owner;
617 }
618 for (i = 0; i < common; i++)
619 new_propval[local + new + i] = me->propval[local + old + i];
620 } else
621 new_propval = 0;
622
623 if (me->propval)
624 myfree(me->propval, M_PVAL);
625 me->propval = new_propval;
626
627 for (c = me->child; c != NOTHING; c = dbpriv_find_object(c)->sibling)
628 fix_props(c, local, old, new, common);
629 }
630
631 int
dbpriv_check_properties_for_chparent(Objid oid,Objid new_parent)632 dbpriv_check_properties_for_chparent(Objid oid, Objid new_parent)
633 {
634 Object *o;
635 int i;
636
637 for (o = dbpriv_find_object(new_parent);
638 o;
639 o = dbpriv_find_object(o->parent)) {
640 Proplist *props = &o->propdefs;
641
642 for (i = 0; i < props->cur_length; i++)
643 if (property_defined_at_or_below(props->l[i].name,
644 props->l[i].hash,
645 oid))
646 return 0;
647 }
648
649 return 1;
650 }
651
652 void
dbpriv_fix_properties_after_chparent(Objid oid,Objid old_parent)653 dbpriv_fix_properties_after_chparent(Objid oid, Objid old_parent)
654 {
655 Objid o1, o2, common, new_parent;
656 int common_props, old_props, new_props;
657
658 /* Find the nearest common ancestor between old & new parent */
659 new_parent = db_object_parent(oid);
660 common = NOTHING;
661 for (o1 = new_parent; o1 != NOTHING; o1 = db_object_parent(o1))
662 for (o2 = old_parent; o2 != NOTHING; o2 = db_object_parent(o2))
663 if (o1 == o2) {
664 common = o1;
665 goto endouter;
666 }
667 endouter:
668
669 if (common != NOTHING)
670 common_props = dbpriv_count_properties(common);
671 else
672 common_props = 0;
673
674 old_props = dbpriv_count_properties(old_parent) - common_props;
675 new_props = dbpriv_count_properties(new_parent) - common_props;
676
677 fix_props(oid, 0, old_props, new_props, common_props);
678 }
679
680 char rcsid_db_properties[] = "$Id: db_properties.c,v 1.3 1998/12/14 13:17:38 nop Exp $";
681
682 /*
683 * $Log: db_properties.c,v $
684 * Revision 1.3 1998/12/14 13:17:38 nop
685 * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
686 *
687 * Revision 1.2 1997/03/03 04:18:31 nop
688 * GNU Indent normalization
689 *
690 * Revision 1.1.1.1 1997/03/03 03:44:59 nop
691 * LambdaMOO 1.8.0p5
692 *
693 * Revision 2.6 1996/04/08 01:08:32 pavel
694 * Fixed `db_rename_propdef()' to allow case-only changes. Release 1.8.0p3.
695 *
696 * Revision 2.5 1996/02/11 00:46:48 pavel
697 * Enhanced db_find_property() to report the defining object of the found
698 * property. Release 1.8.0beta2.
699 *
700 * Revision 2.4 1996/02/08 07:18:02 pavel
701 * Renamed TYPE_NUM to TYPE_INT. Updated copyright notice for 1996.
702 * Release 1.8.0beta1.
703 *
704 * Revision 2.3 1995/12/31 03:27:40 pavel
705 * Removed a few more uses of `unsigned'. Reordered things in
706 * db_delete_propdef() to fix an occasional memory smash.
707 * Release 1.8.0alpha4.
708 *
709 * Revision 2.2 1995/12/28 00:41:34 pavel
710 * Made *all* built-in property references return fresh value references.
711 * Release 1.8.0alpha3.
712 *
713 * Revision 2.1 1995/12/11 07:52:27 pavel
714 * Added support for renaming propdefs.
715 *
716 * Release 1.8.0alpha2.
717 *
718 * Revision 2.0 1995/11/30 04:21:13 pavel
719 * New baseline version, corresponding to release 1.8.0alpha1.
720 *
721 * Revision 1.1 1995/11/30 04:21:02 pavel
722 * Initial revision
723 */
724