STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk, int (*cb)())
	{
	X509_INFO *xi=NULL;
	char *name=NULL,*header=NULL,**pp;
	unsigned char *data=NULL,*p;
	long len,error=0;
	int ok=0;
	STACK_OF(X509_INFO) *ret=NULL;
	unsigned int i,raw;
	char *(*d2i)();

	if (sk == NULL)
		{
		if ((ret=sk_X509_INFO_new_null()) == NULL)
			{
			PEMerr(PEM_F_PEM_X509_INFO_READ_BIO,ERR_R_MALLOC_FAILURE);
			goto err;
			}
		}
	else
		ret=sk;

	if ((xi=X509_INFO_new()) == NULL) goto err;
	for (;;)
		{
		raw=0;
		i=PEM_read_bio(bp,&name,&header,&data,&len);
		if (i == 0)
			{
			error=ERR_GET_REASON(ERR_peek_error());
			if (error == PEM_R_NO_START_LINE)
				{
				ERR_clear_error();
				break;
				}
			goto err;
			}
start:
		if (	(strcmp(name,PEM_STRING_X509) == 0) ||
			(strcmp(name,PEM_STRING_X509_OLD) == 0))
			{
			d2i=(char *(*)())d2i_X509;
			if (xi->x509 != NULL)
				{
				if (!sk_X509_INFO_push(ret,xi)) goto err;
				if ((xi=X509_INFO_new()) == NULL) goto err;
				goto start;
				}
			pp=(char **)&(xi->x509);
			}
		else if (strcmp(name,PEM_STRING_X509_CRL) == 0)
			{
			d2i=(char *(*)())d2i_X509_CRL;
			if (xi->crl != NULL)
				{
				if (!sk_X509_INFO_push(ret,xi)) goto err;
				if ((xi=X509_INFO_new()) == NULL) goto err;
				goto start;
				}
			pp=(char **)&(xi->crl);
			}
		else
#ifndef NO_RSA
			if (strcmp(name,PEM_STRING_RSA) == 0)
			{
			d2i=(char *(*)())d2i_RSAPrivateKey;
			if (xi->x_pkey != NULL) 
				{
				if (!sk_X509_INFO_push(ret,xi)) goto err;
				if ((xi=X509_INFO_new()) == NULL) goto err;
				goto start;
				}

			xi->enc_data=NULL;
			xi->enc_len=0;

			xi->x_pkey=X509_PKEY_new();
			if ((xi->x_pkey->dec_pkey=EVP_PKEY_new()) == NULL)
				goto err;
			xi->x_pkey->dec_pkey->type=EVP_PKEY_RSA;
			pp=(char **)&(xi->x_pkey->dec_pkey->pkey.rsa);
			if ((int)strlen(header) > 10) /* assume encrypted */
				raw=1;
			}
		else
#endif
#ifndef NO_DSA
			if (strcmp(name,PEM_STRING_DSA) == 0)
			{
			d2i=(char *(*)())d2i_DSAPrivateKey;
			if (xi->x_pkey != NULL) 
				{
				if (!sk_X509_INFO_push(ret,xi)) goto err;
				if ((xi=X509_INFO_new()) == NULL) goto err;
				goto start;
				}

			xi->enc_data=NULL;
			xi->enc_len=0;

			xi->x_pkey=X509_PKEY_new();
			if ((xi->x_pkey->dec_pkey=EVP_PKEY_new()) == NULL)
				goto err;
			xi->x_pkey->dec_pkey->type=EVP_PKEY_DSA;
			pp=(char **)&(xi->x_pkey->dec_pkey->pkey.dsa);
			if ((int)strlen(header) > 10) /* assume encrypted */
				raw=1;
			}
		else
#endif
			{
			d2i=NULL;
			pp=NULL;
			}

		if (d2i != NULL)
			{
			if (!raw)
				{
				EVP_CIPHER_INFO cipher;

				if (!PEM_get_EVP_CIPHER_INFO(header,&cipher))
					goto err;
				if (!PEM_do_header(&cipher,data,&len,cb))
					goto err;
				p=data;
				if (d2i(pp,&p,len) == NULL)
					{
					PEMerr(PEM_F_PEM_X509_INFO_READ_BIO,ERR_R_ASN1_LIB);
					goto err;
					}
				}
			else
				{ /* encrypted RSA data */
				if (!PEM_get_EVP_CIPHER_INFO(header,
					&xi->enc_cipher)) goto err;
				xi->enc_data=(char *)data;
				xi->enc_len=(int)len;
				data=NULL;
				}
			}
		else	{
			/* unknown */
			}
		if (name != NULL) Free(name);
		if (header != NULL) Free(header);
		if (data != NULL) Free(data);
		name=NULL;
		header=NULL;
		data=NULL;
		}

	/* if the last one hasn't been pushed yet and there is anything
	 * in it then add it to the stack ... 
	 */
	if ((xi->x509 != NULL) || (xi->crl != NULL) ||
		(xi->x_pkey != NULL) || (xi->enc_data != NULL))
		{
		if (!sk_X509_INFO_push(ret,xi)) goto err;
		xi=NULL;
		}
	ok=1;
err:
	if (xi != NULL) X509_INFO_free(xi);
	if (!ok)
		{
		for (i=0; ((int)i)<sk_X509_INFO_num(ret); i++)
			{
			xi=sk_X509_INFO_value(ret,i);
			X509_INFO_free(xi);
			}
		if (ret != sk) sk_X509_INFO_free(ret);
		ret=NULL;
		}
		
	if (name != NULL) Free(name);
	if (header != NULL) Free(header);
	if (data != NULL) Free(data);
	return(ret);
	}