1 /* asntypes.c
2 * ===========================================================================
3 *
4 *                            PUBLIC DOMAIN NOTICE
5 *               National Center for Biotechnology Information
6 *
7 *  This software/database is a "United States Government Work" under the
8 *  terms of the United States Copyright Act.  It was written as part of
9 *  the author's official duties as a United States Government employee and
10 *  thus cannot be copyrighted.  This software/database is freely available
11 *  to the public for use. The National Library of Medicine and the U.S.
12 *  Government have not placed any restriction on its use or reproduction.
13 *
14 *  Although all reasonable efforts have been taken to ensure the accuracy
15 *  and reliability of the software and data, the NLM and the U.S.
16 *  Government do not and cannot warrant the performance or results that
17 *  may be obtained by using this software or data. The NLM and the U.S.
18 *  Government disclaim all warranties, express or implied, including
19 *  warranties of performance, merchantability or fitness for any particular
20 *  purpose.
21 *
22 *  Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * File Name: asntypes.c
27 *
28 * Author:  James Ostell
29 *
30 * Version Creation Date: 3/4/91
31 *
32 * $Revision: 6.9 $
33 *
34 * File Description:
35 *   Routines to deal with internal operations on AsnType objects.
36 *
37 * Modifications:
38 * --------------------------------------------------------------------------
39 * Date     Name        Description of modification
40 * -------  ----------  -----------------------------------------------------
41 * 3/4/91   Kans        Stricter typecasting for GNU C and C++
42 * 04-20-93 Schuler     LIBCALL calling convention
43 * 02-24-94 Schuler     Make AsnTypeStringToHex LIBCALL too
44 *
45 * $Log: asntypes.c,v $
46 * Revision 6.9  2003/12/03 19:31:09  gouriano
47 * Corrected DTD generation (a different approach)
48 *
49 * Revision 6.8  2003/12/02 21:40:17  gouriano
50 * Revert back to rev 6.6
51 *
52 * Revision 6.7  2003/12/02 19:52:49  gouriano
53 * Corrected DTD generation
54 *
55 * Revision 6.6  2000/07/25 20:30:58  ostell
56 * added support for printing multiple ASN.1 modules as multiple XML DTD and .mod files
57 *
58 * Revision 6.5  2000/05/26 14:51:23  ostell
59 * fixed uninitialized variable
60 *
61 * Revision 6.4  2000/05/10 03:12:37  ostell
62 * added support for XML DTD and XML data output
63 *
64 * Revision 6.2  1998/06/12 19:27:56  kans
65 * fixed unix compiler warnings
66 *
67 * Revision 6.1  1997/10/29 02:42:10  vakatov
68 * Type castings to pass through the C++ compiler
69 *
70 * Revision 6.0  1997/08/25 18:10:25  madden
71 * Revision changed to 6.0
72 *
73 * Revision 5.3  1997/04/24 12:47:49  ostell
74 * in AsnTypeStringToHex, allowed argument "left" to be NULL
75 *
76  * Revision 5.2  1997/04/23  21:23:03  ostell
77  * changed BitHex reading routine to strip internal spaces and allow for linewraps at
78  * any spacing.
79  *
80  * Revision 5.1  1996/12/03  21:43:48  vakatov
81  * Adopted for 32-bit MS-Windows DLLs
82  *
83  * Revision 5.0  1996/05/28  14:00:29  ostell
84  * Set to revision 5.0
85  *
86  * Revision 4.0  1995/07/26  13:47:38  ostell
87  * force revision to 4.0
88  *
89  * Revision 2.19  1995/05/20  17:05:26  ostell
90  * added ASNTYPE_RUNTIME_LINK
91  * if defined enables oldstyle runtime link of AsnLinkType().
92  * default is disabled, which assumes all linking of asn.1 parse trees
93  * via calls to AsnLoad() functions is done before any AsnREAD or AsnWrite
94  *
95  * Revision 2.18  1995/05/15  18:38:28  ostell
96  * added Log line
97  *
98 *
99 * ==========================================================================
100 */
101 
102 /*****************************************************************************
103 *
104 *   asntypes.c
105 *   	basic routines for processing asn1 types
106 *   	does not support lexxing of type definitions - those are in
107 *   		asnlext.c
108 *
109 *****************************************************************************/
110 
111 #include "asnbuild.h"
112 
113 /*****************************************************************************
114 *
115 *   This stores all loaded parse trees and links them together
116 *
117 *****************************************************************************/
118 static ValNodePtr
119     amps ,                /* the AsnModulePtr blocks */
120     names ;               /* the load file names */
121 
122 /*****************************************************************************
123 *
124 *   CharPtr AsnFindPrimName(atp)
125 *   	returns the string name of an ASN.1 primitive type
126 *
127 *****************************************************************************/
AsnFindPrimName(AsnTypePtr atp)128 NLM_EXTERN CharPtr LIBCALL  AsnFindPrimName (AsnTypePtr atp)
129 {
130 	AsnTypePtr atp2;
131 
132 	atp2 = AsnFindBaseType(atp);
133 	if (atp2 == NULL)
134 		return NULL;
135 
136 	return atp2->type->name;
137 }
138 
139 /*****************************************************************************
140 *
141 *   CharPtr AsnFindBaseName(atp)
142 *   	returns the string name of an ASN.1 basic entity
143 *       returns NULL if not resolved
144 *       returns NULL if resolves to a primitive type (use AsnFindPrimName)
145 *
146 *****************************************************************************/
AsnFindBaseName(AsnTypePtr atp)147 NLM_EXTERN CharPtr LIBCALL  AsnFindBaseName (AsnTypePtr atp)
148 {
149 	AsnTypePtr atp2;
150 
151 	atp2 = AsnFindBaseType(atp);
152 	if (atp2 == NULL)
153 		return NULL;
154 
155 	if (atp2->name == NULL)
156 		return NULL;
157 	else if (IS_UPPER(*atp2->name))
158 		return atp2->name;
159 	else
160 		return NULL;
161 }
162 
163 /*****************************************************************************
164 *
165 *   void AsnKillValue(atp, dvp)
166 *   	eliminates stored values such as strings
167 *
168 *****************************************************************************/
AsnKillValue(AsnTypePtr atp,DataValPtr dvp)169 NLM_EXTERN void LIBCALL  AsnKillValue (AsnTypePtr atp, DataValPtr dvp)
170 {
171 	Int2 isa;
172 
173 	isa = AsnFindBaseIsa(atp);
174 	if (ISA_STRINGTYPE(isa))
175 		MemFree(dvp->ptrvalue);
176 	else if ((isa == OCTETS_TYPE) || (isa == STRSTORE_TYPE))
177 		BSFree((ByteStorePtr) dvp->ptrvalue);
178 	return;
179 }
180 
181 /*****************************************************************************
182 *
183 *   void AsnTypeSetIndent(increase, aip, atp)
184 *   	set the typestack type_indent
185 *
186 *****************************************************************************/
AsnTypeSetIndent(Boolean increase,AsnIoPtr aip,AsnTypePtr atp)187 NLM_EXTERN void AsnTypeSetIndent (Boolean increase, AsnIoPtr aip, AsnTypePtr atp)
188 {
189 	Int1 curr_indent;
190 	PstackPtr tmptype;
191 
192 
193 	if (increase)
194 	{
195 		aip->typestack[aip->type_indent].type = atp;     /* save type */
196 		aip->type_indent++;
197 		curr_indent = aip->type_indent;
198 		if (curr_indent == aip->max_type)   /* expand indent levels */
199 		{
200 			tmptype = aip->typestack;
201 			aip->typestack = (PstackPtr) MemNew((sizeof(Pstack) * (aip->max_type + 10)));
202 			MemCopy(aip->typestack, tmptype, (size_t)(sizeof(Pstack) * aip->max_type));
203 			MemFree(tmptype);
204 			aip->max_type += 10;
205 		}
206 		aip->typestack[curr_indent].type = NULL;     /* set to first time */
207 	}
208 	else
209 	{
210 		do
211 		{
212 			aip->typestack[aip->type_indent].type = NULL;
213 			if (aip->type_indent)
214 				aip->type_indent--;
215 		} while ((aip->type_indent) &&
216 			(AsnFindBaseIsa(aip->typestack[aip->type_indent-1].type) == CHOICE_TYPE)
217 			 && (aip->token != ISMODULE_TOKEN) && (!(aip->type & ASNIO_XML)));
218 		/* this is to pop out of choice nests */
219 		/* XML popping is formatted at ends */
220 	}
221 	return;
222 }
223 
224 /*****************************************************************************
225 *
226 *   AsnTypePtr AsnFindBaseType(atp)
227 *   	find the primitive type for atp
228 *
229 *****************************************************************************/
AsnFindBaseType(AsnTypePtr atp)230 NLM_EXTERN AsnTypePtr LIBCALL AsnFindBaseType (AsnTypePtr atp)
231 {
232 	if (atp == NULL) return NULL;
233 
234 	if (atp->type == NULL) return NULL;
235 
236 	while (! ISA_BASETYPE(atp->type->isa))
237 	{
238 		atp = atp->type;
239 		if (atp->type == NULL)   /* not found */
240 			return NULL;
241 	}
242 	return atp;
243 }
244 
AsnFindBaseTypeDTD(AsnTypePtr atp)245 NLM_EXTERN AsnTypePtr LIBCALL AsnFindBaseTypeDTD (AsnTypePtr atp)
246 {
247 	if (atp == NULL) return NULL;
248 
249 	if (atp->type == NULL) return NULL;
250 
251 	if (! ISA_BASETYPE(atp->type->isa))
252 	{
253 		atp = atp->type;
254 		if (atp->type == NULL)   /* not found */
255 			return atp;
256 	}
257 	return atp;
258 }
259 
260 /*****************************************************************************
261 *
262 *   Int2 AsnFindBaseIsa(atp)
263 *   	find the primitive type for atp
264 *
265 *****************************************************************************/
AsnFindBaseIsa(AsnTypePtr atp)266 NLM_EXTERN Int2 LIBCALL AsnFindBaseIsa (AsnTypePtr atp)
267 {
268 	if (atp == NULL)
269 		return -1;
270 
271 	if (ISA_BASETYPE(atp->isa))
272 		return atp->isa;
273 
274 	if (atp->type == NULL)
275 		return -1;
276 
277 	while (! ISA_BASETYPE(atp->type->isa))
278 	{
279 		atp = atp->type;
280 		if (atp->type == NULL)
281 			return -1;
282 	}
283 	return atp->type->isa;
284 }
285 
286 /*****************************************************************************
287 *
288 *   Boolean AsnTypeValidateOut(aip, atp, dvp)
289 *   	if not open or closing a SET, SEQ, SETOF, SEQOF, will
290 *   		put atp in aip->typestack if it validates.
291 *
292 *   KNOWN DEFECT:  SET should check for 1 of each non-optional element
293 *   	It does not right now, it only checks that an element is a valid
294 *   	member of the SET.
295 *
296 *****************************************************************************/
AsnTypeValidateOut(AsnIoPtr aip,AsnTypePtr atp,DataValPtr dvp)297 NLM_EXTERN Boolean AsnTypeValidateOut (AsnIoPtr aip, AsnTypePtr atp, DataValPtr dvp)
298 {
299 	Int2 isa;
300 	AsnTypePtr atp2, parent_type, base_type, curr_type;
301 	Boolean foundit;
302 	AsnValxNodePtr avnp;
303 
304 	if ((aip == NULL) || (atp == NULL))
305 		return FALSE;
306 
307 	curr_type = aip->typestack[aip->type_indent].type;
308 	base_type = AsnFindBaseType(atp);
309 	if (base_type == NULL)
310 	{
311 		AsnIoErrorMsg(aip, 10, AsnErrGetTypeName(atp->name), "AsnTypeValidateOut");
312 		return FALSE;
313 	}
314 
315 	isa = base_type->type->isa;
316 
317 	if (((isa == SEQ_TYPE) || (isa == SET_TYPE) ||
318 		 (isa == SEQOF_TYPE) || (isa == SETOF_TYPE))
319 		 && (dvp->intvalue == END_STRUCT))
320 	{
321 		switch (isa)
322 		{
323 			case SEQOF_TYPE:
324 			case SETOF_TYPE:
325 				break;
326 			case SEQ_TYPE:       /* check that no more waiting */
327 				if (curr_type != NULL)   /* check next one */
328 					atp2 = curr_type->next;
329 				else                     /* empty sequence written */
330 					atp2 = (AsnTypePtr) base_type->branch;
331 				while (atp2 != NULL)
332 				{
333 					if (! (atp2->optional || atp2->hasdefault))
334 					{
335 						AsnIoErrorMsg(aip, 7, AsnErrGetTypeName(atp2->name), AsnErrGetTypeName(atp->name));
336 						return FALSE;
337 					}
338 					atp2 = atp2->next;
339 				}
340 				break;
341 			default:
342 				break;
343 		}
344 		return TRUE;
345 	}
346 
347 	if (aip->type_indent)       /* part of a larger struct */
348 	{
349 		foundit = FALSE;
350 		if (aip->type_indent < 1)
351 			return FALSE;
352 
353 		parent_type = aip->typestack[aip->type_indent - 1].type;
354 		base_type = AsnFindBaseType(parent_type);
355 		if (base_type == NULL)
356 			return FALSE;
357 		isa = base_type->type->isa;
358 		atp2 = (AsnTypePtr) base_type->branch;
359 		while (atp2 != NULL)
360 		{
361 			if (atp2 == atp)
362 			{
363 				foundit = TRUE;
364 				break;
365 			}
366 			atp2 = atp2->next;
367 		}
368 		if (! foundit)
369 		{
370 			atp2 = AsnFindBaseType(atp);
371 			AsnIoErrorMsg(aip, 22, AsnErrGetTypeName(atp->name),
372 				AsnErrGetTypeName(atp2->name), AsnErrGetTypeName(parent_type->name));
373 			return FALSE;
374 		}
375 		switch (isa)
376 		{
377 			case SETOF_TYPE:       /* just has to be right type */
378 			case SEQOF_TYPE:
379 				break;
380 			case CHOICE_TYPE:      /* can only have one value */
381 				if (curr_type != NULL)
382 				{
383 					AsnIoErrorMsg(aip, 23, AsnErrGetTypeName(parent_type->name),
384 						AsnErrGetTypeName(atp->name));
385 					return FALSE;
386 				}
387 				break;
388 			case SET_TYPE:         /* SET should check for 1 of each */
389 				                   /* non-optional element, but doesn't */
390 				if (curr_type == atp)
391 				{
392 					AsnIoErrorMsg(aip, 24, AsnErrGetTypeName(atp->name),
393 						AsnErrGetTypeName(parent_type->name));
394 					return FALSE;
395 				}
396 				break;
397 			case SEQ_TYPE:
398 				if (curr_type == atp)
399 				{
400 					AsnIoErrorMsg(aip, 24, AsnErrGetTypeName(atp->name),
401 						AsnErrGetTypeName(parent_type->name));
402 					return FALSE;
403 				}
404 				 				 /* check preceeding elements */
405 				if (curr_type != NULL)
406 				{
407 					atp2 = (AsnTypePtr) base_type->branch;
408 					while (atp2 != curr_type->next)
409 					{
410 						if (atp == atp2)
411 						{
412 							AsnIoErrorMsg(aip, 6, AsnErrGetTypeName(atp->name),
413 								AsnErrGetTypeName(parent_type->name));
414 							return FALSE;
415 						}
416 						atp2 = atp2->next;
417 					}
418 				}
419 				else
420 					atp2 = (AsnTypePtr) base_type->branch;
421 				while ((atp2 != NULL) && (atp != atp2))
422 				{
423 					if (! (atp2->optional || atp2->hasdefault))
424 					{					/* skipped a non-optional element */
425 						AsnIoErrorMsg(aip, 7, AsnErrGetTypeName(atp2->name),
426 							AsnErrGetTypeName(parent_type->name));
427 						return FALSE;
428 					}
429 					atp2 = atp2->next;
430 				}
431 				if (atp2 == NULL)  /* element out of order */
432 				{
433 					AsnIoErrorMsg(aip, 6, AsnErrGetTypeName(atp->name),
434 						AsnErrGetTypeName(parent_type->name));
435 					return FALSE;
436 				}
437 				break;
438 			default:
439 				AsnIoErrorMsg(aip, 25, AsnErrGetTypeName(parent_type->name),
440 					parent_type->isa);
441 				return FALSE;
442 		}
443 		base_type = AsnFindBaseType(atp);
444 		isa = base_type->type->isa;
445 	}
446 
447 	if (ISA_STRINGTYPE(isa))
448 		isa = GENERALSTRING_TYPE;
449 
450 				/******************* maintain typestack ****************/
451 	switch (isa)          /* set aip->typestack for non-struct types */
452 	{					  /* this is done for them in AsnTypeSetIndent() */
453 		case SEQ_TYPE:
454 		case SET_TYPE:
455 		case SEQOF_TYPE:
456 		case SETOF_TYPE:
457 		case CHOICE_TYPE:
458 		default:				   /* terminal value */
459 			aip->typestack[aip->type_indent].type = atp;
460 			break;
461 	}
462 
463 	switch (isa)          /* check ranges and values */
464 	{
465 		case ENUM_TYPE:
466 			avnp = (AsnValxNodePtr) base_type->branch;
467 			while (avnp != NULL)
468 			{
469 				if (avnp->intvalue == dvp->intvalue)
470 					return TRUE;
471 				avnp = avnp->next;
472 			}
473 			AsnIoErrorMsg(aip, 12, dvp->intvalue, AsnErrGetTypeName(atp->name));
474 			return FALSE;
475 		default :
476 			break;
477 	}
478 	return TRUE;
479 }
480 
481 /*****************************************************************************
482 *
483 *   AsnTypePathFind(amp, str, countptr)
484 *   	like AsnTypeFind() but allocates an array of AsnTypePtr and fills
485 *       it in with the proper path.  Sets countptr to sizeof array.
486 *       Returns array or NULL on failure
487 *
488 *****************************************************************************/
AsnTypePathFind(AsnModulePtr amp,CharPtr str,Int2Ptr countptr)489 NLM_EXTERN AsnTypePtr PNTR LIBCALL  AsnTypePathFind (AsnModulePtr amp, CharPtr str, Int2Ptr countptr)
490 {
491 	AsnModulePtr amp2;
492 	AsnTypePtr atp;
493 	Int2 count;
494 	AsnTypePtr PNTR typeptr;
495 	CharPtr ptr;
496 
497 	*countptr = 0;
498 	count = 1;
499 	ptr = str;
500 	while (*ptr != '\0')
501 	{
502 		if (*ptr == '.')
503 			count++;
504 		ptr++;
505 	}
506 	typeptr = (AsnTypePtr*) MemNew(sizeof(AsnTypePtr) * count);
507 
508     if (amp == NULL)
509 	{
510 		if (amps == NULL)
511 		{
512 			AsnIoErrorMsg(NULL, (Int2)105);
513 			return NULL;
514 		}
515         amp = (AsnModulePtr) amps->data.ptrvalue;
516 	}
517 
518 	amp2 = amp;
519 	atp = NULL;
520 
521 	while (amp2 != NULL)
522 	{
523 		atp = AsnTypeFindType(amp2->types, str, typeptr, count, FALSE);
524 		if (atp != NULL)
525 		{
526 			*countptr = count;
527 			return typeptr;
528 		}
529 		else
530 			amp2 = amp2->next;
531 	}
532 
533 	MemFree(typeptr);
534 	return NULL;       /* not found */
535 }
536 
537 
538 /*****************************************************************************
539 *
540 *   AsnTypePtr AsnTypeFind(amp, str)
541 *   	finds a type in a module or set of modules
542 *   	str is a path with dot separation.
543 *   	E is used to indicate an element of SEQOF or SETOF
544 *   	looks for partial paths as well
545 *
546 *   if called with amp == NULL
547 *       searches all loaded modules
548 *   returns a single node at the end of the path
549 *
550 *****************************************************************************/
AsnTypeFind(AsnModulePtr amp,CharPtr str)551 NLM_EXTERN AsnTypePtr LIBCALL  AsnTypeFind (AsnModulePtr amp, CharPtr str)
552 {
553 	AsnModulePtr amp2;
554 	AsnTypePtr atp;
555 
556     if (amp == NULL)
557 	{
558 		if (amps == NULL)
559 		{
560 			AsnIoErrorMsg(NULL, (Int2)105);
561 			return NULL;
562 		}
563         amp = (AsnModulePtr) amps->data.ptrvalue;
564 	}
565 
566 	amp2 = amp;
567 	atp = NULL;
568 
569 	while (amp2 != NULL)
570 	{
571 		atp = AsnTypeFindType(amp2->types, str, NULL, 0, FALSE);
572 		if (atp != NULL)
573 			return atp;
574 		else
575 			amp2 = amp2->next;
576 	}
577 
578 	return atp;       /* not found */
579 }
580 
581 /*****************************************************************************
582 *
583 *   AsnTypePtr AsnTypeFindType(atp, str, typeptr, count, in_it)
584 *
585 *****************************************************************************/
AsnTypeFindType(AsnTypePtr atp,CharPtr str,AsnTypePtr PNTR typeptr,Int2 count,Boolean in_it)586 NLM_EXTERN AsnTypePtr AsnTypeFindType (AsnTypePtr atp, CharPtr str, AsnTypePtr PNTR typeptr, Int2 count, Boolean in_it)
587 {
588 	AsnTypePtr curr, next;
589 	CharPtr beg;
590 	Int2 len, isa;
591 	Boolean is_element,       /* (unamed) element of setof seqof */
592 		is_type,              /* if a type, must find the original def */
593 		got_it;
594 	AsnTypePtr PNTR typenext;
595 	Int2 newcount;
596 
597 	if (count < 0)      /* off array ... error condition */
598 		return NULL;
599 
600 	beg = str;
601 	len = 0;
602 	if (typeptr == NULL)
603 	{
604 		typenext = NULL;
605 		newcount = 0;
606 	}
607 	else
608 	{
609 		newcount = count - 1;
610 		if (newcount >= 0)
611 			typenext = typeptr + 1;
612 		else
613 			typenext = NULL;
614 	}
615 	is_element = FALSE;
616 	is_type = FALSE;
617 	while ((*beg != '.') && (*beg != '\0'))
618 	{
619 		beg++; len++;
620 	}
621 
622 	if ((*str == 'E') && (len == 1))
623 		is_element = TRUE;
624 	else if (IS_UPPER(*str))
625 		is_type = TRUE;
626 
627 	curr = atp;			  	/* look at this level */
628 	while (curr != NULL)
629 	{
630 		got_it = FALSE;
631 		if (is_element)
632 			got_it = TRUE;
633 		else if (StrMatch(curr->name, str, len))   /* found it */
634 		{
635 			if (! is_type)
636 				got_it = TRUE;
637 			else                    /* check that this is the real def */
638 			{
639 				if (! curr->imported)
640 					got_it = TRUE;
641 			}
642 		}
643 
644 		if (got_it)
645 		{
646 			if (typeptr != NULL)
647 				*typeptr = curr;
648 
649 			if (*beg == '\0')    /* that's it */
650 				return curr;
651 			else                 /* go down the path */
652 			{
653 				next = AsnFindBaseType(curr);
654 				isa = next->type->isa;
655 				if ((next->branch != NULL)   /* go further */
656 					&& (isa != INTEGER_TYPE)
657 					&& (isa != ENUM_TYPE))
658 				{
659 					next = AsnTypeFindType((AsnTypePtr) next->branch, (beg + 1), typenext, newcount, TRUE);
660 					if (next != NULL)
661 						return next;
662 				}
663 			}
664 		}
665 		curr = curr->next;
666 	}
667 
668 	if (in_it)     /* processing a path and can't find next node here */
669 		return NULL;
670 
671 	curr = atp;           /* start down a level */
672 	while (curr != NULL)
673 	{
674 		if (curr->branch != NULL)
675 		{
676 			next = AsnFindBaseType(curr);
677 
678 			if ((next != NULL) && (next->type != NULL))
679 			{
680 				isa = next->type->isa;
681 				if ((next->branch != NULL)   /* go further */
682 					&& (isa != INTEGER_TYPE)
683 					&& (isa != ENUM_TYPE))
684 				{
685 					next = AsnTypeFindType((AsnTypePtr) next->branch, str, typenext, newcount, FALSE);
686 					if (next != NULL)
687 					{
688 						if (typeptr != NULL)
689 							*typeptr = curr;
690 						return next;
691 					}
692 				}
693 			}
694 			/** else, an error has occurred, unresolved branch **/
695 		}
696 		curr = curr->next;
697 	}
698 	return NULL;
699 }
700 
701 /*****************************************************************************
702 *
703 *   CharPtr AsnTypeDumpStack(str, aip)
704 *   	dump a typestack into str
705 *       returns a pointer to the '\0' at the end of the str
706 *
707 *****************************************************************************/
AsnTypeDumpStack(CharPtr str,AsnIoPtr aip)708 NLM_EXTERN CharPtr LIBCALL  AsnTypeDumpStack (CharPtr str, AsnIoPtr aip)
709 {
710     Int2 level, isa, lastnochoice;
711 
712     lastnochoice = -1;
713     *str = '\0';
714 
715 	for(level = 0; level <= aip->type_indent; level++)
716 	{
717 		if (level == aip->type_indent)
718 		{
719 			if ((aip->type & ASNIO_OUT) ||
720 				((aip->type & ASNIO_IN) && (aip->read_id == FALSE)))
721 				str = StringMove(str, "<");
722 		}
723 		if (aip->typestack[level].type == NULL)
724 			str = StringMove(str, " ");
725 		else
726 		{
727 			isa = AsnFindBaseIsa(aip->typestack[level].type);
728             if (aip->typestack[level].type->name != NULL)
729                 str = StringMove(str, aip->typestack[level].type->name);
730             else if ((lastnochoice == SEQOF_TYPE) ||
731                         (lastnochoice == SETOF_TYPE))
732                 str = StringMove(str, "E");
733             else
734                 str = StringMove(str, "?");
735             if (isa != CHOICE_TYPE)
736                 lastnochoice = isa;
737 		}
738         if (level != aip->type_indent)
739             str = StringMove(str, ".");
740 		else
741 		{
742 			if ((aip->type & ASNIO_OUT) ||
743 				((aip->type & ASNIO_IN) && (aip->read_id == FALSE)))
744 				str = StringMove(str, ">");
745 		}
746 	}
747 	return str;
748 }
749 
750 /*****************************************************************************
751 *
752 *   Int4 AsnTypeStringToHex(from, len, to, left)
753 *   	converts an octet string to binary
754 *   	returns number of hex digits created if all ok
755 *       *left is chars left at the end of the buffer including first letter of
756 *          a remaining digit (from does not have an even number of letters)
757 *          since this could include white space, could be more than 1
758 *       returns a negative number on an error
759 *       skips over internal or trailing white space
760 *       left can be NULL in which case it is ignored
761 *
762 *****************************************************************************/
AsnTypeStringToHex(Pointer from,Int4 len,Pointer to,Int4Ptr left)763 NLM_EXTERN Int4 LIBCALL AsnTypeStringToHex (Pointer from, Int4 len, Pointer to, Int4Ptr left)
764 {
765 	BytePtr f, t;
766 	Byte octet = 0, value;
767 	int i;
768 	Int4 added = 0;
769 
770 	f = (BytePtr) from;
771 	t = (BytePtr) to;
772 	if (left != NULL)
773 		*left = 0;
774 	i = 16;
775 
776 	while (len)
777 	{
778 	     if (! IS_WHITESP(*f))  /* skip spaces */
779 	     {
780 		if (i == 16)       /* first letter of pair */
781 		{
782 			octet = 0;
783 			if (left != NULL)
784 				*left = len; /* point at it in case one letter left */
785 		}
786 
787 		value = TO_UPPER(*f);
788 		if ((value >= 'A') && (value <= 'F'))
789 			octet += (((value - 'A') + 10) * i);
790 		else if ((value >= '0') && (value <= '9'))
791 			octet += ((value - '0') * i);
792 		else
793 			return (Int4)(-1);
794 
795 		if (i == 16)     /* first letter of pair */
796 			i = 1;   /* goto second letter */
797 		else             /* letter pair was read */
798 		{
799 			i = 16;  /* reset for first letter */
800 			if (left != NULL)
801 				*left = 0;  /* nothing left so far */
802 			*t = octet;  /* record the hex digit */
803 			t++; added++;
804 		}
805 
806 	     }
807 	     len--;
808 	     f++;
809 	}
810 	return added;
811 }
812 
813 #ifdef ASNTYPE_RUNTIME_LINK
814 static Int2 tstack_max;
815 static AsnTypePtr PNTR tstack;
816 static Int2Ptr prev;
817 #endif
818 
819 /*****************************************************************************
820 *
821 *   AsnLinkType(type, localtype)
822 *      provides for linkage of imported types
823 *      if (type != NULL)
824 *         it's an element of type localtype
825 *         so, set type->type to localtype
826 *      else
827 *         it's localtype itself
828 *         so, return localtype
829 *      return pointer to correct type
830 *
831 *      ifdef'd OUT NOW THAT LINKING IS DONE AT LOAD TIME
832 *
833 *****************************************************************************/
AsnLinkType(AsnTypePtr type,AsnTypePtr localtype)834 NLM_EXTERN AsnTypePtr LIBCALL  AsnLinkType (AsnTypePtr type, AsnTypePtr localtype)
835 {
836 
837 	if (type == NULL)
838 		return localtype;
839 	else
840 #ifndef ASNTYPE_RUNTIME_LINK
841 		return type;        /* assume has been linked at tree load time */
842 #else
843 	{
844     Int2 i, j;
845     Int2Ptr oldprev;
846     AsnTypePtr PNTR oldtstack;
847 
848         for (i = 0; i < tstack_max; i++)
849 		{
850 	        if (tstack[i] == NULL)
851                 break;
852 		}
853 
854         if (i >= tstack_max)   /* increase stack size */
855         {
856             oldtstack = tstack;
857             oldprev = prev;
858             tstack = MemNew(((tstack_max + 20) * sizeof(AsnTypePtr)));
859             prev = MemNew(((tstack_max + 20) * sizeof(Int2)));
860             MemCopy(tstack, oldtstack, (size_t)(tstack_max * sizeof(AsnTypePtr)));
861             MemCopy(prev, oldprev, (size_t)(tstack_max * sizeof(Int2)));
862             MemFree(oldtstack);
863             MemFree(oldprev);
864             tstack_max += 20;
865         }
866 
867         j = type->tmp;
868         if (j)    /* previous type linked */
869             prev[i] = j;
870         tstack[i] = type->type;
871         type->tmp =  (i + 1);
872 		if (type != localtype)   /* don't link to itself */
873 			type->type = localtype;
874 		return type;
875 	}
876 #endif
877 }
878 
879 /*****************************************************************************
880 *
881 *   AsnUnlinkType(type)
882 *     disconnects local tree.
883 *
884 *****************************************************************************/
AsnUnlinkType(AsnTypePtr type)885 NLM_EXTERN void LIBCALL  AsnUnlinkType (AsnTypePtr type)
886 {
887 #ifdef ASNTYPE_RUNTIME_LINK
888     Int2 j;
889 
890 	if (type != NULL)
891 	{
892         j = type->tmp;
893         j--;
894 		type->type = tstack[j];
895         if (prev[j])
896             type->tmp = prev[j];
897         else
898     		type->tmp = 0;
899         tstack[j] = NULL;
900         prev[j] = 0;
901 	}
902 #endif
903 	return;
904 }
905 
AddXMLname(AsnTypePtr atp,AsnTypePtr PNTR typestack,Int2 stackptr)906 static void AddXMLname(AsnTypePtr atp, AsnTypePtr PNTR typestack, Int2 stackptr)
907 {
908 	Char buf[80];
909 	AsnTypePtr atp2;
910 	Boolean found=FALSE, getparent=TRUE, doitem = FALSE, done = FALSE;
911 	CharPtr tmp;
912 	Int2 i, isa;
913 
914 	if (atp->XMLname == NULL)   /* only do it once */
915 	{
916 		tmp = buf; *tmp = '\0';
917 		if (atp->name == NULL)
918 		{
919 			doitem = TRUE;
920 		}
921 		else if (IS_UPPER(*(atp->name)))
922 		{
923 			getparent = FALSE;
924 			tmp = StringMove(tmp, atp->name);
925 		}
926 
927 		if ((getparent) && (stackptr))
928 		{
929 			atp2 = typestack[stackptr - 1];
930 			isa = AsnFindBaseIsa(atp2);
931 			if ((doitem) && ((isa == SEQOF_TYPE) || (isa == SETOF_TYPE)))
932 			{
933 				atp2 = AsnFindBaseTypeDTD(atp);
934 				if (atp2 != NULL)
935 				{
936 				if (atp2->name == NULL)
937 				{
938 					atp2 = typestack[stackptr - 1];
939 					if (atp->type != NULL)
940 					{
941 						if (ISA_BASETYPE(atp->type->isa))
942 						{
943 							if (atp2->XMLname != NULL)
944 							{
945 								tmp = StringMove(tmp, atp2->XMLname);
946 								getparent = FALSE;
947 								found = TRUE;
948 							}
949 						}
950 					}
951 				}
952 				else
953 				{
954 					if (atp2->XMLname != NULL)
955 						tmp = StringMove(tmp, atp2->XMLname);
956 					else if (atp2->name != NULL)
957 						tmp = StringMove(tmp, atp2->name);
958 					if (*buf != '\0')
959 					{
960 						doitem = FALSE;
961 						getparent = FALSE;
962 						done = TRUE;
963 						found = TRUE;
964 					}
965 				}
966 				}
967 			}
968 
969 			atp2 = typestack[stackptr - 1];
970 			if ((getparent) && (atp2->XMLname != NULL))  /* already nested */
971 			{
972 				tmp = StringMove(tmp, atp2->XMLname);
973 				tmp = StringMove(tmp, "_");
974 				getparent = FALSE;
975 				found = TRUE;
976 			}
977 
978 			if ((stackptr) && (getparent))
979 			{
980 				found = FALSE;
981 
982 				for (i = (stackptr - 1); (i >= 0) && (! found); i--)
983 				{
984 					atp2 = AsnFindBaseTypeDTD(typestack[i]);
985 					if ((atp2 != NULL) && (atp2->name != NULL) && (IS_UPPER(*(atp2->name))))
986 					{
987 						while (i < stackptr)
988 						{
989 							atp2 = AsnFindBaseTypeDTD(typestack[i]);
990 							if (atp2->name == NULL)
991 								tmp = StringMove(tmp, "E");
992 							else
993 								tmp = StringMove(tmp, atp2->name);
994 							tmp = StringMove(tmp, "_");
995 							i++;
996 						}
997 						found = TRUE;
998 					}
999 				}
1000 			}
1001 
1002 			if ((! done) && found)
1003 			{
1004 				if (doitem)
1005 					tmp = StringMove(tmp, "_E");
1006 				else
1007 					tmp = StringMove(tmp, atp->name);
1008 			}
1009 		}
1010 
1011 
1012 		if (*buf != '\0')
1013 		{
1014 			atp->XMLname = StringSave(buf);
1015 		}
1016 	}
1017 
1018 	atp2 = AsnFindBaseTypeDTD(atp);
1019 	if ((! atp->imported) && (atp2 != NULL) && (atp2->type != NULL))
1020 	{
1021 		isa = atp2->type->isa;
1022 		if ((isa == SEQ_TYPE) || (isa == SET_TYPE))
1023 		{
1024 			typestack[stackptr] = atp;
1025 			for (atp2 = atp->branch; atp2 != NULL; atp2 = atp2->next)
1026 				AddXMLname(atp2, typestack, (Int2)(stackptr + 1));
1027 		}
1028 		else if ((isa == SEQOF_TYPE) || (isa == SETOF_TYPE))
1029 		{
1030 			typestack[stackptr] = atp;
1031 			for (atp2 = atp->branch; atp2 != NULL; atp2 = atp2->next)
1032 				AddXMLname(atp2, typestack, (Int2)(stackptr + 1));
1033 		}
1034 		else if (isa == CHOICE_TYPE)
1035 		{
1036 			typestack[stackptr] = atp;
1037 			for (atp2 = atp->branch; atp2 != NULL; atp2 = atp2->next)
1038 				AddXMLname(atp2, typestack, (Int2)(stackptr + 1));
1039 		}
1040 	}
1041 
1042 	return;
1043 
1044 }
1045 /*****************************************************************************
1046 *
1047 *   void AsnModuleLink(amp)
1048 *   	links IMPORT types with EXPORTS from other modules
1049 *   	if cannot link an IMPORT, no errors are generated
1050 *
1051 *****************************************************************************/
AsnModuleLink(AsnModulePtr amp)1052 NLM_EXTERN void LIBCALL  AsnModuleLink (AsnModulePtr amp)
1053 {
1054 	AsnTypePtr atp, atp2, typestack[10];
1055 	AsnModulePtr currmod, mod;
1056 	Boolean found;
1057 
1058 	currmod = amp;
1059 	while (currmod != NULL)
1060 	{
1061 		atp = currmod->types;
1062 		while (atp != NULL)
1063 		{
1064 			if ((atp->imported) && (atp->type == NULL)) /* link imported types */
1065 			{
1066 				mod = amp;
1067 				if (mod == currmod)
1068 					mod = mod->next;
1069 				found = FALSE;
1070 				while ((mod != NULL) && (! found))
1071 				{
1072 					atp2 = mod->types;
1073 					while ((atp2 != NULL) && (! found))
1074 					{
1075 						if ((! atp2->imported) &&
1076                         	(! StringCmp(atp2->name, atp->name)))
1077 						{
1078 							if (atp2->exported)
1079 							{
1080 								atp->type = atp2;
1081 								found = TRUE;
1082 							}
1083 							else
1084 							{
1085 								AsnIoErrorMsg(NULL, 84,	AsnErrGetTypeName(atp2->name),
1086 									mod->modulename);
1087 								atp2 = atp2->next;
1088 							}
1089 						}
1090 						else
1091 							atp2 = atp2->next;
1092 					}
1093 					mod = mod->next;
1094 					if (mod == currmod)
1095 						mod = mod->next;
1096 				}
1097 			}
1098 			atp = atp->next;
1099 		}
1100 		currmod = currmod->next;
1101 	}
1102 
1103 	/*** Fill in the XML names *********/
1104 
1105 	currmod = amp;
1106 	while (currmod != NULL)
1107 	{
1108 		atp = currmod->types;
1109 		while (atp != NULL)
1110 		{
1111 			AddXMLname(atp, typestack, (Int2)0);
1112 			atp = atp->next;
1113 		}
1114 		currmod = currmod->next;
1115 	}
1116 
1117 
1118 	return;
1119 }
1120 
1121 /*****************************************************************************
1122 *
1123 *   AsnStoreTree(file, amp)
1124 *
1125 *****************************************************************************/
AsnStoreTree(CharPtr file,AsnModulePtr amp)1126 NLM_EXTERN void LIBCALL AsnStoreTree (CharPtr file, AsnModulePtr amp)
1127 {
1128     ValNodePtr anp, last;
1129     AsnModulePtr tamp, tamp2, newamp;
1130     Int2 numamp;
1131 
1132                     /* check to see if we already have it (could happen) */
1133 
1134     anp = names;
1135     last = NULL;
1136     while (anp != NULL)
1137     {
1138         if (! StringCmp((CharPtr)anp->data.ptrvalue, file))
1139             return;   /* have it */
1140         last = anp;
1141         anp = anp->next;
1142     }
1143 
1144                     /* add the name */
1145     anp = ValNodeNew(last);
1146     if (last == NULL)
1147         names = anp;
1148     anp->data.ptrvalue = (Pointer) StringSave(file);
1149 
1150     last = NULL;
1151     anp = amps;
1152     while (anp != NULL)
1153     {
1154         last = anp;
1155         anp = anp->next;
1156     }
1157     anp = ValNodeNew(last);
1158     if (last == NULL)
1159         amps = anp;
1160 
1161     tamp = amp;
1162     numamp = 0;
1163     while (tamp != NULL)
1164     {
1165         numamp++;
1166         tamp = tamp->next;
1167     }
1168     newamp = (AsnModulePtr) MemNew(numamp * sizeof(AsnModule));
1169     anp->data.ptrvalue = (Pointer) newamp;
1170     tamp = amp;
1171     tamp2 = newamp;
1172     while (numamp)
1173     {
1174         MemCopy(tamp2, tamp, sizeof(AsnModule));
1175         tamp2++; tamp++; numamp--;
1176     }
1177     if (last != NULL)
1178     {
1179         tamp = (AsnModulePtr)last->data.ptrvalue;
1180         while (tamp->next != NULL)
1181             tamp = tamp->next;
1182         tamp->next = newamp;    /* link it in to list copy */
1183     }
1184 
1185     tamp = (AsnModulePtr) amps->data.ptrvalue;
1186     AsnModuleLink(tamp);              /* connect the tree */
1187     return;
1188 }
1189 
1190 
1191 /*****************************************************************************
1192 *
1193 *   Pointer PointerDecode(index, avn, at)
1194 *
1195 *****************************************************************************/
PointerDecode(Int2 index,AsnValxNodePtr avn,AsnTypePtr at)1196 static Pointer PointerDecode (Int2 index, AsnValxNodePtr avn, AsnTypePtr at)
1197 {
1198     if (index == -32000)
1199         return NULL;
1200     if (index == -32001)
1201         return (Pointer) avn;
1202     if (index < 0)
1203     {
1204         index *= -1;
1205         return (Pointer) &avn[index];
1206     }
1207     return (Pointer) &at[index];
1208 }
1209 
1210 /*****************************************************************************
1211 *
1212 *   AsnTreeLoad(file, &avn, &at, &amp)
1213 *
1214 *****************************************************************************/
AsnTreeLoad(char * file,AsnValxNodePtr * avnptr,AsnTypePtr * atptr,AsnModulePtr * ampptr)1215 NLM_EXTERN Boolean LIBCALL  AsnTreeLoad (char *file, AsnValxNodePtr *avnptr, AsnTypePtr *atptr, AsnModulePtr *ampptr)
1216 {
1217     AsnValxNodePtr avn = NULL, avncurr;
1218     AsnTypePtr at = NULL, atcurr;
1219     AsnModulePtr amp = NULL, ampcurr;
1220     ValNodePtr anp, ampanp;
1221     int numval, numtype, nummod, i;
1222     FILE * fp;
1223     int isa, next;
1224     long intvalue;
1225     double realvalue;
1226     char name[PATH_MAX], fname[60];
1227     CharPtr ptr;
1228     int bools[5], tagclass, tagnumber, defaultvalue, type, branch;
1229 
1230                     /* check to see if we already have it (could happen) */
1231 
1232     anp = names;
1233     ampanp = amps;
1234     while (anp != NULL)
1235     {
1236         if (! StringCmp((CharPtr)anp->data.ptrvalue, file))
1237         {                               /* have it */
1238             if (* ampptr == NULL)       /* fill dynamic load */
1239             {
1240                 * ampptr = (AsnModulePtr) ampanp->data.ptrvalue;
1241                 * atptr = (* ampptr)->types;
1242                      /* don't really need avnptr filled in */
1243             }
1244             return TRUE;
1245         }
1246         ampanp = ampanp->next;
1247         anp = anp->next;
1248     }
1249 
1250 	if (! ProgMon("Load ASN.1 Trees"))
1251 		return FALSE;
1252 
1253     if (* ampptr != NULL)      /* static load, add to list */
1254     {
1255         AsnStoreTree(file, (* ampptr));
1256         return TRUE;        /* already loaded */
1257     }
1258 
1259     if (! FindPath("ncbi", "ncbi", "asnload", name, sizeof (name)))
1260 	{
1261 		AsnIoErrorMsg(NULL, 85);
1262         return FALSE;
1263 	}
1264 
1265     FileBuildPath (name, NULL, file);/* add file to path */
1266 
1267     fp = FileOpen(name, "r");
1268     if (fp == NULL)
1269     {
1270         AsnIoErrorMsg(NULL, 83, (CharPtr) name);
1271         return FALSE;
1272     }
1273 
1274     fscanf(fp, "%d %d %d", &numval, &numtype, &nummod);
1275 
1276     if (numval)
1277         avn = (AsnValxNodePtr) MemNew(numval * sizeof(AsnValxNode));
1278     at  = (AsnTypePtr)   MemNew(numtype * sizeof(AsnType));
1279     amp = (AsnModulePtr) MemNew(nummod  * sizeof(AsnModule));
1280 
1281     *avnptr = avn;
1282     *atptr = at;
1283     *ampptr = amp;
1284 
1285     avncurr = avn;
1286     for (i = 0; i < numval; i++)
1287     {
1288         fscanf(fp, "%d %s %ld %lf %d", &isa, name, &intvalue, &realvalue, &next);
1289         avncurr->valueisa = isa;
1290         if (*name != '-')
1291             avncurr->name = StringSave(name);
1292         avncurr->intvalue = (Int4)intvalue;
1293         avncurr->realvalue = (FloatHi)realvalue;
1294         avncurr->next = (AsnValxNodePtr) PointerDecode((Int2)next, avn, at);
1295         avncurr++;
1296     }
1297 
1298     atcurr = at;
1299     for (i = 0; i < numtype; i++)
1300     {
1301         fscanf(fp, "%d %s %d %d %d %d %d %d %d %d %d %d %d",
1302             &isa, name, &tagclass, &tagnumber, bools, bools+1, bools+2,
1303             bools+3, bools+4, &defaultvalue, &type, &branch, &next);
1304         atcurr->isa = isa;
1305         if (*name != '-')
1306         {
1307             atcurr->name = StringSave(name);
1308             ptr = atcurr->name;
1309             while (*ptr != '\0')
1310             {
1311                 if (*ptr == '!')
1312                     *ptr = ' ';
1313                 ptr++;
1314             }
1315         }
1316         atcurr->tagclass = (Uint1)tagclass;
1317         atcurr->tagnumber = tagnumber;
1318         atcurr->implicit = (Boolean)bools[0];
1319         atcurr->optional = (Boolean)bools[1];
1320         atcurr->hasdefault = (Boolean)bools[2];
1321         atcurr->exported = (Boolean)bools[3];
1322         atcurr->imported = (Boolean)bools[4];
1323         atcurr->defaultvalue = (AsnValxNodePtr)PointerDecode((Int2)defaultvalue, avn, at);
1324         atcurr->type = (AsnTypePtr)PointerDecode((Int2)type, avn, at);
1325         atcurr->branch = PointerDecode((Int2)branch, avn, at);
1326         atcurr->next = (AsnTypePtr)PointerDecode((Int2)next, avn,at);
1327         atcurr++;
1328     }
1329 
1330     ampcurr = amp;
1331     for (i = 0; i < nummod; i++)
1332     {
1333         fscanf(fp, "%s %s %d %d", name, fname, &type, &next);
1334         if (*name != '-')
1335             ampcurr->modulename = StringSave(name);
1336 		if (*fname != '-')
1337 			ampcurr->filename = StringSave(fname);
1338         ampcurr->types = (AsnTypePtr)PointerDecode((Int2)type, avn, at);
1339         if (next != -32000)
1340             ampcurr->next = &amp[next];
1341         ampcurr++;
1342     }
1343 
1344     FileClose(fp);
1345                                   /* store it in list */
1346     AsnStoreTree(file, (* ampptr));
1347 
1348     return TRUE;
1349 }
1350 
1351 /*****************************************************************************
1352 *
1353 *   AsnEnumStr(type, val)
1354 *
1355 *****************************************************************************/
AsnEnumStr(CharPtr type,Int2 val)1356 NLM_EXTERN CharPtr LIBCALL  AsnEnumStr (CharPtr type, Int2 val)
1357 {
1358     AsnTypePtr atp;
1359 
1360 	if (amps == NULL)
1361 	{
1362 		AsnIoErrorMsg(NULL, (Int2)105);
1363 		return NULL;
1364 	}
1365     atp = AsnTypeFind((AsnModulePtr)amps->data.ptrvalue, type);
1366     return AsnEnumTypeStr(atp, val);
1367 }
1368 
1369 /*****************************************************************************
1370 *
1371 *   AsnEnumTypeStr(atp, val)
1372 *       returns string for ennumerated type val or a named integer
1373 *
1374 *****************************************************************************/
AsnEnumTypeStr(AsnTypePtr atp,Int2 val)1375 NLM_EXTERN CharPtr LIBCALL  AsnEnumTypeStr (AsnTypePtr atp, Int2 val)
1376 {
1377     AsnValxNodePtr avnp;
1378 
1379     atp = AsnFindBaseType(atp);
1380     if (atp == NULL)
1381         return NULL;
1382 
1383     if ((atp->type->isa != ENUM_TYPE) && (atp->type->isa != INTEGER_TYPE))
1384         return NULL;
1385 
1386 	avnp = (AsnValxNodePtr) atp->branch;
1387 	while (avnp != NULL)
1388 	{
1389 		if (val == (Int2) avnp->intvalue)
1390             return avnp->name;
1391 		else
1392 			avnp = avnp->next;
1393 	}
1394     return NULL;
1395 }
1396 
1397 /*****************************************************************************
1398 *
1399 *   AsnAllModPtr()
1400 *      returns a pointer to the start of all modules in memory
1401 *
1402 *****************************************************************************/
AsnAllModPtr(void)1403 NLM_EXTERN AsnModulePtr LIBCALL  AsnAllModPtr (void)
1404 {
1405     if (amps == NULL)
1406         return NULL;
1407     return (AsnModulePtr)amps->data.ptrvalue;
1408 }
1409 
1410 
1411 
1412