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