Commit a338e21b authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

New ASN1 functions that just deal with
content octets, not tag+length.
parent 5789f8f7
Loading
Loading
Loading
Loading
+12 −0
Original line number Original line Diff line number Diff line
@@ -4,6 +4,18 @@


 Changes between 0.9.5a and 0.9.6  [xx XXX 2000]
 Changes between 0.9.5a and 0.9.6  [xx XXX 2000]


  *) New ASN1 functions, i2c_* and c2i_* for INTEGER and BIT
     STRING types. These convert content octets to and from the
     underlying type. The actual tag and length octets are
     already assumed to have been read in and checked. These
     are needed because all other string types have virtually
     identical handling apart from the tag. By having versions
     of the ASN1 functions that just operate on content octets
     IMPLICIT tagging can be handled properly. It also allows
     the ASN1_ENUMERATED code to be cut down because ASN1_ENUMERATED
     and ASN1_INTEGER are identical apart from the tag.
     [Steve Henson]

  *) Change the handling of OID objects as follows:
  *) Change the handling of OID objects as follows:


     - New object identifiers are inserted in objects.txt, following
     - New object identifiers are inserted in objects.txt, following
+45 −17
Original line number Original line Diff line number Diff line
@@ -71,12 +71,26 @@ int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, unsigned char *d, int len)


int i2d_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp)
int i2d_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp)
{
{
	int ret,j,r,bits,len;
	int len, ret;
	len = i2c_ASN1_BIT_STRING(a, NULL);	
	ret=ASN1_object_size(0,len,V_ASN1_BIT_STRING);
	if(pp) {
		ASN1_put_object(pp,0,ret,V_ASN1_BIT_STRING,V_ASN1_UNIVERSAL);
		i2c_ASN1_BIT_STRING(a, pp);	
	}
	return ret;
}

int i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp)
	{
	int ret,j,bits,len;
	unsigned char *p,*d;
	unsigned char *p,*d;


	if (a == NULL) return(0);
	if (a == NULL) return(0);


	len=a->length;
	len=a->length;
	ret=1+len;
	if (pp == NULL) return(ret);


	if (len > 0)
	if (len > 0)
		{
		{
@@ -104,36 +118,27 @@ int i2d_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp)
		}
		}
	else
	else
		bits=0;
		bits=0;
	ret=1+len;
	r=ASN1_object_size(0,ret,V_ASN1_BIT_STRING);
	if (pp == NULL) return(r);
	p= *pp;
	p= *pp;


	ASN1_put_object(&p,0,ret,V_ASN1_BIT_STRING,V_ASN1_UNIVERSAL);
	*(p++)=(unsigned char)bits;
	*(p++)=(unsigned char)bits;
	d=a->data;
	d=a->data;
	memcpy(p,d,len);
	memcpy(p,d,len);
	p+=len;
	p+=len;
	if (len > 0) p[-1]&=(0xff<<bits);
	if (len > 0) p[-1]&=(0xff<<bits);
	*pp=p;
	*pp=p;
	return(r);
	return(ret);
	}
	}



/* Convert DER encoded ASN1 BIT_STRING to ASN1_BIT_STRING structure */
ASN1_BIT_STRING *d2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a, unsigned char **pp,
ASN1_BIT_STRING *d2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a, unsigned char **pp,
	     long length)
	     long length)
{
{
	ASN1_BIT_STRING *ret=NULL;
	unsigned char *p;
	unsigned char *p,*s;
	long len;
	long len;
	int inf,tag,xclass;
	int i;
	int i;

	int inf,tag,xclass;
	if ((a == NULL) || ((*a) == NULL))
	ASN1_BIT_STRING *ret;
		{
		if ((ret=M_ASN1_BIT_STRING_new()) == NULL) return(NULL);
		}
	else
		ret=(*a);


	p= *pp;
	p= *pp;
	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
@@ -149,7 +154,30 @@ ASN1_BIT_STRING *d2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a, unsigned char **pp,
		goto err;
		goto err;
		}
		}
	if (len < 1) { i=ASN1_R_STRING_TOO_SHORT; goto err; }
	if (len < 1) { i=ASN1_R_STRING_TOO_SHORT; goto err; }
	ret = c2i_ASN1_BIT_STRING(a, &p, len);
	if(ret) *pp = p;
	return ret;
err:
	ASN1err(ASN1_F_D2I_ASN1_BIT_STRING,i);
	return(NULL);

}

ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a, unsigned char **pp,
	     long len)
	{
	ASN1_BIT_STRING *ret=NULL;
	unsigned char *p,*s;
	int i;


	if ((a == NULL) || ((*a) == NULL))
		{
		if ((ret=M_ASN1_BIT_STRING_new()) == NULL) return(NULL);
		}
	else
		ret=(*a);

	p= *pp;
	i= *(p++);
	i= *(p++);
	/* We do this to preserve the settings.  If we modify
	/* We do this to preserve the settings.  If we modify
	 * the settings, via the _set_bit function, we will recalculate
	 * the settings, via the _set_bit function, we will recalculate
+21 −135
Original line number Original line Diff line number Diff line
@@ -71,88 +71,27 @@ ASN1_ENUMERATED *ASN1_ENUMERATED_new(void)
void ASN1_ENUMERATED_free(ASN1_ENUMERATED *x)
void ASN1_ENUMERATED_free(ASN1_ENUMERATED *x)
{ M_ASN1_ENUMERATED_free(x); }
{ M_ASN1_ENUMERATED_free(x); }


int i2d_ASN1_ENUMERATED(ASN1_ENUMERATED *a, unsigned char **pp)
	{
	int pad=0,ret,r,i,t;
	unsigned char *p,*n,pb=0;

	if ((a == NULL) || (a->data == NULL)) return(0);
	t=a->type;
	if (a->length == 0)
		ret=1;
	else
		{
		ret=a->length;
		i=a->data[0];
		if ((t == V_ASN1_ENUMERATED) && (i > 127)) {
			pad=1;
			pb=0;
		} else if(t == V_ASN1_NEG_ENUMERATED) {
			if(i>128) {
				pad=1;
				pb=0xFF;
			} else if(i == 128) {
				for(i = 1; i < a->length; i++) if(a->data[i]) {
						pad=1;
						pb=0xFF;
						break;
				}
			}
		}
		ret+=pad;
		}
	r=ASN1_object_size(0,ret,V_ASN1_ENUMERATED);
	if (pp == NULL) return(r);
	p= *pp;


	ASN1_put_object(&p,0,ret,V_ASN1_ENUMERATED,V_ASN1_UNIVERSAL);
int i2d_ASN1_ENUMERATED(ASN1_ENUMERATED *a, unsigned char **pp)
	if (pad) *(p++)=pb;
	if (a->length == 0)
		*(p++)=0;
	else if (t == V_ASN1_ENUMERATED)
{
{
		memcpy(p,a->data,(unsigned int)a->length);
	int len, ret;
		p+=a->length;
	len = i2c_ASN1_INTEGER(a, NULL);	
		}
	ret=ASN1_object_size(0,len,V_ASN1_ENUMERATED);
	else {
	if(pp) {
		/* Begin at the end of the encoding */
		ASN1_put_object(pp,0,ret,V_ASN1_ENUMERATED,V_ASN1_UNIVERSAL);
		n=a->data + a->length - 1;
		i2c_ASN1_INTEGER(a, pp);	
		p += a->length - 1;
		i = a->length;
		/* Copy zeros to destination as long as source is zero */
		while(!*n) {
			*(p--) = 0;
			n--;
			i--;
		}
		/* Complement and increment next octet */
		*(p--) = ((*(n--)) ^ 0xff) + 1;
		i--;
		/* Complement any octets left */
		for(;i > 0; i--) *(p--) = *(n--) ^ 0xff;
		p += a->length;
	}
	}

	return ret;
	*pp=p;
	return(r);
}
}


ASN1_ENUMERATED *d2i_ASN1_ENUMERATED(ASN1_ENUMERATED **a, unsigned char **pp,
ASN1_ENUMERATED *d2i_ASN1_ENUMERATED(ASN1_ENUMERATED **a, unsigned char **pp,
	     long length)
	     long length)
{
{
	ASN1_ENUMERATED *ret=NULL;
	unsigned char *p;
	unsigned char *p,*to,*s;
	long len;
	long len;
	int inf,tag,xclass;
	int i;
	int i;

	int inf,tag,xclass;
	if ((a == NULL) || ((*a) == NULL))
	ASN1_ENUMERATED *ret;
		{
		if ((ret=M_ASN1_ENUMERATED_new()) == NULL) return(NULL);
		ret->type=V_ASN1_ENUMERATED;
		}
	else
		ret=(*a);


	p= *pp;
	p= *pp;
	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
@@ -167,69 +106,16 @@ ASN1_ENUMERATED *d2i_ASN1_ENUMERATED(ASN1_ENUMERATED **a, unsigned char **pp,
		i=ASN1_R_EXPECTING_AN_ENUMERATED;
		i=ASN1_R_EXPECTING_AN_ENUMERATED;
		goto err;
		goto err;
		}
		}

	ret = c2i_ASN1_INTEGER(a, &p, len);
	/* We must OPENSSL_malloc stuff, even for 0 bytes otherwise it
	if(ret) {
	 * signifies a missing NULL parameter. */
		ret->type = (V_ASN1_NEG & ret->type) | V_ASN1_ENUMERATED;
	s=(unsigned char *)OPENSSL_malloc((int)len+1);
	if (s == NULL)
		{
		i=ERR_R_MALLOC_FAILURE;
		goto err;
		}
	to=s;
	if(!len) {
		/* Strictly speaking this is an illegal ENUMERATED but we
		 * tolerate it.
		 */
		ret->type=V_ASN1_ENUMERATED;
	} else if (*p & 0x80) /* a negative number */
		{
		ret->type=V_ASN1_NEG_ENUMERATED;
		if ((*p == 0xff) && (len != 1)) {
			p++;
			len--;
		}
		i = len;
		p += i - 1;
		to += i - 1;
		while((!*p) && i) {
			*(to--) = 0;
			i--;
			p--;
		}
		if(!i) {
			*s = 1;
			s[len] = 0;
			p += len;
			len++;
		} else {
			*(to--) = (*(p--) ^ 0xff) + 1;
			i--;
			for(;i > 0; i--) *(to--) = *(p--) ^ 0xff;
			p += len;
		}
	} else {
		ret->type=V_ASN1_ENUMERATED;
		if ((*p == 0) && (len != 1))
			{
			p++;
			len--;
			}
		memcpy(s,p,(int)len);
		p+=len;
	}

	if (ret->data != NULL) OPENSSL_free(ret->data);
	ret->data=s;
	ret->length=(int)len;
	if (a != NULL) (*a)=ret;
		*pp = p;
		*pp = p;
	return(ret);
	}
	return ret;
err:
err:
	ASN1err(ASN1_F_D2I_ASN1_ENUMERATED,i);
	ASN1err(ASN1_F_D2I_ASN1_ENUMERATED,i);
	if ((ret != NULL) && ((a == NULL) || (*a != ret)))
		M_ASN1_ENUMERATED_free(ret);
	return(NULL);
	return(NULL);

}
}


int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v)
int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v)
+59 −25
Original line number Original line Diff line number Diff line
@@ -72,8 +72,22 @@ ASN1_INTEGER *ASN1_INTEGER_dup(ASN1_INTEGER *x)
int ASN1_INTEGER_cmp(ASN1_INTEGER *x, ASN1_INTEGER *y)
int ASN1_INTEGER_cmp(ASN1_INTEGER *x, ASN1_INTEGER *y)
{ return M_ASN1_INTEGER_cmp(x,y);}
{ return M_ASN1_INTEGER_cmp(x,y);}


/* Output ASN1 INTEGER including tag+length */

int i2d_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
{
	int len, ret;
	len = i2c_ASN1_INTEGER(a, NULL);	
	ret=ASN1_object_size(0,len,V_ASN1_INTEGER);
	if(pp) {
		ASN1_put_object(pp,0,ret,V_ASN1_INTEGER,V_ASN1_UNIVERSAL);
		i2c_ASN1_INTEGER(a, pp);	
	}
	return ret;
}

/* 
/* 
 * This converts an ASN1 INTEGER into its DER encoding.
 * This converts an ASN1 INTEGER into its content encoding.
 * The internal representation is an ASN1_STRING whose data is a big endian
 * The internal representation is an ASN1_STRING whose data is a big endian
 * representation of the value, ignoring the sign. The sign is determined by
 * representation of the value, ignoring the sign. The sign is determined by
 * the type: V_ASN1_INTEGER for positive and V_ASN1_NEG_INTEGER for negative. 
 * the type: V_ASN1_INTEGER for positive and V_ASN1_NEG_INTEGER for negative. 
@@ -97,23 +111,23 @@ int ASN1_INTEGER_cmp(ASN1_INTEGER *x, ASN1_INTEGER *y)
 * followed by optional zeros isn't padded.
 * followed by optional zeros isn't padded.
 */
 */


int i2d_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
int i2c_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
	{
	{
	int pad=0,ret,r,i,t;
	int pad=0,ret,i,neg;
	unsigned char *p,*n,pb=0;
	unsigned char *p,*n,pb=0;


	if ((a == NULL) || (a->data == NULL)) return(0);
	if ((a == NULL) || (a->data == NULL)) return(0);
	t=a->type;
	neg=a->type & V_ASN1_NEG;
	if (a->length == 0)
	if (a->length == 0)
		ret=1;
		ret=1;
	else
	else
		{
		{
		ret=a->length;
		ret=a->length;
		i=a->data[0];
		i=a->data[0];
		if ((t == V_ASN1_INTEGER) && (i > 127)) {
		if (!neg && (i > 127)) {
			pad=1;
			pad=1;
			pb=0;
			pb=0;
		} else if(t == V_ASN1_NEG_INTEGER) {
		} else if(neg) {
			if(i>128) {
			if(i>128) {
				pad=1;
				pad=1;
				pb=0xFF;
				pb=0xFF;
@@ -131,14 +145,12 @@ int i2d_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
		}
		}
		ret+=pad;
		ret+=pad;
		}
		}
	r=ASN1_object_size(0,ret,V_ASN1_INTEGER);
	if (pp == NULL) return(ret);
	if (pp == NULL) return(r);
	p= *pp;
	p= *pp;


	ASN1_put_object(&p,0,ret,V_ASN1_INTEGER,V_ASN1_UNIVERSAL);
	if (pad) *(p++)=pb;
	if (pad) *(p++)=pb;
	if (a->length == 0) *(p++)=0;
	if (a->length == 0) *(p++)=0;
	else if (t == V_ASN1_INTEGER) memcpy(p,a->data,(unsigned int)a->length);
	else if (!neg) memcpy(p,a->data,(unsigned int)a->length);
	else {
	else {
		/* Begin at the end of the encoding */
		/* Begin at the end of the encoding */
		n=a->data + a->length - 1;
		n=a->data + a->length - 1;
@@ -157,30 +169,22 @@ int i2d_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
		for(;i > 0; i--) *(p--) = *(n--) ^ 0xff;
		for(;i > 0; i--) *(p--) = *(n--) ^ 0xff;
	}
	}


	*pp+=r;
	*pp+=ret;
	return(r);
	return(ret);
	}
	}


/* Convert DER encoded ASN1 INTEGER to ASN1_INTEGER structure */
ASN1_INTEGER *d2i_ASN1_INTEGER(ASN1_INTEGER **a, unsigned char **pp,
ASN1_INTEGER *d2i_ASN1_INTEGER(ASN1_INTEGER **a, unsigned char **pp,
	     long length)
	     long length)
{
{
	ASN1_INTEGER *ret=NULL;
	unsigned char *p;
	unsigned char *p,*to,*s, *pend;
	long len;
	long len;
	int inf,tag,xclass;
	int i;
	int i;

	int inf,tag,xclass;
	if ((a == NULL) || ((*a) == NULL))
	ASN1_INTEGER *ret;
		{
		if ((ret=M_ASN1_INTEGER_new()) == NULL) return(NULL);
		ret->type=V_ASN1_INTEGER;
		}
	else
		ret=(*a);


	p= *pp;
	p= *pp;
	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
	inf=ASN1_get_object(&p,&len,&tag,&xclass,length);
	pend = p + len;
	if (inf & 0x80)
	if (inf & 0x80)
		{
		{
		i=ASN1_R_BAD_OBJECT_HEADER;
		i=ASN1_R_BAD_OBJECT_HEADER;
@@ -192,6 +196,35 @@ ASN1_INTEGER *d2i_ASN1_INTEGER(ASN1_INTEGER **a, unsigned char **pp,
		i=ASN1_R_EXPECTING_AN_INTEGER;
		i=ASN1_R_EXPECTING_AN_INTEGER;
		goto err;
		goto err;
		}
		}
	ret = c2i_ASN1_INTEGER(a, &p, len);
	if(ret) *pp = p;
	return ret;
err:
	ASN1err(ASN1_F_D2I_ASN1_INTEGER,i);
	return(NULL);

}


/* Convert just ASN1 INTEGER content octets to ASN1_INTEGER structure */

ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a, unsigned char **pp,
	     long len)
	{
	ASN1_INTEGER *ret=NULL;
	unsigned char *p,*to,*s, *pend;
	int i;

	if ((a == NULL) || ((*a) == NULL))
		{
		if ((ret=M_ASN1_INTEGER_new()) == NULL) return(NULL);
		ret->type=V_ASN1_INTEGER;
		}
	else
		ret=(*a);

	p= *pp;
	pend = p + len;


	/* We must OPENSSL_malloc stuff, even for 0 bytes otherwise it
	/* We must OPENSSL_malloc stuff, even for 0 bytes otherwise it
	 * signifies a missing NULL parameter. */
	 * signifies a missing NULL parameter. */
@@ -261,6 +294,7 @@ err:
	return(NULL);
	return(NULL);
	}
	}



/* This is a version of d2i_ASN1_INTEGER that ignores the sign bit of
/* This is a version of d2i_ASN1_INTEGER that ignores the sign bit of
 * ASN1 integers: some broken software can encode a positive INTEGER
 * ASN1 integers: some broken software can encode a positive INTEGER
 * with its MSB set as negative (it doesn't add a padding zero).
 * with its MSB set as negative (it doesn't add a padding zero).
+10 −2
Original line number Original line Diff line number Diff line
@@ -86,11 +86,13 @@ extern "C" {


#define V_ASN1_APP_CHOOSE		-2	/* let the recipient choose */
#define V_ASN1_APP_CHOOSE		-2	/* let the recipient choose */


#define V_ASN1_NEG			0x100	/* negative flag */

#define V_ASN1_UNDEF			-1
#define V_ASN1_UNDEF			-1
#define V_ASN1_EOC			0
#define V_ASN1_EOC			0
#define V_ASN1_BOOLEAN			1	/**/
#define V_ASN1_BOOLEAN			1	/**/
#define V_ASN1_INTEGER			2
#define V_ASN1_INTEGER			2
#define V_ASN1_NEG_INTEGER		(2+0x100)
#define V_ASN1_NEG_INTEGER		(2 | V_ASN1_NEG)
#define V_ASN1_BIT_STRING		3
#define V_ASN1_BIT_STRING		3
#define V_ASN1_OCTET_STRING		4
#define V_ASN1_OCTET_STRING		4
#define V_ASN1_NULL			5
#define V_ASN1_NULL			5
@@ -99,7 +101,7 @@ extern "C" {
#define V_ASN1_EXTERNAL			8
#define V_ASN1_EXTERNAL			8
#define V_ASN1_REAL			9
#define V_ASN1_REAL			9
#define V_ASN1_ENUMERATED		10
#define V_ASN1_ENUMERATED		10
#define V_ASN1_NEG_ENUMERATED		(10+0x100)
#define V_ASN1_NEG_ENUMERATED		(10 | V_ASN1_NEG)
#define V_ASN1_UTF8STRING		12
#define V_ASN1_UTF8STRING		12
#define V_ASN1_SEQUENCE			16
#define V_ASN1_SEQUENCE			16
#define V_ASN1_SET			17
#define V_ASN1_SET			17
@@ -526,8 +528,11 @@ unsigned char * ASN1_STRING_data(ASN1_STRING *x);
ASN1_BIT_STRING *	ASN1_BIT_STRING_new(void);
ASN1_BIT_STRING *	ASN1_BIT_STRING_new(void);
void		ASN1_BIT_STRING_free(ASN1_BIT_STRING *a);
void		ASN1_BIT_STRING_free(ASN1_BIT_STRING *a);
int		i2d_ASN1_BIT_STRING(ASN1_BIT_STRING *a,unsigned char **pp);
int		i2d_ASN1_BIT_STRING(ASN1_BIT_STRING *a,unsigned char **pp);
int		i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a,unsigned char **pp);
ASN1_BIT_STRING *d2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,unsigned char **pp,
ASN1_BIT_STRING *d2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,unsigned char **pp,
			long length);
			long length);
ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,unsigned char **pp,
			long length);
int		ASN1_BIT_STRING_set(ASN1_BIT_STRING *a, unsigned char *d,
int		ASN1_BIT_STRING_set(ASN1_BIT_STRING *a, unsigned char *d,
			int length );
			int length );
int		ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, int value);
int		ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, int value);
@@ -547,8 +552,11 @@ int d2i_ASN1_BOOLEAN(int *a,unsigned char **pp,long length);
ASN1_INTEGER *	ASN1_INTEGER_new(void);
ASN1_INTEGER *	ASN1_INTEGER_new(void);
void		ASN1_INTEGER_free(ASN1_INTEGER *a);
void		ASN1_INTEGER_free(ASN1_INTEGER *a);
int		i2d_ASN1_INTEGER(ASN1_INTEGER *a,unsigned char **pp);
int		i2d_ASN1_INTEGER(ASN1_INTEGER *a,unsigned char **pp);
int		i2c_ASN1_INTEGER(ASN1_INTEGER *a,unsigned char **pp);
ASN1_INTEGER *d2i_ASN1_INTEGER(ASN1_INTEGER **a,unsigned char **pp,
ASN1_INTEGER *d2i_ASN1_INTEGER(ASN1_INTEGER **a,unsigned char **pp,
			long length);
			long length);
ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a,unsigned char **pp,
			long length);
ASN1_INTEGER *d2i_ASN1_UINTEGER(ASN1_INTEGER **a,unsigned char **pp,
ASN1_INTEGER *d2i_ASN1_UINTEGER(ASN1_INTEGER **a,unsigned char **pp,
			long length);
			long length);
ASN1_INTEGER *	ASN1_INTEGER_dup(ASN1_INTEGER *x);
ASN1_INTEGER *	ASN1_INTEGER_dup(ASN1_INTEGER *x);