1 /* a subcolumn
2  */
3 
4 /*
5 
6     Copyright (C) 1991-2003 The National Gallery
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 
22  */
23 
24 /*
25 
26     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27 
28  */
29 
30 /*
31 #define DEBUG
32  */
33 
34 #include "ip.h"
35 
36 static HeapmodelClass *parent_class = NULL;
37 
38 static gboolean
subcolumn_row_pred_none(Row * row)39 subcolumn_row_pred_none( Row *row )
40 {
41 	return( FALSE );
42 }
43 
44 /* No params, no super.
45  */
46 static gboolean
subcolumn_row_pred_members(Row * row)47 subcolumn_row_pred_members( Row *row )
48 {
49 	if( row->sym &&
50 		is_system( row->sym ) )
51 		return( FALSE );
52 
53 	if( row->sym &&
54 		is_super( row->sym ) )
55 		return( FALSE );
56 
57 	if( row->sym &&
58 		row->sym->type == SYM_PARAM )
59 		return( FALSE );
60 
61 	return( TRUE );
62 }
63 
64 static gboolean
subcolumn_row_pred_params(Row * row)65 subcolumn_row_pred_params( Row *row )
66 {
67 	return( row->sym &&
68 		row->sym->type == SYM_PARAM );
69 }
70 
71 /* Everything but empty superclasses.
72  */
73 static gboolean
subcolumn_row_pred_super(Row * row)74 subcolumn_row_pred_super( Row *row )
75 {
76 	if( row->sym &&
77 		is_super( row->sym ) &&
78 		PEISELIST( &row->expr->root ) )
79 		return( FALSE );
80 
81 	return( TRUE );
82 }
83 
84 /* Array of these guys control member visibility, scol->vislevel indexes this
85  * array, one of preds from vislevel down has to be TRUE for the row to be
86  * visible.
87  */
88 const SubcolumnVisibility subcolumn_visibility[] = {
89 	{ "none", subcolumn_row_pred_none },
90 	{ "members", subcolumn_row_pred_members },
91 	{ "params", subcolumn_row_pred_params },
92 	{ "super", subcolumn_row_pred_super }
93 };
94 const int subcolumn_nvisibility = IM_NUMBER( subcolumn_visibility );
95 
96 /* Map down a Subcolumn.
97  */
98 void *
subcolumn_map(Subcolumn * scol,row_map_fn fn,void * a,void * b)99 subcolumn_map( Subcolumn *scol, row_map_fn fn, void *a, void *b )
100 {
101 	return( icontainer_map( ICONTAINER( scol ),
102 		(icontainer_map_fn) fn, a, b ) );
103 }
104 
105 static void
subcolumn_dispose(GObject * gobject)106 subcolumn_dispose( GObject *gobject )
107 {
108 	Subcolumn *scol;
109 
110 #ifdef DEBUG
111 	printf( "subcolumn_dispose\n" );
112 #endif /*DEBUG*/
113 
114 	g_return_if_fail( gobject != NULL );
115 	g_return_if_fail( IS_SUBCOLUMN( gobject ) );
116 
117 	scol = SUBCOLUMN( gobject );
118 
119 	scol->col = NULL;
120 	scol->scol = NULL;
121 	scol->top_col = NULL;
122 
123 	heap_unregister_element( reduce_context->heap, &scol->base );
124 	scol->base.type = ELEMENT_NOVAL;
125 	scol->base.ele = (void *) 13;
126 
127 	scol->this = NULL;
128 	scol->super = NULL;
129 
130 	G_OBJECT_CLASS( parent_class )->dispose( gobject );
131 }
132 
133 /* Stuff we track during class instance display update.
134  */
135 typedef struct {
136 	Subcolumn *scol;		/* Enclosing column */
137 	GSList *notused;		/* List of row we've not used */
138 } ClassRefreshInfo;
139 
140 /* Test for row represents a sym.
141  */
142 static void *
subcolumn_test_sym(Row * row,Symbol * sym)143 subcolumn_test_sym( Row *row, Symbol *sym )
144 {
145 	if( row->sym == sym )
146 		return( row );
147 
148 	return( NULL );
149 }
150 
151 /* Test for row has a zombie of the same name.
152  */
153 static void *
subcolumn_test_row_name(Row * row,Symbol * sym)154 subcolumn_test_row_name( Row *row, Symbol *sym )
155 {
156 	if( !row->sym &&
157 		strcmp( IOBJECT( row )->name, IOBJECT( sym )->name ) == 0 )
158 		return( row );
159 
160 	return( NULL );
161 }
162 
163 /* Refresh one line of a subcolumn.
164  */
165 static void
subcolumn_class_new_heap_sub(ClassRefreshInfo * cri,Symbol * sym,PElement * value)166 subcolumn_class_new_heap_sub( ClassRefreshInfo *cri,
167 	Symbol *sym, PElement *value )
168 {
169 	Row *row;
170 
171 #ifdef DEBUG
172 	char txt[200];
173 	VipsBuf buf = VIPS_BUF_STATIC( txt );
174 
175 	symbol_qualified_name( sym, &buf );
176 	printf( "subcolumn_class_new_heap_sub: %s\n", vips_buf_all( &buf ) );
177 #endif /*DEBUG*/
178 
179 	/* Do we have a row for this symbol?
180 	 */
181 	if( (row = (Row *) slist_map( cri->notused,
182 		(SListMapFn) subcolumn_test_sym, sym )) ) {
183 		/* Update it.
184 		 */
185 		if( heapmodel_new_heap( HEAPMODEL( row ), value ) )
186 			expr_error_set( row->expr );
187 
188 		cri->notused = g_slist_remove( cri->notused, row );
189 	}
190 	else if( (row = (Row *) slist_map( cri->notused,
191 		(SListMapFn) subcolumn_test_row_name, sym )) ) {
192 		/* There's a blank row of the same name, left for us by XML
193 		 * load. Update the row with the correct symbol.
194 		 */
195 		row_link_symbol( row, sym, NULL );
196 		if( heapmodel_new_heap( HEAPMODEL( row ), value ) )
197 			expr_error_set( row->expr );
198 
199 		cri->notused = g_slist_remove( cri->notused, row );
200 	}
201 	else {
202 		row = row_new( cri->scol, sym, value );
203 		if( heapmodel_new_heap( HEAPMODEL( row ), value ) )
204 			expr_error_set( row->expr );
205 	}
206 }
207 
208 #ifdef DEBUG
209 static void *
subcolumn_class_dump_tiny_row(Row * row)210 subcolumn_class_dump_tiny_row( Row *row )
211 {
212 	row_name_print( row );
213 	printf( " " );
214 
215 	return( NULL );
216 }
217 #endif /*DEBUG*/
218 
219 /* A new scrap of heap for a subcolumn.
220  */
221 static gboolean
subcolumn_class_new_heap(Subcolumn * scol,PElement * root)222 subcolumn_class_new_heap( Subcolumn *scol, PElement *root )
223 {
224 	PElement instance = *root;
225 
226 	Expr *expr;
227 	Row *row;
228 	gboolean result;
229 	PElement base, member;
230 	HeapNode *p;
231 	ClassRefreshInfo cri;
232 
233 	/* Must be a class display.
234 	 */
235 	g_assert( !scol->is_top );
236 	row = HEAPMODEL( scol )->row;
237 	expr = row->expr;
238 
239 #ifdef DEBUG
240 	printf( "subcolumn_class_new_heap: " );
241 	row_name_print( row );
242 	printf( "\n" );
243 #endif /*DEBUG*/
244 
245 	/* Can loop here for some recursive classes.
246 
247 		FIXME
248 
249 	if( mainw_countdown_animate( 99 ) )
250 		return( FALSE );
251 	 */
252 
253 	/* No displays for system rows.
254 	 */
255 	if( row->sym &&
256 		is_system( row->sym ) )
257 		return( TRUE );
258 
259 	/* If we are the top of a class instance display, get a new serial.
260 	 * As we recurse down refreshing our contents, this should stop
261 	 * circular structures looping the browser.
262 
263 	 	FIXME ... clear flags for a whole class, then do a complete
264 		redisplay? more reliable, but even slower :-(
265 
266 	 */
267 	if( scol->scol->is_top )
268 		heap_serial_new( reduce_context->heap );
269 
270 	/* Is it a class with a typecheck member? Go through
271 	 * that. Do an isclass first to force eval.
272 	 */
273 	if( !heap_is_class( &instance, &result ) )
274 		return( FALSE );
275 	if( result &&
276 		class_get_member( &instance, MEMBER_CHECK, NULL, &member ) ) {
277 #ifdef DEBUG
278 		printf( "subcolumn_class_new_heap: invoking arg checker\n" );
279 #endif
280 
281 		/* Force eval of the typecheck member.
282 		 */
283 		if( !heap_is_class( &member, &result ) || !result )
284 			return( FALSE );
285 	}
286 
287 	/* Have we already displayed this class?
288 	 */
289 	if( (PEGETVAL( &instance )->flgs & FLAG_SERIAL) ==
290 		reduce_context->heap->serial ) {
291 		/*
292 
293 			FIXME ... display something here? "circular"?
294 
295 		 */
296 		return( TRUE );
297 	}
298 	SETSERIAL( PEGETVAL( &instance )->flgs, reduce_context->heap->serial );
299 
300 	/* Note the heap root ... if this is the top of a row tree, then we
301 	 * clone the class and use that private copy.
302 	 */
303 	PEPOINTE( &base, &(SUBCOLUMN( scol ))->base );
304 	PEPUTPE( &base, &instance );
305 	PEPUTPE( &expr->root, &base );
306 
307 	/* Init rebuild params. We make a list of all the existing
308 	 * row objects for this class display, and every time we
309 	 * manage to reuse one of them, we knock it off the list. At the
310 	 * end, remove all unused rows.
311 	 */
312 	cri.scol = scol;
313 	cri.notused = g_slist_copy( ICONTAINER( scol )->children );
314 
315 #ifdef DEBUG
316 	printf( "subcolumn_class_new_heap: existing rows: " );
317 	icontainer_map( ICONTAINER( scol ),
318 		(icontainer_map_fn) subcolumn_class_dump_tiny_row, NULL, NULL );
319 	printf( "\n" );
320 #endif /*DEBUG*/
321 
322 	/* Loop along the members, updating row entries.
323 	 */
324 	PEGETCLASSMEMBER( &member, &base );
325 
326 	if( PEISNODE( &member ) )
327 		for( p = PEGETVAL( &member ); p; p = GETRIGHT( p ) ) {
328 			PElement s, v;
329 			HeapNode *hn;
330 			Symbol *sym;
331 
332 			/* Get the sym/value pair.
333 			 */
334 			hn = GETLEFT( p );
335 			PEPOINTLEFT( hn, &s );
336 			PEPOINTRIGHT( hn, &v );
337 			sym = SYMBOL( PEGETSYMREF( &s ) );
338 
339 			/* We don't make rows for the default constructor, or
340 			 * for ".name". These things don't change, so there's
341 			 * no point (and the default constructor has no text
342 			 * equivalent anyway).
343 			 */
344 			if( strcmp( IOBJECT( sym )->name, MEMBER_NAME ) == 0 )
345 				continue;
346 			if( is_member( sym ) &&
347 				strcmp( IOBJECT( sym )->name,
348 				IOBJECT( symbol_get_parent( sym ) )->name ) ==
349 				0 )
350 				continue;
351 
352 			/* Display!
353 			 */
354 			subcolumn_class_new_heap_sub( &cri, sym, &v );
355 		}
356 
357 	/* Remove all the rows we've not used.
358 	 */
359 	slist_map( cri.notused, (SListMapFn) iobject_destroy, NULL );
360 	IM_FREEF( g_slist_free, cri.notused );
361 
362 	return( TRUE );
363 }
364 
365 static void *
subcolumn_new_heap(Heapmodel * heapmodel,PElement * root)366 subcolumn_new_heap( Heapmodel *heapmodel, PElement *root )
367 {
368 	Subcolumn *scol = SUBCOLUMN( heapmodel );
369 
370 	/* New heap for a class display? CLear known_private, we've no idea
371 	 * where this heap came from.
372 	 */
373 	if( scol == scol->top_scol )
374 		scol->known_private = FALSE;
375 
376 	/* A bunch of locals? Update them all.
377 	 */
378 	if( !scol->is_top && !subcolumn_class_new_heap( scol, root ) )
379 		return( scol );
380 
381 	return( HEAPMODEL_CLASS( parent_class )->new_heap( heapmodel, root ) );
382 }
383 
384 static void
subcolumn_child_add(iContainer * parent,iContainer * child,int pos)385 subcolumn_child_add( iContainer *parent, iContainer *child, int pos )
386 {
387 	Subcolumn *scol = SUBCOLUMN( parent );
388 	Row *row = ROW( child );
389 
390 	/* May not have a symbol yet during ws load.
391 	 *
392 	 * Can't use is_this()/is_super(), not everything has been built yet.
393 	 * We don't do this often, so strcmp() it.
394 	 */
395 	const char *name =
396 		row->sym ? IOBJECT( row->sym )->name : IOBJECT( row )->name;
397 
398 	if( strcmp( name, MEMBER_THIS ) == 0 )
399 		scol->this = row;
400 
401 	if( strcmp( name, MEMBER_SUPER ) == 0 )
402 		scol->super = row;
403 
404 	ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos );
405 }
406 
407 static void
subcolumn_child_remove(iContainer * parent,iContainer * child)408 subcolumn_child_remove( iContainer *parent, iContainer *child )
409 {
410 	Subcolumn *scol = SUBCOLUMN( parent );
411 	Row *row = ROW( child );
412 
413 	ICONTAINER_CLASS( parent_class )->child_remove( parent, child );
414 
415 	if( scol->this == row )
416 		scol->this = NULL;
417 	if( scol->super == row )
418 		scol->super = NULL;
419 }
420 
421 /* If this is a top-level subcolumn, get the enclosing column.
422  */
423 static Column *
subcolumn_get_column(Subcolumn * scol)424 subcolumn_get_column( Subcolumn *scol )
425 {
426 	g_assert( scol->is_top );
427 
428 	return( COLUMN( ICONTAINER( scol )->parent ) );
429 }
430 
431 /* If this is a nested subcolumn, get the enclosing subcolumn.
432  */
433 static Subcolumn *
subcolumn_get_subcolumn(Subcolumn * scol)434 subcolumn_get_subcolumn( Subcolumn *scol )
435 {
436 	Rhs *rhs;
437 	Row *row;
438 	Subcolumn *escol;
439 
440 	g_assert( !scol->is_top );
441 
442 	rhs = HEAPMODEL( scol )->rhs;
443 	row = HEAPMODEL( rhs )->row;
444 	escol = row->scol;
445 
446 	return( escol );
447 }
448 
449 /* Return the enclosing column for a Subcolumn.
450  */
451 static Column *
subcolumn_get_top_column(Subcolumn * scol)452 subcolumn_get_top_column( Subcolumn *scol )
453 {
454 	if( !scol->is_top )
455 		return( subcolumn_get_top_column(
456 			subcolumn_get_subcolumn( scol ) ) );
457 
458 	return( subcolumn_get_column( scol ) );
459 }
460 
461 /* Return the enclosing subcolumn ... but not the is_top one. Ie. the enclosing
462  * subcolumn which has the base for this class tree.
463  */
464 static Subcolumn *
subcolumn_get_top_subcolumn(Subcolumn * scol)465 subcolumn_get_top_subcolumn( Subcolumn *scol )
466 {
467 	Subcolumn *enclosing;
468 
469 	if( scol->is_top )
470 		return( NULL );
471 
472 	enclosing = subcolumn_get_subcolumn( scol );
473 	if( enclosing->is_top )
474 		return( scol );
475 	else
476 		return( subcolumn_get_top_subcolumn( enclosing ) );
477 }
478 
479 static void
subcolumn_parent_add(iContainer * child)480 subcolumn_parent_add( iContainer *child )
481 {
482 	Subcolumn *scol = SUBCOLUMN( child );
483 
484 	ICONTAINER_CLASS( parent_class )->parent_add( child );
485 
486 	g_assert( IS_COLUMN( child->parent ) || IS_RHS( child->parent ) );
487 	g_assert( !IS_COLUMN( child->parent ) ||
488 		g_slist_length( child->parent->children ) == 1 );
489 
490 	scol->is_top = IS_COLUMN( child->parent );
491 
492 	/* For sub-columns, default to nothing visible.
493 	 */
494 	if( !scol->is_top )
495 		scol->vislevel = 0;
496 
497 	/* Update context pointers.
498 	 */
499 	if( scol->is_top )
500 		scol->col = subcolumn_get_column( scol );
501 	else
502 		scol->col = NULL;
503 
504 	if( !scol->is_top )
505 		scol->scol = subcolumn_get_subcolumn( scol );
506 	else
507 		scol->scol = NULL;
508 
509 	scol->top_col = subcolumn_get_top_column( scol );
510 	scol->top_scol = subcolumn_get_top_subcolumn( scol );
511 
512 	/* Top level subcolumns default to display on, others to display off.
513 	 */
514 	MODEL( scol )->display = scol->is_top;
515 }
516 
517 static View *
subcolumn_view_new(Model * model,View * parent)518 subcolumn_view_new( Model *model, View *parent )
519 {
520 	return( subcolumnview_new() );
521 }
522 
523 static void
subcolumn_display(Model * model,gboolean display)524 subcolumn_display( Model *model, gboolean display )
525 {
526 	/*
527 	printf( "subcolumn_display: " );
528 	row_name_print( HEAPMODEL( model )->row );
529 	printf( " %d\n", display );
530 	 */
531 
532 	MODEL_CLASS( parent_class )->display( model, display );
533 }
534 
535 static gboolean
subcolumn_load(Model * model,ModelLoadState * state,Model * parent,xmlNode * xnode)536 subcolumn_load( Model *model,
537 	ModelLoadState *state, Model *parent, xmlNode *xnode )
538 {
539 	Subcolumn *scol = SUBCOLUMN( model );
540 
541 	g_assert( IS_COLUMN( parent ) || IS_RHS( parent ) );
542 
543 	if( !get_iprop( xnode, "vislevel", &scol->vislevel ) )
544 		return( FALSE );
545 
546 	if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) )
547 		return( FALSE );
548 
549 	return( TRUE );
550 }
551 
552 static xmlNode *
subcolumn_save(Model * model,xmlNode * xnode)553 subcolumn_save( Model *model, xmlNode *xnode )
554 {
555 	Subcolumn *scol = SUBCOLUMN( model );
556 
557 	xmlNode *xthis;
558 
559 	if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) )
560 		return( NULL );
561 
562 	if( !set_iprop( xthis, "vislevel", scol->vislevel ) )
563 		return( NULL );
564 
565 	return( xthis );
566 }
567 
568 static void
subcolumn_class_init(SubcolumnClass * class)569 subcolumn_class_init( SubcolumnClass *class )
570 {
571 	GObjectClass *gobject_class = (GObjectClass *) class;
572 	iContainerClass *icontainer_class = (iContainerClass *) class;
573 	ModelClass *model_class = (ModelClass *) class;
574 	HeapmodelClass *heapmodel_class = (HeapmodelClass *) class;
575 
576 	parent_class = g_type_class_peek_parent( class );
577 
578 	/* Create signals.
579 	 */
580 
581 	/* Init methods.
582 	 */
583 	gobject_class->dispose = subcolumn_dispose;
584 
585 	icontainer_class->child_add = subcolumn_child_add;
586 	icontainer_class->child_remove = subcolumn_child_remove;
587 	icontainer_class->parent_add = subcolumn_parent_add;
588 
589 	model_class->view_new = subcolumn_view_new;
590 	model_class->display = subcolumn_display;
591 	model_class->load = subcolumn_load;
592 	model_class->save = subcolumn_save;
593 
594 	heapmodel_class->new_heap = subcolumn_new_heap;
595 
596 	/* Static init.
597 	 */
598 	model_register_loadable( MODEL_CLASS( class ) );
599 }
600 
601 static void
subcolumn_init(Subcolumn * scol)602 subcolumn_init( Subcolumn *scol )
603 {
604 #ifdef DEBUG
605 	printf( "subcolumn_init\n" );
606 #endif /*DEBUG*/
607 
608 	scol->col = NULL;
609 	scol->scol = NULL;
610 	scol->top_col = NULL;
611 
612         scol->vislevel = subcolumn_nvisibility - 1;
613 
614 	scol->base.type = ELEMENT_NOVAL;
615 	scol->base.ele = (void *) 14;
616 	heap_register_element( reduce_context->heap, &scol->base );
617 	scol->known_private = FALSE;
618 
619 	scol->this = NULL;
620 	scol->super = NULL;
621 }
622 
623 GType
subcolumn_get_type(void)624 subcolumn_get_type( void )
625 {
626 	static GType subcolumn_type = 0;
627 
628 	if( !subcolumn_type ) {
629 		static const GTypeInfo info = {
630 			sizeof( SubcolumnClass ),
631 			NULL,           /* base_init */
632 			NULL,           /* base_finalize */
633 			(GClassInitFunc) subcolumn_class_init,
634 			NULL,           /* class_finalize */
635 			NULL,           /* class_data */
636 			sizeof( Subcolumn ),
637 			32,             /* n_preallocs */
638 			(GInstanceInitFunc) subcolumn_init,
639 		};
640 
641 		subcolumn_type = g_type_register_static( TYPE_HEAPMODEL,
642 			"Subcolumn", &info, 0 );
643 	}
644 
645 	return( subcolumn_type );
646 }
647 
648 static void
subcolumn_link(Subcolumn * scol,Rhs * rhs,Column * col)649 subcolumn_link( Subcolumn *scol, Rhs *rhs, Column *col )
650 {
651 	g_assert( rhs == NULL || col == NULL );
652 
653 	/* parent_add() sets is_top for us.
654 	 */
655 	if( rhs )
656 		icontainer_child_add( ICONTAINER( rhs ),
657 			ICONTAINER( scol ), -1 );
658 	else
659 		icontainer_child_add( ICONTAINER( col ),
660 			ICONTAINER( scol ), -1 );
661 }
662 
663 Subcolumn *
subcolumn_new(Rhs * rhs,Column * col)664 subcolumn_new( Rhs *rhs, Column *col )
665 {
666 	Subcolumn *scol;
667 
668 	scol = SUBCOLUMN( g_object_new( TYPE_SUBCOLUMN, NULL ) );
669 	subcolumn_link( scol, rhs, col );
670 
671 	return( scol );
672 }
673 
674 void
subcolumn_set_vislevel(Subcolumn * scol,int vislevel)675 subcolumn_set_vislevel( Subcolumn *scol, int vislevel )
676 {
677 	scol->vislevel = IM_CLIP( 0, vislevel, subcolumn_nvisibility - 1 );
678 
679 #ifdef DEBUG
680 	printf( "subcolumn_set_vislevel: %d\n", scol->vislevel );
681 #endif /*DEBUG*/
682 
683 	iobject_changed( IOBJECT( scol ) );
684 }
685 
686 /* Make sure we have a private copy of the graph for this tree of stuff.
687  */
688 gboolean
subcolumn_make_private(Subcolumn * scol)689 subcolumn_make_private( Subcolumn *scol )
690 {
691 	Subcolumn *top_scol = scol->top_scol;
692 	PElement base;
693 
694 	if( !top_scol || top_scol->known_private )
695 		return( TRUE );
696 
697 #ifdef DEBUG
698 {
699 	Row *row = HEAPMODEL( top_scol )->row;
700 
701 	printf( "subcolumn_make_private: cloning " );
702 	row_name_print( row );
703 	printf( "\n" );
704 }
705 #endif /*DEBUG*/
706 
707 	/* Clone from the class args and rebuild our tree.
708 	 */
709 	PEPOINTE( &base, &top_scol->base );
710 	if( !class_clone_args( reduce_context->heap, &base, &base ) ||
711 		heapmodel_new_heap( HEAPMODEL( top_scol ), &base ) )
712 		return( FALSE );
713 
714 	top_scol->known_private = TRUE;
715 
716 	return( TRUE );
717 }
718 
719