1/* BEGIN LICENSE BLOCK
2 * Version: CMPL 1.1
3 *
4 * The contents of this file are subject to the Cisco-style Mozilla Public
5 * License Version 1.1 (the "License"); you may not use this file except
6 * in compliance with the License.  You may obtain a copy of the License
7 * at www.eclipse-clp.org/license.
8 *
9 * Software distributed under the License is distributed on an "AS IS"
10 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
11 * the License for the specific language governing rights and limitations
12 * under the License.
13 *
14 * The Original Code is  The ECLiPSe Constraint Logic Programming System.
15 * The Initial Developer of the Original Code is  Cisco Systems, Inc.
16 * Portions created by the Initial Developer are
17 * Copyright (C) 1989-2007 Cisco Systems, Inc.  All Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * END LICENSE BLOCK */
22
23/*
24 * VERSION	$Id: bip_record.c,v 1.3 2012/02/12 02:16:13 jschimpf Exp $
25 */
26
27/* ********************************************************************
28 *
29 *	ECLiPSe built-ins for the indexed database
30 *
31 ******************************************************************** */
32
33#include	"config.h"
34#include        "sepia.h"
35#include        "types.h"
36#include        "embed.h"
37#include        "mem.h"
38#include        "error.h"
39#include	"dict.h"
40#include	"property.h"
41#include	"module.h"
42
43
44#include <stdio.h>	/* for sprintf() */
45
46
47static dident	d_visible_;
48
49
50
51/*----------------------------------------------------------------------
52 * Recorded database primitives
53 *
54 * Data structure is a circular doubly linked list with one dummy
55 * element as header. The header is referred to by the IDB_PROP
56 * property (but could also be passed around as a handle of type
57 * heap_rec_header_tid).
58 *
59 * Individual recorded iterms are identified by their list element
60 * and handles of type heap_rec_tid are used as "db references".
61 * They are always created as part of a record-list, but can continue
62 * to exist independently when their db-reference was obtained and
63 * they were subsequently erased from the list.
64 *----------------------------------------------------------------------*/
65
66
67/* INSTANCE TYPE DECLARATION */
68
69typedef struct record_elem {
70    uword		ref_ctr;	/* one count for list membership */
71    struct record_elem	*next, *prev;	/* NULL if not in list */
72    uword		hash;
73    pword		term;		/* TEND for header cell */
74} t_heap_rec;
75
76
77/* METHODS */
78
79
80/* Allocation of both header and proper elements */
81
82static t_heap_rec *
83_rec_create(void)
84{
85    t_heap_rec *obj = (t_heap_rec *) hg_alloc_size(sizeof(t_heap_rec));
86    obj->ref_ctr = 1;
87    obj->next = obj->prev = obj;
88    obj->term.val.nint = 0;
89    obj->term.tag.kernel = TEND;	/* remains TEND for header cells */
90    return obj;
91}
92
93
94t_ext_ptr
95ec_record_create(void)
96{
97    return (t_ext_ptr) _rec_create();
98}
99
100
101/* Lose a reference to an element */
102
103static void
104_rec_free_elem(t_heap_rec *this)
105{
106    if (--this->ref_ctr <= 0)
107    {
108	if (this->term.tag.kernel == TEND)
109	    ec_panic("Trying to free record list header", "_rec_free_elem()");
110
111#ifdef DEBUG_RECORDS
112	p_fprintf(current_err_, "\n_rec_free_elem(0x%x)", this);
113	ec_flush(current_err_);
114#endif
115	free_heapterm(&this->term);
116	hg_free_size((generic_ptr) this, sizeof(t_heap_rec));
117    }
118}
119
120
121/* Remove and lose all elements from header's list (but note that the
122 * elements may survive if db-references to them still exist) */
123
124static void
125_rec_free_elems(t_heap_rec *header)
126{
127    t_heap_rec *this = header->next;
128    if (header->term.tag.kernel != TEND)
129	ec_panic("Not a record list header", "_rec_free_all()");
130
131    while (this != header)
132    {
133	t_heap_rec *next = this->next;
134	this->prev = this->next = 0;
135	_rec_free_elem(this);
136	this = next;
137    }
138    header->next = header->prev = header;
139}
140
141
142/* Lose a reference to the whole list identified by header */
143
144static void
145_rec_free_all(t_heap_rec *header)
146{
147    if (--header->ref_ctr <= 0)
148    {
149#ifdef DEBUG_RECORDS
150	p_fprintf(current_err_, "\n_rec_free_all(0x%x)", header);
151	ec_flush(current_err_);
152#endif
153	_rec_free_elems(header);
154	hg_free_size((generic_ptr) header, sizeof(t_heap_rec));
155    }
156}
157
158
159static t_heap_rec *
160_rec_copy_elem(t_heap_rec *this)	/* this != NULL */
161{
162    ++this->ref_ctr;
163    return this;
164}
165
166
167static void
168_rec_mark_elem(t_heap_rec *this)	/* this != NULL */
169{
170    mark_dids_from_heapterm(&this->term);
171}
172
173
174static void
175_rec_mark_all(t_heap_rec *header)	/* header != NULL */
176{
177    t_heap_rec *this = header->next;
178    if (header->term.tag.kernel != TEND)
179	ec_panic("Not a record list header", "_rec_mark_all()");
180    while (this != header)
181    {
182	_rec_mark_elem(this);
183	this = this->next;
184    }
185}
186
187static int
188_rec_tostr_elem(t_heap_rec *obj, char *buf, int quoted)	/* obj != NULL */
189{
190#define STRSZ_DBREF 20
191    sprintf(buf, "'DBREF'(16'%08x)", obj);
192    return STRSZ_DBREF;
193}
194
195static int
196_rec_strsz_elem(t_heap_rec *obj, int quoted) /* obj != NULL */
197{
198    return STRSZ_DBREF;
199}
200
201
202static int
203_rec_tostr_all(t_heap_rec *obj, char *buf, int quoted) /* obj != NULL */
204{
205#define STRSZ_REC 18
206    sprintf(buf, "'REC'(16'%08x)", obj);
207    return STRSZ_REC;
208}
209
210static int
211_rec_strsz_all(t_heap_rec *obj, int quoted) /* obj != NULL */
212{
213    return STRSZ_REC;
214}
215
216
217
218/* CLASS DESCRIPTOR (method table) */
219t_ext_type heap_rec_tid = {
220    (void (*)(t_ext_ptr)) _rec_free_elem,
221    (t_ext_ptr (*)(t_ext_ptr)) _rec_copy_elem,
222    (void (*)(t_ext_ptr)) _rec_mark_elem,
223    (int (*)(t_ext_ptr,int)) _rec_strsz_elem,
224    (int (*)(t_ext_ptr,char*,int)) _rec_tostr_elem,
225    0,	/* equal */
226    (t_ext_ptr (*)(t_ext_ptr)) _rec_copy_elem,
227    0,	/* get */
228    0	/* set */
229};
230
231t_ext_type heap_rec_header_tid = {
232    (void (*)(t_ext_ptr)) _rec_free_all,
233    (t_ext_ptr (*)(t_ext_ptr)) _rec_copy_elem,
234    (void (*)(t_ext_ptr)) _rec_mark_all,
235    (int (*)(t_ext_ptr,int)) _rec_strsz_all,
236    (int (*)(t_ext_ptr,char*,int)) _rec_tostr_all,
237    0,	/* equal */
238    (t_ext_ptr (*)(t_ext_ptr)) _rec_copy_elem,
239    0,	/* get */
240    0	/* set */
241};
242
243
244/*----------------------------------------------------------------------
245 * PROLOG INTERFACE
246 *----------------------------------------------------------------------*/
247
248
249/* get the record header from either the functor key or a handle */
250
251static int
252_get_rec_list(value vrec, type trec, value vmod, type tmod, t_heap_rec **pheader)
253{
254    if (SameTypeC(trec, THANDLE))
255    {
256	Get_Typed_Object(vrec, trec, &heap_rec_header_tid, *pheader);
257    }
258    else
259    {
260	dident key_did;
261	pword *prop;
262	int err;
263	Get_Key_Did(key_did,vrec,trec)
264	prop = get_modular_property(key_did, IDB_PROP, vmod.did, tmod, VISIBLE_PROP, &err);
265	if (!prop)
266	    return err == PERROR ? NO_LOCAL_REC : err;
267	*pheader = (t_heap_rec *) prop->val.ptr;
268        if (!IsTag(prop->tag.kernel,TPTR) || !IsTag((*pheader)->term.tag.kernel,TEND))
269	    ec_panic("Not a valid record-property", "_get_rec_list()");
270    }
271    return PSUCCEED;
272}
273
274
275/*
276 * is_record(Key)@Module checks whether Key is a record key (or handle)
277 * on which recorded terms have been (and still are) stored.
278 */
279
280static int
281p_is_record_body(value vrec, type trec, value vmod, type tmod)
282{
283    t_heap_rec *header;
284    int		err;
285
286    a_mutex_lock(&PropertyLock);
287    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
288    if (err == NO_LOCAL_REC || err == STALE_HANDLE)
289	err = PFAIL;
290    else if (err == PSUCCEED && header->next == header)
291	err = PFAIL;
292    a_mutex_unlock(&PropertyLock);
293    return err;
294}
295
296
297/* record_create(-Handle) creates an anonymous record */
298
299static int
300p_record_create(value vrec, type trec)
301{
302    pword rec;
303    Check_Ref(trec);
304    rec = ec_handle(&heap_rec_header_tid, (t_ext_ptr) _rec_create());
305    Return_Unify_Pw(vrec, trec, rec.val, rec.tag);
306}
307
308
309static int
310p_local_record_body(value vkey, type tkey, value vmod, type tmod)
311{
312    pword	*prop, *p;
313    dident	key_did;
314    int		err;
315
316    Get_Functor_Did(vkey, tkey, key_did);
317
318    a_mutex_lock(&PropertyLock);
319
320    prop = set_modular_property(key_did, IDB_PROP, vmod.did, tmod,
321				LOCAL_PROP, &err);
322    if (!prop)
323    {
324	a_mutex_unlock(&PropertyLock);
325	if (err == PERROR)
326	    { Succeed_; }	/* exists already */
327	else
328	    Bip_Error(err);
329    }
330    prop->val.wptr = (uword *) _rec_create();
331    prop->tag.kernel = TPTR;
332    a_mutex_unlock(&PropertyLock);
333    Succeed_;
334}
335
336
337static int
338p_global_record_body(value vkey, type tkey, value vmod, type tmod)
339{
340    pword	*prop, *p;
341    dident	key_did;
342    int		err;
343
344    Get_Functor_Did(vkey, tkey, key_did);
345
346    a_mutex_lock(&PropertyLock);
347
348    prop = set_modular_property(key_did, IDB_PROP, vmod.did, tmod,
349				GLOBAL_PROP, &err);
350    if (!prop)
351    {
352	a_mutex_unlock(&PropertyLock);
353	Bip_Error((err == PERROR) ? LOCAL_REC : err);
354    }
355    prop->val.wptr = (uword *) _rec_create();
356    prop->tag.kernel = TPTR;
357    a_mutex_unlock(&PropertyLock);
358    Succeed_;
359}
360
361
362static int
363p_abolish_record_body(value vkey, type tkey, value vmod, type tmod)
364{
365    dident	key_did;
366    int		err;
367
368    if (IsHandle(tkey))
369    {
370	return p_handle_free(vkey, tkey);
371    }
372    else
373    {
374	Get_Functor_Did(vkey, tkey, key_did);
375
376	err = erase_modular_property(key_did, IDB_PROP, vmod.did,tmod, LOCAL_PROP);
377
378	if (err < 0)
379	{
380	    Bip_Error((err == PERROR) ? NO_LOCAL_REC : err);
381	}
382	else
383	    Succeed_;
384    }
385}
386
387
388/* record[az](+Key, ?Term)@Module */
389
390static int
391p_recorda_body(value vrec, type trec, value vterm, type tterm, value vmod, type tmod)
392{
393    t_heap_rec *obj, *header;
394    pword copy_pw;
395    int err = PSUCCEED;
396
397    if ((err = create_heapterm(&copy_pw, vterm, tterm)) != PSUCCEED)
398	{ Bip_Error(err); }
399
400    a_mutex_lock(&PropertyLock);
401    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
402    if (err != PSUCCEED) goto _unlock_return_err_;
403
404    obj = _rec_create();
405    move_heapterm(&copy_pw, &obj->term);
406    obj->next = header->next;
407    obj->prev = header;
408    header->next->prev = obj;
409    header->next = obj;
410
411_unlock_return_err_:
412    a_mutex_unlock(&PropertyLock);
413    return err;
414}
415
416static int
417p_recordz_body(value vrec, type trec, value vterm, type tterm, value vmod, type tmod)
418{
419    t_heap_rec *obj, *header;
420    pword copy_pw;
421    int err = PSUCCEED;
422
423    if ((err = create_heapterm(&copy_pw, vterm, tterm)) != PSUCCEED)
424	{ Bip_Error(err); }
425
426    a_mutex_lock(&PropertyLock);
427    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
428    if (err != PSUCCEED) goto _unlock_return_err_;
429
430    obj = _rec_create();
431    move_heapterm(&copy_pw, &obj->term);
432    obj->next = header;
433    obj->prev = header->prev;
434    header->prev->next = obj;
435    header->prev = obj;
436
437_unlock_return_err_:
438    a_mutex_unlock(&PropertyLock);
439    return err;
440}
441
442
443/* record[az](+Key, ?Term, -DbRef)@Module */
444
445static int
446p_recorda3_body(value vrec, type trec, value vterm, type tterm, value vdref, type tdref, value vmod, type tmod)
447{
448    t_heap_rec *obj, *header;
449    pword copy_pw, ref_pw;
450    int err = PSUCCEED;
451
452    if ((err = create_heapterm(&copy_pw, vterm, tterm)) != PSUCCEED)
453	{ Bip_Error(err); }
454
455    a_mutex_lock(&PropertyLock);
456    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
457    if (err != PSUCCEED) goto _unlock_return_err_;
458
459    obj = _rec_create();
460    move_heapterm(&copy_pw, &obj->term);
461    obj->next = header->next;
462    obj->prev = header;
463    header->next->prev = obj;
464    header->next = obj;
465    obj = _rec_copy_elem(obj);
466    a_mutex_unlock(&PropertyLock);
467    ref_pw = ec_handle(&heap_rec_tid, (t_ext_ptr) obj);
468    Return_Unify_Pw(vdref, tdref, ref_pw.val, ref_pw.tag);
469
470_unlock_return_err_:
471    a_mutex_unlock(&PropertyLock);
472    return err;
473}
474
475static int
476p_recordz3_body(value vrec, type trec, value vterm, type tterm, value vdref, type tdref, value vmod, type tmod)
477{
478    t_heap_rec *obj, *header;
479    pword copy_pw, ref_pw;
480    int err = PSUCCEED;
481
482    if ((err = create_heapterm(&copy_pw, vterm, tterm)) != PSUCCEED)
483	{ Bip_Error(err); }
484
485    a_mutex_lock(&PropertyLock);
486    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
487    if (err != PSUCCEED) goto _unlock_return_err_;
488
489    obj = _rec_create();
490    move_heapterm(&copy_pw, &obj->term);
491    obj->next = header;
492    obj->prev = header->prev;
493    header->prev->next = obj;
494    header->prev = obj;
495    obj = _rec_copy_elem(obj);
496    a_mutex_unlock(&PropertyLock);
497    ref_pw = ec_handle(&heap_rec_tid, (t_ext_ptr) obj);
498    Return_Unify_Pw(vdref, tdref, ref_pw.val, ref_pw.tag);
499
500_unlock_return_err_:
501    a_mutex_unlock(&PropertyLock);
502    return err;
503}
504
505
506/* filter for recorded terms: simple tests to reduce the recorded terms
507   returned to the ECLiPSe level, that need to be unified with the filter
508   term. The filter test performs simple comparison on the arguments of the
509   first argument of a recorded term against the filter, This is designed to
510   speed up the matching of dynamic predicates, which are recorded as (H :- B),
511   so that `filtering' is performed on the head H and its arguments.
512*/
513static int
514_may_match_filter(value vfilter, uword tfilter, value vterm, type tterm)
515{
516    pword *farg;	/* pointer into filter term */
517    pword *targ;	/* pointer into recorded term */
518    int i;
519
520    /* toplevel term */
521    if (ISRef(tfilter) || IsRef(tterm))
522        return 1;
523    if (DifferTypeC(tterm,tfilter))
524        return 0;
525    if (ISSimple(tfilter))
526        return SimpleEq(tfilter,vfilter,vterm);
527    if (IsTag(tfilter,TCOMP))
528    {
529        farg = vfilter.ptr;
530        targ = vterm.ptr;
531	if (farg->val.did != targ->val.did)
532	    return 0;
533        ++farg;
534        ++targ;
535    }
536    else if (IsTag(tfilter,TLIST))
537    {
538        farg = vfilter.ptr;
539        targ = vterm.ptr;
540    }
541    else
542	return 1;       /* in case of doubt, succeed (don't filter) */
543
544    /* first argument (the head in case of a head:-body term */
545    if (IsRef(farg->tag) || IsRef(targ->tag))
546        return 1;
547    if (DifferType(farg->tag, targ->tag))
548        return 0;
549    if (IsSimple(farg->tag))
550        return SimpleEq(farg->tag.kernel,farg->val,targ->val);
551    switch (TagType(farg->tag))
552    {
553    case TCOMP:
554	targ = targ->val.ptr;
555	farg = farg->val.ptr;
556	i = DidArity(farg->val.did);
557	if (farg->val.did != targ->val.did) return 0;
558    _check_rec_args:
559	do
560	{
561	    pword *f = ++farg;
562	    ++targ;
563	    Dereference_(f);
564	    if (IsRef(f->tag) || IsRef(targ->tag))
565	    	continue;
566	    if (DifferType(f->tag, targ->tag))
567	    	return 0;
568            if (IsSimple(f->tag))
569            {
570                if (!SimpleEq(f->tag.kernel,f->val,targ->val))
571		    return 0;
572            }
573            else if (IsTag(f->tag.kernel,TCOMP))
574            {
575		if (f->val.ptr->val.did != targ->val.ptr->val.did)
576		    return 0;
577            }
578	} while (--i > 0);
579	break;
580    case TLIST:
581	targ = targ->val.ptr-1;
582	farg = farg->val.ptr-1;
583	i = 2;
584	goto _check_rec_args;
585    }
586    return 1;
587}
588
589
590/* recorded_list(+Key, -Terms)@Module */
591
592static int
593p_recorded_list_body(value vrec, type trec, value vl, type tl, value vmod, type tmod)
594{
595    t_heap_rec *header, *obj;
596    int err;
597
598    Check_Output_List(tl);
599    a_mutex_lock(&PropertyLock);
600    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
601    if (err == PSUCCEED)
602    {
603	pword list;
604	pword *tail = &list;
605	for (obj = header->next; obj != header; obj = obj->next)
606	{
607	    pword *car = TG;
608	    Make_List(tail, car);
609	    Push_List_Frame();
610	    get_heapterm(&obj->term, car);
611	    tail = car+1;
612	}
613	Make_Nil(tail);
614	a_mutex_unlock(&PropertyLock);
615	Return_Unify_Pw(vl, tl, list.val, list.tag);
616    }
617    else if (err == NO_LOCAL_REC)
618    {
619	a_mutex_unlock(&PropertyLock);
620	Return_Unify_Nil(vl, tl);
621    }
622    else
623    {
624	a_mutex_unlock(&PropertyLock);
625	Bip_Error(err);
626    }
627}
628
629
630/* recorded_refs(+Key, ?Filter, -Refs)@Module */
631
632static int
633p_recorded_refs_body(value vrec, type trec, value vfilter, type tfilter, value vl, type tl, value vmod, type tmod)
634{
635    t_heap_rec *header, *obj;
636    int err;
637
638    Check_Output_List(tl);
639    a_mutex_lock(&PropertyLock);
640    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
641    if (err == PSUCCEED)
642    {
643	pword list;
644	pword *tail = &list;
645	for (obj = header->next; obj != header; obj = obj->next)
646	{
647	    if (ISRef(tfilter.kernel) ||
648		_may_match_filter(vfilter, tfilter.kernel, obj->term.val, obj->term.tag))
649	    {
650		pword *car = TG;
651		Make_List(tail, car);
652		Push_List_Frame();
653		*car = ec_handle(&heap_rec_tid, (t_ext_ptr) _rec_copy_elem(obj));
654		tail = car+1;
655	    }
656	}
657	Make_Nil(tail);
658	a_mutex_unlock(&PropertyLock);
659	Return_Unify_Pw(vl, tl, list.val, list.tag);
660    }
661    else if (err == NO_LOCAL_REC)
662    {
663	a_mutex_unlock(&PropertyLock);
664	Return_Unify_Nil(vl, tl);
665    }
666    else
667    {
668	a_mutex_unlock(&PropertyLock);
669	Bip_Error(err);
670    }
671}
672
673
674/* erase_all(+Key)@Module */
675
676static int
677p_erase_all_body(value vrec, type trec, value vmod, type tmod)
678{
679    t_heap_rec *header;
680    int err;
681
682    a_mutex_lock(&PropertyLock);
683    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
684    if (err == PSUCCEED)
685    {
686	_rec_free_elems(header);
687    }
688    else if (err == NO_LOCAL_REC)
689    {
690	err = PSUCCEED;
691    }
692    a_mutex_unlock(&PropertyLock);
693    Bip_Error(err);
694}
695
696
697/* referenced_record(+DbRef, -Term) */
698
699static int
700p_referenced_record(value vrec, type trec, value vl, type tl)
701{
702    t_heap_rec *obj;
703    pword result;
704
705    Get_Typed_Object(vrec, trec, &heap_rec_tid, obj);
706    get_heapterm(&obj->term, &result);
707    if (IsRef(result.tag) && result.val.ptr == &result)
708    {
709	Succeed_;
710    }
711    Return_Unify_Pw(vl, tl, result.val, result.tag);
712}
713
714
715/* erase(+DbRef) */
716
717static int
718p_erase(value vrec, type trec)
719{
720    t_heap_rec *obj;
721    pword result;
722
723    Get_Typed_Object(vrec, trec, &heap_rec_tid, obj);
724    a_mutex_lock(&PropertyLock);
725    if (obj->next)
726    {
727	obj->next->prev = obj->prev;
728	obj->prev->next = obj->next;
729	obj->prev = obj->next = 0;
730	_rec_free_elem(obj);
731	a_mutex_unlock(&PropertyLock);
732	Succeed_;
733    }
734    else /* was already removed from record-list */
735    {
736	a_mutex_unlock(&PropertyLock);
737	Fail_;
738    }
739}
740
741
742/*
743 * Two internal predicates for stepping through the recorded list:
744 * first_recorded(+Key, ?Filter, -Ref)@Module is semidet
745 * next_recorded(+Ref, ?Filter, -Ref) is semidet
746 * These cannot be used for logical update semantics!
747 */
748
749static int
750p_first_recorded(value vrec, type trec, value vfilter, type tfilter, value vdref, type tdref, value vmod, type tmod)
751{
752    t_heap_rec *header, *obj;
753    pword ref_pw;
754    int err;
755
756    a_mutex_lock(&PropertyLock);
757    err = _get_rec_list(vrec, trec, vmod, tmod, &header);
758    if (err == PSUCCEED)
759    {
760	for(obj=header->next; obj != header; obj=obj->next)
761	{
762	    if (IsRef(tfilter) ||
763		_may_match_filter(vfilter, tfilter.kernel, obj->term.val, obj->term.tag))
764	    {
765		obj = _rec_copy_elem(obj);
766		a_mutex_unlock(&PropertyLock);
767		ref_pw = ec_handle(&heap_rec_tid, (t_ext_ptr) obj);
768		Return_Unify_Pw(vdref, tdref, ref_pw.val, ref_pw.tag);
769	    }
770	}
771    }
772    else if (err != NO_LOCAL_REC)
773    {
774	a_mutex_unlock(&PropertyLock);
775	Bip_Error(err);
776    }
777    a_mutex_unlock(&PropertyLock);
778    Fail_;
779}
780
781
782static int
783p_next_recorded(value vref1, type tref1, value vfilter, type tfilter, value vref2, type tref2)
784{
785    t_heap_rec *obj;
786    pword ref_pw;
787
788    Get_Typed_Object(vref1, tref1, &heap_rec_tid, obj);
789    a_mutex_lock(&PropertyLock);
790    for(;;)
791    {
792        obj = obj->next;
793        if (!obj || IsTag(obj->term.tag.kernel,TEND))
794        {
795            a_mutex_unlock(&PropertyLock);
796            Fail_;
797        }
798	if (IsRef(tfilter) ||
799	    _may_match_filter(vfilter, tfilter.kernel, obj->term.val, obj->term.tag))
800        {
801            break;
802        }
803    }
804    obj = _rec_copy_elem(obj);
805    a_mutex_unlock(&PropertyLock);
806    ref_pw = ec_handle(&heap_rec_tid, (t_ext_ptr) obj);
807    Return_Unify_Pw(vref2, tref2, ref_pw.val, ref_pw.tag);
808}
809
810
811/*----------------------------------------------------------------------
812 * Special purpose record meta_attribute/0 for storing attribute-name
813 * mapping.  We assume the record contains [Name|Index] pairs.
814 *----------------------------------------------------------------------*/
815
816static t_heap_rec	*rec_meta_attribute_;
817
818int
819meta_index(dident wd)
820{
821    t_heap_rec *this;
822
823    for (this = rec_meta_attribute_->next; this != rec_meta_attribute_; this = this->next)
824    {
825	if (this->term.val.ptr[0].val.did == wd)
826	    return this->term.val.ptr[1].val.nint;
827    }
828    return 0;
829}
830
831
832dident
833meta_name(int slot)
834{
835    t_heap_rec *this;
836
837    for (this = rec_meta_attribute_->next; this != rec_meta_attribute_; this = this->next)
838    {
839	if (this->term.val.ptr[1].val.nint == slot)
840	    return this->term.val.ptr[0].val.did;
841    }
842    return D_UNKNOWN;
843}
844
845
846/*----------------------------------------------------------------------
847 * the subsequent BIPs fail on error and set the global variable
848 *----------------------------------------------------------------------*/
849
850#undef Bip_Error
851#define Bip_Error(N) Bip_Error_Fail(N)
852
853/*
854 * Check if key is a valid key for records
855 */
856
857/* ARGSUSED */
858static int
859p_valid_key(value v, type t)
860{
861    Error_If_Ref(t);
862    if (IsAtom(t) || IsStructure(t) || IsNil(t) || IsList(t))
863	{ Succeed_; }
864    Check_Typed_Object_Handle(v, t, &heap_rec_header_tid);
865    Succeed_;
866}
867
868#undef Bip_Error
869#define Bip_Error(N) return(N);
870
871/*----------------------------------------------------------------------
872 * End of fail on error BIPs
873 *----------------------------------------------------------------------*/
874
875void
876bip_record_init(int flags)
877{
878    pri		*pd;
879    type	t;
880    value	v1, v2;
881    int		res;
882
883    d_visible_ = in_dict("visible", 0);
884
885    if (flags & INIT_SHARED)
886    {
887	(void) local_built_in(in_dict("valid_key", 1),
888				 p_valid_key, B_SAFE|U_SIMPLE);
889	(void) exported_built_in(in_dict("erase_all_body", 2),
890				 p_erase_all_body, B_UNSAFE);
891	(void) exported_built_in(in_dict("is_record_body", 2),
892				 p_is_record_body, B_UNSAFE);
893	(void) exported_built_in(in_dict("recorda_body", 3),
894				 p_recorda_body, B_UNSAFE);
895	(void) exported_built_in(in_dict("recordz_body", 3),
896			         p_recordz_body, B_UNSAFE);
897	(void) exported_built_in(in_dict("recorda_body", 4),
898				 p_recorda3_body, B_UNSAFE);
899	(void) exported_built_in(in_dict("recordz_body", 4),
900				 p_recordz3_body, B_UNSAFE);
901	exported_built_in(in_dict("recorded_list_body", 3),
902			p_recorded_list_body, B_UNSAFE|U_FRESH)
903	    -> mode = BoundArg(2, NONVAR);
904	exported_built_in(in_dict("recorded_refs_body", 4),
905			p_recorded_refs_body, B_UNSAFE|U_FRESH)
906	    -> mode = BoundArg(2, NONVAR);
907	exported_built_in(in_dict("referenced_record", 2),
908				 p_referenced_record, B_UNSAFE|U_FRESH)
909	    -> mode = BoundArg(2, NONVAR);
910	(void) exported_built_in(in_dict("erase", 1), p_erase, B_UNSAFE);
911	(void) exported_built_in(in_dict("record_create", 1),
912				 p_record_create, B_UNSAFE);
913	(void) exported_built_in(in_dict("local_record_body", 2),
914				 p_local_record_body, B_UNSAFE);
915	(void) local_built_in(in_dict("global_record_body", 2),
916				 p_global_record_body, B_UNSAFE);
917	(void) exported_built_in(in_dict("abolish_record_body", 2),
918				 p_abolish_record_body, B_UNSAFE);
919	(void) local_built_in(in_dict("first_recorded_", 4),
920				 p_first_recorded, B_UNSAFE);
921	(void) local_built_in(in_dict("next_recorded", 3),
922				 p_next_recorded, B_UNSAFE);
923    }
924
925    t.kernel = ModuleTag(d_.kernel_sepia);
926    v1.did = in_dict("meta_attribute", 0);
927    v2.did = d_.kernel_sepia;
928    if (flags & INIT_SHARED)
929    {
930	(void) p_local_record_body(v1, tdict, v2, t);
931    }
932    rec_meta_attribute_ = (t_heap_rec *) get_modular_property(v1.did,
933			IDB_PROP, v2.did, t, LOCAL_PROP, &res)->val.ptr;
934}
935
936
937