1 #include "apricot.h"
2 #include <ctype.h>
3 #include "Component.h"
4 #include "Application.h"
5 #include <Component.inc>
6 
7 #ifdef __cplusplus
8 extern "C" {
9 #endif
10 
11 
12 #undef  my
13 #define inherited CObject->
14 #define my  ((( PComponent) self)-> self)
15 #define var (( PComponent) self)
16 
17 typedef Bool ActionProc ( Handle self, Handle item, void * params);
18 typedef ActionProc *PActionProc;
19 
20 void
Component_init(Handle self,HV * profile)21 Component_init( Handle self, HV * profile)
22 {
23 	dPROFILE;
24 	SV * res;
25 	HV * hv;
26 	HE * he;
27 	inherited init( self, profile);
28 	if ( !my-> validate_owner( self, &var-> owner, profile)) {
29 		var-> stage = csDeadInInit;
30 		croak( "Illegal 'owner' reference passed to %s::%s%s", my-> className, "init",
31 				application ? "" : ". Probably you forgot to include 'use Prima::Application' in your code. Error");
32 	}
33 	if ( var-> owner)
34 		((( PComponent) var-> owner)-> self)-> attach( var-> owner, self);
35 	my-> set_name( self, pget_sv( name));
36 	my-> set_delegations( self, pget_sv( delegations));
37 	var-> evQueue = plist_create( 8, 8);
38 	apc_component_create( self);
39 
40 	res = my-> notification_types( self);
41 	hv = ( HV *) SvRV( res);
42 	hv_iterinit( hv);
43 	while (( he = hv_iternext( hv)) != NULL) {
44 		char buf[ 1024];
45 		SV ** holder;
46 		int len = snprintf( buf, 1023, "on%s", HeKEY( he));
47 		holder = hv_fetch( profile, buf, len, 0);
48 		if ( holder == NULL || SvTYPE( *holder) == SVt_NULL) continue;
49 		my-> add_notification( self, HeKEY( he), *holder, self, -1);
50 	}
51 	sv_free( res);
52 }
53 
54 void
Component_setup(Handle self)55 Component_setup( Handle self)
56 {
57 	Event ev = {cmCreate};
58 	ev. gen. source = self;
59 	my-> message( self, &ev);
60 
61 	if ( var-> owner) {
62 		ev. cmd = cmChildEnter;
63 		ev. gen. source = var-> owner;
64 		ev. gen. H      = self;
65 		CComponent( var-> owner)-> message( var-> owner, &ev);
66 	}
67 }
68 
bring_by_name(Handle self,PComponent item,char * name)69 static Bool bring_by_name( Handle self, PComponent item, char * name)
70 {
71 	return strcmp( name, item-> name) == 0;
72 }
73 
74 Handle
Component_bring(Handle self,char * componentName)75 Component_bring( Handle self, char * componentName)
76 {
77 	return my-> first_that_component( self, (void*)bring_by_name, componentName);
78 }
79 
80 static Bool
detach_all(Handle child,Handle self)81 detach_all( Handle child, Handle self)
82 {
83 	my-> detach( self, child, true);
84 	return false;
85 }
86 
87 void
Component_cleanup(Handle self)88 Component_cleanup( Handle self)
89 {
90 	Event ev = {cmDestroy};
91 
92 	if ( var-> owner) {
93 		Event ev = {cmChildLeave};
94 		ev. gen. source = var-> owner;
95 		ev. gen. H      = self;
96 		CComponent( var-> owner)-> message( var-> owner, &ev);
97 	}
98 
99 	if ( var-> components != NULL)
100 		list_first_that( var-> components, (void*)detach_all, ( void*) self);
101 
102 	ev. gen. source = self;
103 	my-> message( self, &ev);
104 }
105 
106 static Bool
free_private_posts(PostMsg * msg,void * dummy)107 free_private_posts( PostMsg * msg, void * dummy)
108 {
109 	sv_free( msg-> info1);
110 	sv_free( msg-> info2);
111 	free( msg);
112 	return false;
113 }
114 
115 static Bool
free_queue(PEvent event,void * dummy)116 free_queue( PEvent event, void * dummy)
117 {
118 	free( event);
119 	return false;
120 }
121 
122 static Bool
free_eventref(Handle self,Handle * org)123 free_eventref( Handle self, Handle * org)
124 {
125 	if ( var-> refs) list_delete( var-> refs, *org);
126 	my-> unlink_notifier( self, *org);
127 	return false;
128 }
129 
130 void
Component_done(Handle self)131 Component_done( Handle self)
132 {
133 	if ( var-> owner)
134 		CComponent( var-> owner)-> detach( var-> owner, self, false);
135 	if ( var-> eventIDs) {
136 		int i;
137 		PList list = var-> events;
138 		hash_destroy( var-> eventIDs, false);
139 		var-> eventIDs = NULL;
140 		for ( i = 0; i < var-> eventIDCount; i++) {
141 			int j;
142 			for ( j = 0; j < list-> count; j += 2)
143 				sv_free(( SV *) list-> items[ j + 1]);
144 			list_destroy( list++);
145 		}
146 		free( var-> events);
147 		var-> events = NULL;
148 	}
149 
150 
151 	if ( var-> refs) {
152 		Handle * pself = &self;
153 		list_first_that( var-> refs, (void*)free_eventref, pself);
154 		plist_destroy( var-> refs);
155 		var-> refs = NULL;
156 	}
157 
158 	if ( var-> postList != NULL) {
159 		list_first_that( var-> postList, (void*)free_private_posts, NULL);
160 		list_destroy( var-> postList);
161 		free( var-> postList);
162 		var-> postList = NULL;
163 	}
164 	if ( var-> evQueue != NULL)
165 	{
166 		list_first_that( var-> evQueue, (void*)free_queue, NULL);
167 		list_destroy( var-> evQueue);
168 		free( var-> evQueue);
169 		var-> evQueue = NULL;
170 	}
171 	if ( var-> components != NULL) {
172 		list_destroy( var-> components);
173 		free( var-> components);
174 		var-> components = NULL;
175 	}
176 	apc_component_destroy( self);
177 	free( var-> name);
178 	var-> name = NULL;
179 	free( var-> evStack);
180 	var-> evStack = NULL;
181 	inherited done( self);
182 }
183 
184 void
Component_attach(Handle self,Handle object)185 Component_attach( Handle self, Handle object)
186 {
187 	if ( var-> stage > csNormal) return;
188 
189 	if ( object && kind_of( object, CComponent)) {
190 		if ( var-> components == NULL)
191 			var-> components = plist_create( 8, 8);
192 		else
193 			if ( list_index_of( var-> components, object) >= 0) {
194 				warn( "Object attach failed");
195 				return;
196 			}
197 		list_add( var-> components, object);
198 		SvREFCNT_inc( SvRV(( PObject( object))-> mate));
199 	} else
200 		warn( "Object attach failed");
201 }
202 
203 void
Component_detach(Handle self,Handle object,Bool kill)204 Component_detach( Handle self, Handle object, Bool kill)
205 {
206 	if ( object && ( var-> components != NULL)) {
207 		int index = list_index_of( var-> components, object);
208 		if ( index >= 0) {
209 			list_delete_at( var-> components, index);
210 			--SvREFCNT( SvRV(( PObject( object))-> mate));
211 			if ( kill) Object_destroy( object);
212 		}
213 	}
214 }
215 
216 SV *
Component_name(Handle self,Bool set,SV * name)217 Component_name( Handle self, Bool set, SV * name)
218 {
219 	if ( set) {
220 		free( var-> name);
221 		var-> name = NULL;
222 		var-> name = duplicate_string( SvPV_nolen( name));
223 		opt_assign( optUTF8_name, prima_is_utf8_sv(name));
224 		if ( var-> stage >= csNormal)
225 			apc_component_fullname_changed_notify( self);
226 	} else {
227 		name = newSVpv( var-> name ? var-> name : "", 0);
228 		if ( is_opt( optUTF8_name)) SvUTF8_on( name);
229 		return name;
230 	}
231 	return NULL_SV;
232 }
233 
234 Handle
Component_owner(Handle self,Bool set,Handle owner)235 Component_owner( Handle self, Bool set, Handle owner)
236 {
237 	HV * profile;
238 	if ( !set)
239 		return var-> owner;
240 	profile = newHV();
241 	pset_H( owner, owner);
242 	my-> set( self, profile);
243 	sv_free(( SV *) profile);
244 	return NULL_HANDLE;
245 }
246 
247 
248 void
Component_set(Handle self,HV * profile)249 Component_set( Handle self, HV * profile)
250 {
251 	/* this can eliminate unwilling items */
252 	/* from HV before indirect Object::set */
253 	my-> update_sys_handle( self, profile);
254 
255 	if ( pexist( owner)) {
256 		Handle owner, oldOwner = var-> owner;
257 		if ( !my-> validate_owner( self, &owner, profile))
258 			croak( "Illegal 'owner' reference passed to %s::%s", my-> className, "set");
259 
260 		if ( oldOwner && oldOwner != owner) {
261 			Event ev;
262 			ev. cmd = cmChildLeave;
263 			ev. gen. source = oldOwner;
264 			ev. gen. H      = self;
265 			if ( oldOwner)
266 				CComponent( oldOwner)-> message( oldOwner, &ev);
267 		}
268 
269 		my-> migrate( self, owner);
270 		var-> owner = owner;
271 		pdelete( owner);
272 
273 		if ( oldOwner != owner) {
274 			Event ev;
275 
276 			ev. cmd = cmChildEnter;
277 			ev. gen. source = owner;
278 			ev. gen. H      = self;
279 			if ( owner)
280 				CComponent( owner)-> message( owner, &ev);
281 
282 			ev. cmd = cmChangeOwner;
283 			ev. gen. source = self;
284 			ev. gen. H      = oldOwner;
285 			my-> message( self, &ev);
286 		}
287 	}
288 
289 	inherited set ( self, profile);
290 }
291 
292 static Bool
find_dup_msg(PEvent event,int * cmd)293 find_dup_msg( PEvent event, int * cmd)
294 {
295 	return event-> cmd == *cmd;
296 }
297 
298 Bool
Component_message(Handle self,PEvent event)299 Component_message( Handle self, PEvent event)
300 {
301 	Bool ret      = false;
302 	if ( var-> stage == csNormal) {
303 		if ( var-> evQueue) goto Constructing;
304 ForceProcess:
305 		protect_object( self);
306 		my-> push_event( self);
307 		my-> handle_event( self, event);
308 		ret = my-> pop_event( self);
309 		if ( !ret) event-> cmd = 0;
310 		unprotect_object( self);
311 	} else if ( var-> stage == csConstructing) {
312 		if ( var-> evQueue == NULL)
313 			croak("Object set twice to constructing stage");
314 Constructing:
315 		switch ( event-> cmd & ctQueueMask) {
316 		case ctDiscardable:
317 			break;
318 		case ctPassThrough:
319 			goto ForceProcess;
320 		case ctSingle:
321 			event-> cmd = ( event-> cmd & ~ctQueueMask) | ctSingleResponse;
322 			if ( list_first_that( var-> evQueue, (void*)find_dup_msg, &event-> cmd) >= 0)
323 				break;
324 		default:
325 			{
326 				void * ev = malloc( sizeof( Event));
327 				if ( ev)
328 					list_add( var-> evQueue, ( Handle) memcpy( ev, event, sizeof( Event)));
329 			}
330 		}
331 	} else if (( var-> stage < csFinalizing) && ( event-> cmd & ctNoInhibit))
332 		goto ForceProcess;
333 	return ret;
334 }
335 
336 
337 Bool
Component_can_event(Handle self)338 Component_can_event( Handle self)
339 {
340 	return var-> stage == csNormal;
341 }
342 
343 void
Component_clear_event(Handle self)344 Component_clear_event( Handle self)
345 {
346 	my-> set_eventFlag( self, 0);
347 }
348 
349 void
Component_push_event(Handle self)350 Component_push_event( Handle self)
351 {
352 	if ( var-> stage == csDead)
353 		return;
354 	if ( var-> evPtr == var-> evLimit) {
355 		char * newStack = allocs( 16 + var-> evLimit);
356 		if ( !newStack) croak("Not enough memory");
357 		if ( var-> evStack) {
358 			memcpy( newStack, var-> evStack, var-> evLimit);
359 			free( var-> evStack);
360 		}
361 		var-> evStack = newStack;
362 		var-> evLimit += 16;
363 	}
364 	var-> evStack[ var-> evPtr++] = 1;
365 }
366 
367 Bool
Component_pop_event(Handle self)368 Component_pop_event( Handle self)
369 {
370 	if ( var-> stage == csDead)
371 		return false;
372 	if ( !var-> evStack || var-> evPtr <= 0) {
373 		warn("Component::pop_event call not within message()");
374 		return false;
375 	}
376 	return var-> evStack[ --var-> evPtr];
377 }
378 
379 
380 Bool
Component_eventFlag(Handle self,Bool set,Bool eventFlag)381 Component_eventFlag( Handle self, Bool set, Bool eventFlag)
382 {
383 	if ( var-> stage == csDead)
384 		return false;
385 	if ( !var-> evStack || var-> evPtr <= 0) {
386 		warn("Component::eventFlag call not within message()");
387 		return false;
388 	}
389 	if ( set)
390 		var-> evStack[ var-> evPtr - 1] = eventFlag;
391 	return set ? eventFlag : var-> evStack[ var-> evPtr - 1];
392 }
393 
394 void
Component_event_error(Handle self)395 Component_event_error( Handle self)
396 {
397 	apc_beep( mbWarning);
398 }
399 
400 SV *
Component_get_handle(Handle self)401 Component_get_handle( Handle self)
402 {
403 	return newSVsv( NULL_SV);
404 }
405 
406 static Bool
oversend(PEvent event,Handle self)407 oversend( PEvent event, Handle self)
408 {
409 	my-> message( self, event);
410 	free( event);
411 	return false;
412 }
413 
414 void
Component_handle_event(Handle self,PEvent event)415 Component_handle_event( Handle self, PEvent event)
416 {
417 	switch ( event-> cmd)
418 	{
419 	case cmCreate:
420 		my-> notify( self, "<s", "Create");
421 		if ( var-> stage == csNormal && var-> evQueue) {
422 			PList q = var-> evQueue;
423 			var-> evQueue = NULL;
424 			if ( q-> count > 0)
425 				list_first_that( q, (void*)oversend, ( void*) self);
426 			list_destroy( q);
427 			free( q);
428 		}
429 		break;
430 	case cmDestroy:
431 		opt_set( optcmDestroy);
432 		my-> notify( self, "<s", "Destroy");
433 		opt_clear( optcmDestroy);
434 		break;
435 	case cmPost:
436 		{
437 			PPostMsg p = ( PPostMsg) event-> gen. p;
438 			list_delete( var-> postList, ( Handle) p);
439 			my-> notify( self, "<sSS", "PostMessage", p-> info1, p-> info2);
440 			if ( p-> info1) sv_free( p-> info1);
441 			if ( p-> info2) sv_free( p-> info2);
442 			free( p);
443 		}
444 		break;
445 	case cmChangeOwner:
446 		my-> notify( self, "<sH", "ChangeOwner", event-> gen. H);
447 		break;
448 	case cmChildEnter:
449 		my-> notify( self, "<sH", "ChildEnter", event-> gen. H);
450 		break;
451 	case cmChildLeave:
452 		my-> notify( self, "<sH", "ChildLeave", event-> gen. H);
453 		break;
454 	case cmSysHandle:
455 		my-> notify( self, "<s", "SysHandle");
456 		break;
457 	}
458 }
459 
460 int
Component_is_owner(Handle self,Handle objectHandle)461 Component_is_owner( Handle self, Handle objectHandle)
462 {
463 	int depth = 1;
464 	if ( !objectHandle || !kind_of( objectHandle, CComponent))
465 		return 0;
466 	if ( objectHandle == self) return -1;
467 	while ( PComponent(objectHandle)-> owner) {
468 		if ( PComponent(objectHandle)-> owner == self)
469 			return depth;
470 		objectHandle = PComponent(objectHandle)-> owner;
471 		depth++;
472 	}
473 	return 0;
474 }
475 
476 Bool
Component_migrate(Handle self,Handle attachTo)477 Component_migrate( Handle self, Handle attachTo)
478 {
479 	Handle detachFrom = var-> owner;
480 
481 	if ( detachFrom != attachTo) {
482 		if ( attachTo)
483 			PComponent(attachTo)  -> self-> attach( attachTo, self);
484 		if ( detachFrom)
485 			PComponent(detachFrom)-> self-> detach( detachFrom, self, false);
486 	}
487 
488 	return detachFrom != attachTo;
489 }
490 
491 void
Component_recreate(Handle self)492 Component_recreate( Handle self)
493 {
494 	HV * profile = newHV();
495 	pset_H( owner, var-> owner);
496 	my-> update_sys_handle( self, profile);
497 	sv_free(( SV *) profile);
498 }
499 
500 Handle
Component_first_that_component(Handle self,void * actionProc,void * params)501 Component_first_that_component( Handle self, void * actionProc, void * params)
502 {
503 	Handle child = NULL_HANDLE;
504 	int i, count;
505 	Handle * list = NULL;
506 
507 	if ( actionProc == NULL || var-> components == NULL)
508 		return NULL_HANDLE;
509 	count = var-> components-> count;
510 	if ( count == 0) return NULL_HANDLE;
511 	if ( !( list = allocn( Handle, count))) return NULL_HANDLE;
512 	memcpy( list, var-> components-> items, sizeof( Handle) * count);
513 
514 	for ( i = 0; i < count; i++)
515 	{
516 		if ((( PActionProc) actionProc)( self, list[ i], params))
517 		{
518 			child = list[ i];
519 			break;
520 		}
521 	}
522 	free( list);
523 	return child;
524 }
525 
526 void
Component_post_message(Handle self,SV * info1,SV * info2)527 Component_post_message( Handle self, SV * info1, SV * info2)
528 {
529 	PPostMsg p;
530 	Event ev = { cmPost};
531 	if ( !application ) return;
532 	if ( var-> stage > csNormal) return;
533 	if (!( p = alloc1( PostMsg))) return;
534 	p-> info1  = newSVsv( info1);
535 	p-> info2  = newSVsv( info2);
536 	p-> h      = self;
537 	if ( var-> postList == NULL)
538 		list_create( var-> postList = ( List*) malloc( sizeof( List)), 8, 8);
539 	list_add( var-> postList, ( Handle) p);
540 	ev. gen. p = p;
541 	ev. gen. source = ev. gen. H = self;
542 	apc_message( application, &ev, true);
543 }
544 
545 
546 void
Component_update_sys_handle(Handle self,HV * profile)547 Component_update_sys_handle( Handle self, HV * profile)
548 {
549 }
550 
551 Bool
Component_validate_owner(Handle self,Handle * owner,HV * profile)552 Component_validate_owner( Handle self, Handle * owner, HV * profile)
553 {
554 	dPROFILE;
555 	*owner = pget_H( owner);
556 
557 	if ( *owner != NULL_HANDLE) {
558 		Handle x = *owner;
559 
560 		if (((( PObject) x)-> stage > csNormal) || !kind_of( x, CComponent))
561 			return false;
562 
563 		while ( x) {
564 			if ( x == self)
565 				return false;
566 			x = PComponent( x)-> owner;
567 		}
568 	}
569 
570 	return true;
571 }
572 
573 void
Component_on_create(Handle self)574 Component_on_create( Handle self)
575 {
576 }
577 
578 void
Component_on_destroy(Handle self)579 Component_on_destroy( Handle self)
580 {
581 }
582 
583 void
Component_on_postmessage(Handle self,SV * info1,SV * info2)584 Component_on_postmessage( Handle self, SV * info1, SV * info2)
585 {
586 }
587 
XS(Component_event_hook_FROMPERL)588 XS( Component_event_hook_FROMPERL)
589 {
590 	dXSARGS;
591 	SV *hook;
592 
593 	if ( items == 0) {
594 	GET_CASE:
595 		if ( eventHook)
596 			XPUSHs( sv_2mortal( newSVsv(( SV *) eventHook)));
597 		else
598 			XPUSHs( &PL_sv_undef);
599 		PUTBACK;
600 		return;
601 	}
602 
603 	hook = ST(0);
604 	/* shift unless ref $_[0] */
605 	if ( SvPOK(hook) && !SvROK(hook)) {
606 		if ( items == 1) goto GET_CASE;
607 		hook = ST(1);
608 	}
609 
610 	if ( SvTYPE(hook) == SVt_NULL) {
611 		if ( eventHook) sv_free( eventHook);
612 		eventHook = NULL;
613 		PUTBACK;
614 		return;
615 	}
616 
617 	if ( !SvROK( hook) || ( SvTYPE( SvRV( hook)) != SVt_PVCV)) {
618 		warn("Not a CODE reference passed to Prima::Component::event_hook");
619 		PUTBACK;
620 		return;
621 	}
622 
623 	if ( eventHook) sv_free( eventHook);
624 	eventHook = newSVsv( hook);
625 	PUTBACK;
626 	return;
627 }
628 
XS(Component_notify_FROMPERL)629 XS( Component_notify_FROMPERL)
630 {
631 	dXSARGS;
632 	Handle   self;
633 	SV     * res;
634 	HV     * hv;
635 	char   * name, * s;
636 	int      nameLen, rnt, i, ret = -1, evPtr;
637 	SV    ** argsv;
638 	int      argsc = items - 1;
639 	char     buf[ 1024];
640 	SV     * privMethod;
641 	Handle * sequence;
642 	int      seqCount = 0, stage = csNormal;
643 	PList    list = NULL;
644 
645 	if ( items < 2) {
646 		exception_remember("Invalid usage of Component.notify");
647 		return;
648 	}
649 	SP -= items;
650 	self    = gimme_the_mate( ST( 0));
651 	name    = ( char*) SvPV_nolen( ST( 1));
652 	if ( self == NULL_HANDLE) {
653 		exception_remember( "Illegal object reference passed to Component.notify");
654 		return;
655 	}
656 
657 	if ( eventHook) {
658 		dSP;
659 		dG_EVAL_ARGS;
660 
661 		int flag;
662 		ENTER;
663 		SAVETMPS;
664 		PUSHMARK( sp);
665 		EXTEND( sp, items);
666 		for ( i = 0; i < items; i++) PUSHs( ST( i));
667 		PUTBACK;
668 		OPEN_G_EVAL;
669 		perl_call_sv( eventHook, G_SCALAR | G_EVAL);
670 		SPAGAIN;
671 		if ( SvTRUE( GvSV( PL_errgv))) {
672 			(void)POPs;
673 			CLOSE_G_EVAL;
674 			exception_remember(SvPV_nolen( GvSV( PL_errgv)));
675 			return;
676 		}
677 		CLOSE_G_EVAL;
678 		SPAGAIN;
679 		flag = POPi;
680 		FREETMPS;
681 		LEAVE;
682 		SPAGAIN;
683 		if ( !flag) XSRETURN_IV(0);
684 	}
685 
686 	if ( var-> stage != csNormal) {
687 		if ( !is_opt( optcmDestroy)) XSRETURN_IV(1);
688 		opt_clear( optcmDestroy);
689 		stage = var-> stage;
690 	}
691 
692 	res = my-> notification_types( self);
693 	hv = ( HV *) SvRV( res);
694 	SPAGAIN;
695 
696 	/* fetching notification type */
697 	nameLen = strlen( name);
698 	if ( hv_exists( hv, name, nameLen)) {
699 		SV ** holder = hv_fetch( hv, name, nameLen, 0);
700 		if ( !holder || !SvOK(*holder) || SvTYPE(*holder) == SVt_NULL) {
701 			char buf[1024];
702 			snprintf(buf, 1024, "Inconsistent storage in %s::notification_types for %s during Component.notify", var-> self-> className, name);
703 			exception_remember(buf);
704 			return;
705 		}
706 		rnt = SvIV( *holder);
707 	} else {
708 		warn("Unknown notification:%s", name);
709 		rnt = ntDefault;
710 	}
711 	sv_free( res);
712 	SPAGAIN;
713 
714 	/* searching private on_xxx method */
715 	strncat( strcpy( buf, "on_"), name, 1020);
716 	for ( s = buf; *s; s++) *s = tolower(*s);
717 	privMethod = ( SV *) query_method( self, buf, 0);
718 	if ( privMethod) {
719 		privMethod = newRV( privMethod);
720 		seqCount++;
721 	}
722 	SPAGAIN;
723 
724 	/* searching dynamic onXxxx subs */
725 	if ( var-> eventIDs) {
726 		void * ret;
727 		ret = hash_fetch( var-> eventIDs, name, nameLen);
728 		if ( ret != NULL) {
729 			list = var-> events + PTR2UV( ret) - 1;
730 			seqCount += list-> count;
731 		}
732 	}
733 
734 	if ( seqCount == 0) XSRETURN_IV(1);
735 
736 	/* filling calling sequence */
737 	sequence = ( Handle *) malloc( seqCount * 2 * sizeof( void *));
738 	if ( !sequence) XSRETURN_IV(1);
739 
740 	i = 0;
741 	if ( privMethod && (( rnt & ntCustomFirst) == 0)) {
742 		sequence[ i++] = self;
743 		sequence[ i++] = ( Handle) privMethod;
744 	}
745 	if ( list) {
746 		int j;
747 		if ( rnt & ntFluxReverse) {
748 			for ( j = list-> count - 1; j > 0; j -= 2) {
749 				sequence[ i++] = list-> items[ j - 1];
750 				sequence[ i++] = list-> items[ j];
751 			}
752 		} else
753 			memcpy( sequence + i, list-> items, list-> count * sizeof( Handle));
754 	}
755 	if ( privMethod && ( rnt & ntCustomFirst)) {
756 		sequence[ i++] = self;
757 		sequence[ i++] = ( Handle) privMethod;
758 	}
759 
760 	/* copying arguments passed from perl */
761 	argsv = ( SV **) malloc( argsc * sizeof( SV *));
762 	if ( !argsv) {
763 		free( sequence);
764 		XSRETURN_IV(1);
765 	}
766 
767 	for ( i = 0; i < argsc; i++) argsv[ i] = ST( i + 1);
768 	argsv[ 0] = ST( 0);
769 
770 	/* entering event */
771 	my-> push_event( self);
772 	SPAGAIN;
773 
774 	/* cycling subs */
775 	rnt &= ntMultiple | ntEvent;
776 	evPtr = var-> evPtr;
777 	for ( i = 0; i < seqCount; i += 2) {
778 		dSP;
779 		dG_EVAL_ARGS;
780 		int j;
781 
782 		ENTER;
783 		SAVETMPS;
784 		PUSHMARK( sp);
785 		EXTEND( sp, argsc + (( sequence[ i] == self) ? 0 : 1));
786 		if ( sequence[ i] != self)
787 			PUSHs((( PAnyObject)( sequence[i]))-> mate);
788 		for ( j = 0; j < argsc; j++) PUSHs( argsv[ j]);
789 		PUTBACK;
790 		OPEN_G_EVAL;
791 		perl_call_sv(( SV*) sequence[ i + 1], G_DISCARD | G_EVAL);
792 		if ( SvTRUE( GvSV( PL_errgv))) {
793 			CLOSE_G_EVAL;
794 			if ( privMethod) sv_free( privMethod);
795 			free( argsv);
796 			free( sequence);
797 			exception_remember(SvPV_nolen( GvSV( PL_errgv)));
798 			return;
799 		}
800 		CLOSE_G_EVAL;
801 		SPAGAIN;
802 		FREETMPS;
803 		LEAVE;
804 		if (( var-> stage != stage) ||
805 			( var-> evPtr != evPtr) ||
806 			( rnt == ntSingle) ||
807 			(( rnt == ntEvent) && ( var-> evStack[ var-> evPtr - 1] == 0))
808 			) break;
809 	}
810 	SPAGAIN;
811 
812 	/* leaving */
813 	if ( privMethod) sv_free( privMethod);
814 	if ( var-> stage < csDead) ret = my-> pop_event( self);
815 	SPAGAIN;
816 	SP -= items;
817 	XPUSHs( sv_2mortal( newSViv( ret)));
818 	free( argsv);
819 	free( sequence);
820 	PUTBACK;
821 }
822 
823 Bool
Component_notify(Handle self,char * format,...)824 Component_notify( Handle self, char * format, ...)
825 {
826 	Bool r;
827 	SV * ret;
828 	va_list args;
829 	va_start( args, format);
830 	ENTER;
831 	SAVETMPS;
832 	ret = call_perl_indirect( self, "notify", format, true, false, args);
833 	va_end( args);
834 	r = ( ret && SvIOK( ret)) ? (SvIV( ret) != 0) : 0;
835 	if ( ret) my-> set_eventFlag( self, r);
836 	FREETMPS;
837 	LEAVE;
838 	return r;
839 }
840 
841 Bool
Component_notify_REDEFINED(Handle self,char * format,...)842 Component_notify_REDEFINED( Handle self, char * format, ...)
843 {
844 	Bool r;
845 	SV * ret;
846 	va_list args;
847 	va_start( args, format);
848 	ENTER;
849 	SAVETMPS;
850 	ret = call_perl_indirect( self, "notify", format, true, false, args);
851 	va_end( args);
852 	r = ( ret && SvIOK( ret)) ? (SvIV( ret) != 0) : 0;
853 	if ( ret) my-> set_eventFlag( self, r);
854 	FREETMPS;
855 	LEAVE;
856 	return r;
857 }
858 
XS(Component_get_components_FROMPERL)859 XS( Component_get_components_FROMPERL)
860 {
861 	dXSARGS;
862 	Handle self;
863 	Handle * list;
864 	int i, count;
865 
866 	if ( items != 1)
867 		croak ("Invalid usage of Component.get_components");
868 	SP -= items;
869 	self = gimme_the_mate( ST( 0));
870 	if ( self == NULL_HANDLE)
871 		croak( "Illegal object reference passed to Component.get_components");
872 	if ( var-> components) {
873 		count = var-> components-> count;
874 		list  = var-> components-> items;
875 		EXTEND( sp, count);
876 		for ( i = 0; i < count; i++)
877 			PUSHs( sv_2mortal( newSVsv((( PAnyObject) list[ i])-> mate)));
878 	}
879 	PUTBACK;
880 	return;
881 }
882 
Component_get_components(Handle self)883 void Component_get_components          ( Handle self) { warn("Invalid call of Component::get_components"); }
Component_get_components_REDEFINED(Handle self)884 void Component_get_components_REDEFINED( Handle self) { warn("Invalid call of Component::get_components"); }
885 
886 UV
Component_add_notification(Handle self,char * name,SV * subroutine,Handle referer,int index)887 Component_add_notification( Handle self, char * name, SV * subroutine, Handle referer, int index)
888 {
889 	UV     ret;
890 	PList  list;
891 	int    nameLen = strlen( name);
892 	SV   * res;
893 
894 	res = my-> notification_types( self);
895 	if ( !hv_exists(( HV *) SvRV( res), name, nameLen)) {
896 		sv_free( res);
897 		warn("No such event %s", name);
898 		return 0;
899 	}
900 	sv_free( res);
901 
902 	if ( !subroutine || !SvROK( subroutine) || ( SvTYPE( SvRV( subroutine)) != SVt_PVCV)) {
903 		warn("Not a CODE reference passed to %s to Component::add_notification", name);
904 		return 0;
905 	}
906 
907 	if ( referer == NULL_HANDLE) referer = self;
908 
909 	if ( var-> eventIDs == NULL) {
910 		var-> eventIDs = hash_create();
911 		ret = 0;
912 	} else
913 		ret = PTR2UV(hash_fetch( var-> eventIDs, name, nameLen));
914 
915 	if ( ret == 0) {
916 		hash_store( var-> eventIDs, name, nameLen, INT2PTR(void*, var-> eventIDCount + 1));
917 		if ( var-> events == NULL)
918 			var-> events = ( List*) malloc( sizeof( List));
919 		else {
920 			void * cf = realloc( var-> events, ( var-> eventIDCount + 1) * sizeof( List));
921 			if ( cf == NULL) free( var-> events);
922 			var-> events = ( List*) cf;
923 		}
924 		if ( var-> events == NULL) croak("Not enough memory");
925 		list = var-> events + var-> eventIDCount++;
926 		list_create( list, 2, 2);
927 	} else
928 		list = var-> events +  PTR2UV( ret) - 1;
929 
930 	ret   = PTR2UV(newSVsv( subroutine));
931 	index = list_insert_at( list, referer, index);
932 	list_insert_at( list, ( Handle) ret, index + 1);
933 
934 	if ( referer != self) {
935 		if ( PComponent( referer)-> refs == NULL)
936 			PComponent( referer)-> refs = plist_create( 2, 2);
937 		else
938 			if ( list_index_of( PComponent( referer)-> refs, self) >= 0) goto NO_ADDREF;
939 		list_add( PComponent( referer)-> refs, self);
940 	NO_ADDREF:;
941 		if ( var-> refs == NULL)
942 			var-> refs = plist_create( 2, 2);
943 		else
944 			if ( list_index_of( var-> refs, referer) >= 0) goto NO_SELFREF;
945 		list_add( var-> refs, referer);
946 	NO_SELFREF:;
947 	}
948 	return ret;
949 }
950 
951 void
Component_remove_notification(Handle self,UV id)952 Component_remove_notification( Handle self, UV id)
953 {
954 	int i = var-> eventIDCount;
955 	PList  list = var-> events;
956 
957 	if ( list == NULL) return;
958 
959 	while ( i--) {
960 		int j;
961 		for ( j = 0; j < list-> count; j += 2) {
962 			if ((( UV ) list-> items[ j + 1]) != id) continue;
963 			sv_free(( SV *) list-> items[ j + 1]);
964 			list_delete_at( list, j + 1);
965 			list_delete_at( list, j);
966 			return;
967 		}
968 		list++;
969 	}
970 }
971 
972 void
Component_unlink_notifier(Handle self,Handle referer)973 Component_unlink_notifier( Handle self, Handle referer)
974 {
975 	int i = var-> eventIDCount;
976 	PList  list = var-> events;
977 
978 	if ( list == NULL) return;
979 
980 	while ( i--) {
981 		int j;
982 	AGAIN:
983 		for ( j = 0; j < list-> count; j += 2) {
984 			if ( list-> items[ j] != referer) continue;
985 			sv_free(( SV *) list-> items[ j + 1]);
986 			list_delete_at( list, j + 1);
987 			list_delete_at( list, j);
988 			goto AGAIN;
989 		}
990 		list++;
991 	}
992 }
993 
XS(Component_get_notification_FROMPERL)994 XS( Component_get_notification_FROMPERL)
995 {
996 	dXSARGS;
997 	Handle self;
998 	char * event;
999 	void * ret;
1000 	PList  list;
1001 
1002 	if ( items < 2)
1003 		croak ("Invalid usage of Component.get_notification");
1004 
1005 	SP -= items;
1006 	self = gimme_the_mate( ST( 0));
1007 	if ( self == NULL_HANDLE)
1008 		croak( "Illegal object reference passed to Component.get_notification");
1009 
1010 	if ( var-> eventIDs == NULL) XSRETURN_EMPTY;
1011 	event = ( char *) SvPV_nolen( ST( 1));
1012 	ret = hash_fetch( var-> eventIDs, event, strlen( event));
1013 	if ( ret == NULL) XSRETURN_EMPTY;
1014 	list = var-> events + PTR2UV( ret) - 1;
1015 
1016 	if ( items < 3) {
1017 		int i;
1018 		if ( GIMME_V == G_ARRAY) {
1019 			int count = (int)( list-> count * 1.5);
1020 			EXTEND( sp, count);
1021 			for ( i = 0; i < list-> count; i += 2) {
1022 				PUSHs( sv_2mortal( newSVsv((( PAnyObject)( list-> items[i]))-> mate)));
1023 				PUSHs( sv_2mortal( newSVsv(( SV *) list->items[i + 1])));
1024 				PUSHs( sv_2mortal( newSViv(( long) list->items[i + 1])));
1025 			}
1026 		} else
1027 			XPUSHs( sv_2mortal( newSViv( list-> count / 2)));
1028 		PUTBACK;
1029 	} else {
1030 		int index = SvIV( ST( 2));
1031 		int count = list-> count / 2;
1032 		if ( index >= count || index < -count) XSRETURN_EMPTY;
1033 		if ( index < 0) index = count + index;
1034 		EXTEND( sp, 3);
1035 		PUSHs( sv_2mortal( newSVsv((( PAnyObject) list->items[index * 2])-> mate)));
1036 		PUSHs( sv_2mortal( newSVsv(( SV *) list->items[index * 2 + 1])));
1037 		PUSHs( sv_2mortal( newSViv(( long) list->items[index * 2 + 1])));
1038 		PUTBACK;
1039 	}
1040 }
1041 
Component_get_notification(Handle self,char * name,int index)1042 void Component_get_notification          ( Handle self, char * name, int index) { warn("Invalid call of Component::get_notification"); }
Component_get_notification_REDEFINED(Handle self,char * name,int index)1043 void Component_get_notification_REDEFINED( Handle self, char * name, int index) { warn("Invalid call of Component::get_notification"); }
1044 
XS(Component_set_notification_FROMPERL)1045 XS( Component_set_notification_FROMPERL)
1046 {
1047 	dXSARGS;
1048 	GV * gv;
1049 	SV * sub;
1050 	char * name, * convname;
1051 	Handle self;
1052 
1053 	if ( items < 1)
1054 		croak ("Invalid usage of Component::notification property");
1055 	self = gimme_the_mate( ST( 0));
1056 	if ( self == NULL_HANDLE)
1057 		croak( "Illegal object reference passed to Component::notification property");
1058 	if ( CvANON( cv) || !( gv = CvGV( cv))) croak("Cannot be called as anonymous sub");
1059 
1060 	sub = sv_newmortal();
1061 	gv_efullname3( sub, gv, NULL);
1062 	name = SvPVX( sub);
1063 	if ( items < 2)
1064 		croak( "Attempt to read write-only property %s", name);
1065 	convname = name;
1066 	while ( *(name++)) {
1067 		if ( *name == ':') convname = name + 1;
1068 	}
1069 
1070 	sub = ST( 1);
1071 	if ( convname[0] == 'o' && convname[1] == 'n')
1072 		my-> add_notification( self, convname + 2, sub, self, -1);
1073 	XSRETURN_EMPTY;
1074 }
1075 
Component_set_notification(Handle self,char * name,SV * subroutine)1076 void Component_set_notification          ( Handle self, char * name, SV * subroutine) { warn("Invalid call of Component::set_notification"); }
Component_set_notification_REDEFINED(Handle self,char * name,SV * subroutine)1077 void Component_set_notification_REDEFINED( Handle self, char * name, SV * subroutine) { warn("Invalid call of Component::set_notification"); }
1078 
1079 SV *
Component_delegations(Handle self,Bool set,SV * delegations)1080 Component_delegations( Handle self, Bool set, SV * delegations)
1081 {
1082 	if ( set) {
1083 		int i, len;
1084 		AV * av;
1085 		Handle referer;
1086 		char *name;
1087 
1088 		if ( var-> stage > csNormal) return NULL_SV;
1089 		if ( !SvROK( delegations) || SvTYPE( SvRV( delegations)) != SVt_PVAV) return NULL_SV;
1090 
1091 		referer = var-> owner;
1092 		name    = var-> name;
1093 		av = ( AV *) SvRV( delegations);
1094 		len = av_len( av);
1095 		for ( i = 0; i <= len; i++) {
1096 			SV **holder = av_fetch( av, i, 0);
1097 			if ( !holder) continue;
1098 			if ( SvROK( *holder)) {
1099 				Handle mate = gimme_the_mate( *holder);
1100 				if (( mate == NULL_HANDLE) || !kind_of( mate, CComponent)) continue;
1101 				referer = mate;
1102 			} else if ( SvPOK( *holder)) {
1103 				CV * sub;
1104 				SV * subref;
1105 				char buf[ 1024];
1106 				char * event = SvPV_nolen( *holder);
1107 
1108 				if ( referer == NULL_HANDLE)
1109 					croak("Event delegations for objects without owners must be provided with explicit referer");
1110 				snprintf( buf, 1023, "%s_%s", name, event);
1111 				sub = query_method( referer, buf, 0);
1112 				if ( sub == NULL) continue;
1113 				my-> add_notification( self, event, subref = newRV(( SV*) sub), referer, -1);
1114 				sv_free( subref);
1115 			}
1116 		}
1117 	} else {
1118 		HE * he;
1119 		AV * av = newAV();
1120 		Handle last = NULL_HANDLE;
1121 		if ( var-> stage > csNormal || var-> eventIDs == NULL)
1122 			return newRV_noinc(( SV*) av);
1123 
1124 		hv_iterinit( var-> eventIDs);
1125 		while (( he = hv_iternext( var-> eventIDs)) != NULL) {
1126 			int i;
1127 			char * event = ( char *) HeKEY( he);
1128 			PList list = var-> events + PTR2UV( HeVAL( he)) - 1;
1129 			for ( i = 0; i < list-> count; i += 2) {
1130 				if ( list-> items[i] != last) {
1131 					last = list-> items[i];
1132 					av_push( av, newSVsv((( PAnyObject) last)-> mate));
1133 				}
1134 				av_push( av, newSVpv( event, 0));
1135 			}
1136 		}
1137 		return newRV_noinc(( SV*) av);
1138 	}
1139 	return NULL_SV;
1140 }
1141 
1142 #ifdef __cplusplus
1143 }
1144 #endif
1145