}
sec_asn1e_contents_length (const SEC_ASN1Template *theTemplate, void *src,			   PRBool disallowStreaming, PRBool *noheaderp){    unsigned long encode_kind, underlying_kind;    PRBool isExplicit, optional, universal, may_stream;    unsigned long len;    /*     * This function currently calculates the length in all cases     * except the following: when writing out the contents of a      * template that belongs to a state where it was a sub-template     * with the SEC_ASN1_MAY_STREAM bit set and it's parent had the     * optional bit set.  The information that the parent is optional     * and that we should return the length of 0 when that length is      * present since that means the optional field is no longer present.     * So we add the disallowStreaming flag which is passed in when     * writing the contents, but for all recursive calls to      * sec_asn1e_contents_length, we pass PR_FALSE, because this     * function correctly calculates the length for children templates     * from that point on.  Confused yet?  At least you didn't have     * to figure it out.  ;)  -javi     */    encode_kind = theTemplate->kind;    universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)		? PR_TRUE : PR_FALSE;    isExplicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;    encode_kind &= ~SEC_ASN1_EXPLICIT;    optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;    encode_kind &= ~SEC_ASN1_OPTIONAL;    PORT_Assert (!(isExplicit && universal));	/* bad templates */    may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;    encode_kind &= ~SEC_ASN1_MAY_STREAM;    /* Just clear this to get it out of the way; we do not need it here */    encode_kind &= ~SEC_ASN1_DYNAMIC;    encode_kind &= ~SEC_ASN1_NO_STREAM;    if( encode_kind & SEC_ASN1_CHOICE ) {      void *src2;      int indx = sec_asn1e_which_choice(src, theTemplate);      if( 0 == indx ) {        /* XXX set an error? "choice not found" */        /* state->top->status = encodeError; */        return 0;      }      src2 = (void *)((char *)src - theTemplate->offset + theTemplate[indx].offset);      return sec_asn1e_contents_length(&theTemplate[indx], src2,                                        PR_FALSE, noheaderp);    }    if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || !universal) {	/* XXX any bits we want to disallow (PORT_Assert against) here? */	theTemplate = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE);	if (encode_kind & SEC_ASN1_POINTER) {	    /*	     * XXX This used to PORT_Assert (encode_kind == SEC_ASN1_POINTER);	     * but that was too restrictive.  This needs to be fixed,	     * probably copying what the decoder now checks for, and	     * adding a big comment here to explain what the checks mean.	     * Alternatively, the check here could be omitted altogether	     * just letting sec_asn1e_init_state_based_on_template	     * do it, since that routine can do better error handling, too.	     */	    src = *(void **)src;	    if (src == NULL) {		if (optional)		    *noheaderp = PR_TRUE;		else 		    *noheaderp = PR_FALSE;		return 0;	    }	} else if (encode_kind & SEC_ASN1_INLINE) {	    /* check that there are no extraneous bits */	    PORT_Assert (encode_kind == SEC_ASN1_INLINE && !optional);	}	src = (char *)src + theTemplate->offset;	if (isExplicit) {	    len = sec_asn1e_contents_length (theTemplate, src, PR_FALSE,                                             noheaderp);	    if (len == 0 && optional) {		*noheaderp = PR_TRUE;	    } else if (*noheaderp) {		/* Okay, *we* do not want to add in a header, but our caller still does. */		*noheaderp = PR_FALSE;	    } else {		/* if the inner content exists, our length is		 * len(identifier) + len(length) + len(innercontent)		 * XXX we currently assume len(identifier) == 1;		 * to support a high-tag-number this would need to be smarter.		 */		len += 1 + SEC_ASN1LengthLength (len);	    }	    return len;	}	underlying_kind = theTemplate->kind;	underlying_kind &= ~SEC_ASN1_MAY_STREAM;	/* XXX Should we recurse here? */    } else {	underlying_kind = encode_kind;    }    /* This is only used in decoding; it plays no part in encoding.  */    if (underlying_kind & SEC_ASN1_SAVE) {	/* check that there are no extraneous bits */	PORT_Assert (underlying_kind == SEC_ASN1_SAVE);	*noheaderp = PR_TRUE;	return 0;    }    /* Having any of these bits is not expected here...  */    PORT_Assert ((underlying_kind & (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL				     | SEC_ASN1_INLINE | SEC_ASN1_POINTER				     | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM				     | SEC_ASN1_SAVE | SEC_ASN1_SKIP)) == 0);    if( underlying_kind & SEC_ASN1_CHOICE ) {      void *src2;      int indx = sec_asn1e_which_choice(src, theTemplate);      if( 0 == indx ) {        /* XXX set an error? "choice not found" */        /* state->top->status = encodeError; */        return 0;      }      src2 = (void *)((char *)src - theTemplate->offset + theTemplate[indx].offset);      len = sec_asn1e_contents_length(&theTemplate[indx], src2, PR_FALSE,                                      noheaderp);    } else    switch (underlying_kind) {      case SEC_ASN1_SEQUENCE_OF:      case SEC_ASN1_SET_OF:	{	    const SEC_ASN1Template *tmpt;	    void *sub_src;	    unsigned long sub_len;	    void **group;	    len = 0;	    group = *(void ***)src;	    if (group == NULL)		break;	    tmpt = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE);	    for (; *group != NULL; group++) {		sub_src = (char *)(*group) + tmpt->offset;		sub_len = sec_asn1e_contents_length (tmpt, sub_src, PR_FALSE,                                                     noheaderp);		len += sub_len;		/*		 * XXX The 1 below is the presumed length of the identifier;		 * to support a high-tag-number this would need to be smarter.		 */		if (!*noheaderp)		    len += 1 + SEC_ASN1LengthLength (sub_len);	    }	}	break;      case SEC_ASN1_SEQUENCE:      case SEC_ASN1_SET:	{	    const SEC_ASN1Template *tmpt;	    void *sub_src;	    unsigned long sub_len;	    len = 0;	    for (tmpt = theTemplate + 1; tmpt->kind; tmpt++) {		sub_src = (char *)src + tmpt->offset;		sub_len = sec_asn1e_contents_length (tmpt, sub_src, PR_FALSE,                                                     noheaderp);		len += sub_len;		/*		 * XXX The 1 below is the presumed length of the identifier;		 * to support a high-tag-number this would need to be smarter.		 */		if (!*noheaderp)		    len += 1 + SEC_ASN1LengthLength (sub_len);	    }	}	break;      case SEC_ASN1_BIT_STRING:	/* convert bit length to byte */	len = (((SECItem *)src)->len + 7) >> 3;	/* bit string contents involve an extra octet */	if (len)	    len++;	break;      case SEC_ASN1_INTEGER:	/* ASN.1 INTEGERs are signed.	 * If the source is an unsigned integer, the encoder will need 	 * to handle the conversion here.	 */	{	    unsigned char *buf = ((SECItem *)src)->data;	    SECItemType integerType = ((SECItem *)src)->type;	    len = ((SECItem *)src)->len;	    while (len > 0) {		if (*buf != 0) {		    if (*buf & 0x80 && integerType == siUnsignedInteger) {			len++; /* leading zero needed to make number signed */		    }		    break; /* reached beginning of number */		}		if (len == 1) {		    break; /* the number 0 */		}		if (buf[1] & 0x80) {		    break; /* leading zero already present */		} 		/* extraneous leading zero, keep going */		buf++;		len--;	    }	}	break;      default:	len = ((SECItem *)src)->len;	if (may_stream && len == 0 && !disallowStreaming)	    len = 1;	/* if we're streaming, we may have a secitem w/len 0 as placeholder */	break;    }    if ((len == 0 && optional) || underlying_kind == SEC_ASN1_ANY)	*noheaderp = PR_TRUE;    else 	*noheaderp = PR_FALSE;    return len;}