Skip to Content.
Sympa Menu

freetds - convert.c: time to split?

freetds AT lists.ibiblio.org

Subject: FreeTDS Development Group

List archive

Chronological Thread  
  • From: Frediano Ziglio <freddyz77 AT tin.it>
  • To: FreeTDS <freetds AT franklin.oit.unc.edu>
  • Subject: convert.c: time to split?
  • Date: 13 Aug 2002 21:20:18 +0200


There are many changes in this file and is quite bigger. Perhaps need
split?

This is just my updated patch to it (I think to split it in the near
future, if someone want to make a look to it...)

Index: src/tds/convert.c
===================================================================
RCS file: /cvsroot/freetds/freetds/src/tds/convert.c,v
retrieving revision 1.34
diff -u -r1.34 convert.c
--- src/tds/convert.c 8 Aug 2002 04:35:05 -0000 1.34
+++ src/tds/convert.c 13 Aug 2002 19:12:15 -0000
@@ -40,19 +40,16 @@
static void *no_unused_var_warn[] = {software_version,
no_unused_var_warn};

-struct diglist {
- short int dig;
- short int carried;
- struct diglist *nextptr;
-};
-
typedef unsigned short utf16_t;

static int _tds_pad_string(char *dest, int destlen);
extern char *tds_numeric_to_string(TDS_NUMERIC *numeric, char *s);
extern char *tds_money_to_string(TDS_MONEY *money, char *s);
static int string_to_datetime(char *datestr, int desttype, CONV_RESULT
*cr );
-static int string_to_numeric(char *instr, CONV_RESULT *cr);
+/**
+ * convert a number in string to a TDSNUMERIC return 0 if success
+ */
+static int string_to_numeric(const char *instr, const char *pend,
CONV_RESULT *cr);
static int store_hour(char *, char *, struct tds_time *);
static int store_time(char *, struct tds_time * );
static int store_yymmdd_date(char *, struct tds_time *);
@@ -69,12 +66,8 @@
static TDS_UINT utf16len(const utf16_t* s);

#define test_alloc(x) {if ((x)==NULL) return TDS_FAIL;}
-extern int g__numeric_bytes_per_prec[];

-extern int (*g_tds_err_handler)(void*);
-
-static int tds_atoi(const char *buf);
-#define atoi(x) tds_atoi((x))
+static TDS_INT tds_atoi(const char *buf,const char *pend,TDS_INT* res);

#define IS_TINYINT(x) ( 0 <= (x) && (x) <= 0xff )

@@ -256,6 +249,7 @@

char *ptr;
int point_found, places;
+TDS_INT tds_i;


switch(desttype) {
@@ -352,18 +346,26 @@
return (srclen / 2);
break;
case SYBINT1:
- if( IS_TINYINT( atoi(src) ) ) {
- cr->ti = atoi(src);
+ if (tds_atoi(src,src + srclen,&tds_i) == TDS_FAIL)
+ return TDS_FAIL;
+ if( IS_TINYINT( tds_i ) ) {
+ cr->ti = tds_i;
return 1;
}
- return 0;
+ return TDS_FAIL;
break;
case SYBINT2:
- cr->si = atoi(src);
+ if (tds_atoi(src,src + srclen,&tds_i) == TDS_FAIL)
+ return TDS_FAIL;
+ if ( tds_i > 32767 || tds_i <= -32768)
+ return TDS_FAIL;
+ cr->si = tds_i;
return 2;
break;
case SYBINT4:
- cr->i = atoi(src);
+ if (tds_atoi(src,src + srclen,&tds_i) == TDS_FAIL)
+ return TDS_FAIL;
+ cr->i = tds_i;
return 4;
break;
case SYBFLT8:
@@ -376,7 +378,9 @@
break;
case SYBBIT:
case SYBBITN:
- cr->ti = (atoi(src)>0) ? 1 : 0;
+ if (tds_atoi(src,src + srclen,&tds_i) == TDS_FAIL)
+ return TDS_FAIL;
+ cr->ti = tds_i ? 1 : 0;
return 1;
break;
case SYBMONEY:
@@ -435,7 +439,10 @@
case SYBDECIMAL:
cr->n.precision = 18;
cr->n.scale = 0;
- return string_to_numeric(src, cr);
+ if (string_to_numeric(src, src + srclen, cr))
+ return TDS_FAIL;
+
+ return sizeof(TDS_NUMERIC);
break;
default:
fprintf(stderr,"error_handler: conversion from %d to %d not
supported\n", srctype, desttype);
@@ -450,34 +457,41 @@
switch(desttype) {
case SYBCHAR:
case SYBVARCHAR:
- cr->c = malloc(1);
- *(cr->c) = src[0] ? '1' : '0';
+ case SYBTEXT:
+ cr->c = malloc(2);
+ test_alloc(cr->c);
+ cr->c[0] = src[0] ? '1' : '0';
+ cr->c[1] = 0;
+ return 1;
break;

- case SYBTEXT:
- break;
case SYBBINARY:
case SYBIMAGE:
break;
case SYBINT1:
cr->ti = src[0] ? 1 : 0;
-
+ return 1;
break;
case SYBINT2:
cr->si = src[0] ? 1 : 0;
+ return 2;
break;
case SYBINT4:
cr->i = src[0] ? 1 : 0;
+ return 4;
break;
case SYBFLT8:
cr->f = src[0] ? 1.0 : 0.0;
+ return sizeof(cr->f);
break;
case SYBREAL:
cr->r = src[0] ? 1.0 : 0.0;
+ return sizeof(cr->r);
break;
case SYBBIT:
case SYBBITN:
cr->ti = src[0];
+ return 1;
break;
case SYBMONEY:
case SYBMONEY4:
@@ -486,7 +500,7 @@
fprintf(stderr,"error_handler: conversion from %d to %d not
supported\n", srctype, desttype);
return TDS_FAIL;
}
- return TDS_SUCCEED;
+ return TDS_FAIL;
}

static TDS_INT
@@ -519,6 +533,10 @@
cr->i = buf;
return 4;
break;
+ case SYBBIT:
+ case SYBBITN:
+ cr->ti = buf ? 1 : 0;
+ return 1;
case SYBFLT8:
cr->f = buf;
return 8;
@@ -527,6 +545,12 @@
cr->r = buf;
return 4;
break;
+ case SYBMONEY4:
+ cr->m4.mny4 = buf * 10000;
+ return 4;
+ case SYBMONEY:
+ cr->m.mny = buf * 10000;
+ return 8;
default:
fprintf(stderr,"error_handler: conversion from %d to %d not
supported\n", srctype, desttype);
return TDS_FAIL;
@@ -545,33 +569,45 @@
case SYBTEXT:
case SYBVARCHAR:
sprintf(tmp_str,"%d",buf);
- cr->c = malloc(strlen(tmp_str) + 1);
+ cr->c = malloc(strlen(tmp_str) + 1);
test_alloc(cr->c);
strcpy(cr->c,tmp_str);
- return strlen(tmp_str);
+ return strlen(tmp_str);
break;
case SYBINT1:
+ if (buf > 255 || buf < 0)
+ return TDS_FAIL;
cr->ti = buf;
- return 1;
+ return 1;
break;
case SYBINT2:
cr->si = buf;
- return 2;
+ return 2;
break;
case SYBINT4:
cr->i = buf;
- return 4;
+ return 4;
break;
+ case SYBBIT:
+ case SYBBITN:
+ cr->ti = buf ? 1 : 0;
+ return 1;
case SYBFLT8:
cr->f = buf;
- return 8;
+ return 8;
break;
case SYBREAL:
cr->r = buf;
- return 4;
+ return 4;
break;
+ case SYBMONEY4:
+ cr->m4.mny4 = buf * 10000;
+ return 4;
+ case SYBMONEY:
+ cr->m.mny = buf * 10000;
+ return 8;
default:
- fprintf(stderr,"error_handler: conversion from %d to %d not
supported\n", srctype, desttype);
+ fprintf(stderr,"error_handler: conversion from %d to
%d not
supported\n", srctype, desttype);
return TDS_FAIL;
}
}
@@ -588,33 +624,41 @@
case SYBTEXT:
case SYBVARCHAR:
sprintf(tmp_str,"%d",buf);
- cr->c = malloc(strlen(tmp_str) + 1);
+ cr->c = malloc(strlen(tmp_str) + 1);
test_alloc(cr->c);
strcpy(cr->c,tmp_str);
- return strlen(tmp_str);
+ return strlen(tmp_str);
break;
case SYBINT1:
+ if (buf > 255 || buf < 0)
+ return TDS_FAIL;
cr->ti = buf;
- return 1;
+ return 1;
break;
case SYBINT2:
+ if (buf > 32767 || buf < -32768)
+ return TDS_FAIL;
cr->si = buf;
- return 2;
+ return 2;
break;
case SYBINT4:
cr->i = buf;
- return 4;
+ return 4;
break;
+ case SYBBIT:
+ case SYBBITN:
+ cr->ti = buf ? 1 : 0;
+ return 1;
case SYBFLT8:
cr->f = buf;
- return 8;
+ return 8;
break;
case SYBREAL:
cr->r = buf;
- return 4;
+ return 4;
break;
default:
- fprintf(stderr,"error_handler: conversion from %d to %d not
supported\n", srctype, desttype);
+ fprintf(stderr,"error_handler: conversion from %d to
%d not
supported\n", srctype, desttype);
return TDS_FAIL;
}
}
@@ -678,6 +722,10 @@
strcpy(cr->c, tmp_str);
return strlen(tmp_str);
break;
+ case SYBBIT:
+ case SYBBITN:
+ cr->ti = buf ? 1 : 0;
+ return 1;
case SYBFLT8:
memcpy(&dollars, src, sizeof(dollars));
cr->f = ((TDS_FLOAT)dollars) / 10000;
@@ -799,8 +847,6 @@
char whole_date_string[30];
struct tds_tm when;

-TDS_INT ret;
-
switch(desttype) {
case SYBCHAR:
case SYBVARCHAR:
@@ -892,10 +938,17 @@
cr->dt4.minutes = (dt_time / 300) / 60;
return sizeof(TDS_DATETIME4);
break;
+ case SYBMONEY4:
+ if (buf > 214748 || buf < -214748)
+ return TDS_FAIL;
+ cr->m4.mny4 = buf * 10000;
+ return 4;
+ case SYBMONEY:
+ cr->m.mny = (TDS_INT8)buf * 10000;
+ return 8;
default:
fprintf(stderr,"error_handler: conversion from %d to %d not
supported\n", srctype, desttype);
return TDS_FAIL;
- break;
}

}
@@ -924,8 +977,6 @@
char whole_date_string[30];
struct tds_tm when;

-TDS_INT ret;
-
switch(desttype) {
case SYBCHAR:
case SYBVARCHAR:
@@ -994,7 +1045,6 @@
default:
fprintf(stderr,"error_handler: conversion from %d to %d not
supported\n", srctype, desttype);
return TDS_FAIL;
- break;
}


@@ -1090,6 +1140,7 @@
}
}

+static TDS_INT
tds_convert_unique(int srctype,TDS_CHAR *src, TDS_INT srclen,
int desttype,TDS_INT destlen, CONV_RESULT *cr)
{
@@ -1511,167 +1562,163 @@

}

-static TDS_INT
-string_to_numeric(char *instr, CONV_RESULT *cr)
+static int
+string_to_numeric(const char *instr, const char *pend, CONV_RESULT *cr)
{

-char mynumber[39];
-/* unsigned char mynumeric[16]; */
+char mynumber[40];
+/* num packaged 8 digit, see below for detail */
+TDS_UINT packed_num[5];

char *ptr;
-char c = '\0';
-
-unsigned char masks[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02,
0x01 };
+const char *pdigits;
+const char* pstr;

-short int binarylist[128];
-short int carry_on = 1;
-short int i = 0;
-short int j = 0;
-short int x = 0;
-short int bits, bytes, places, point_found, sign;
-
-struct diglist *topptr = (struct diglist *)NULL;
-struct diglist *curptr = (struct diglist *)NULL;
-struct diglist *freeptr = (struct diglist *)NULL;
+TDS_UINT carry = 0;
+char not_zero = 1;
+int i = 0;
+int j = 0;
+short int bytes, places, point_found, sign, digits;

sign = 0;
point_found = 0;
places = 0;
-
+
+ /* FIXME: application can pass invalid value for precision and scale
?? */
+ if (cr->n.precision > 38)
+ return 1;

if (cr->n.precision == 0)
- cr->n.precision = 18;
+ cr->n.precision = 38; /* assume max precision */
+
+ if ( cr->n.scale > cr->n.precision )
+ return 1;


- for (ptr = instr; *ptr == ' '; ptr++); /* skip leading blanks */
+ /* skip leading blanks */
+ for (pstr = instr;; ++ptr)
+ {
+ if (pstr == pend) return 1;
+ if (*pstr != ' ') break;
+ }

- if (*ptr == '-' || *ptr == '+') /* deal with a leading sign
*/
+ if ( *pstr == '-' || *pstr == '+' ) /* deal with a leading
sign */
{
- if (*ptr == '-')
+ if (*pstr == '-')
sign = 1;
- ptr++;
+ pstr++;
}

- for(; *ptr; ptr++) /* deal with the rest */
+ digits = 0;
+ pdigits = pstr;
+ for(; pstr != pend; ++pstr) /* deal with the rest */
{
- if (isdigit(*ptr)) /* it's a number */
+ if (isdigit(*pstr)) /* its a number */
{
- mynumber[i++] = *ptr;
if (point_found) /* if we passed a decimal
point */
- places++; /* count digits after that
point */
+ ++places; /* count digits after that
point */
+ else
+ ++digits; /* count digits before point
*/
}
- else if (*ptr == '.') /* found a decimal point */
+ else if (*pstr == '.') /* found a decimal point */
{
- if (point_found) /* already had one. lose the
rest */
- break;
+ if (point_found) /* already had one. return
error */
+ return 1;
if (cr->n.scale == 0) /* no scale...lose the rest
*/
- break;
+ break; /* FIXME: check other characters */
point_found = 1;
}
else /* first invalid character */
- break; /* lose the rest. */
- }
-
-
- if (cr->n.scale > 0) /* scale specified, pad out */
- { /* number with zeroes to the
*/
- /* scale...
*/
-
- for (j = 0 ; j < (cr->n.scale - places) ; j++ )
- mynumber[i++] = '0';
+ return 1; /* return error. */

}
-
- mynumber[i] = '\0';
-
-
- if (strlen(mynumber) > cr->n.precision )
- strcpy(mynumber, &mynumber[strlen(mynumber) - cr->n.precision]);

- for (ptr = mynumber; *ptr; ptr++)
- {
- if (topptr == (struct diglist *)NULL)
- {
- topptr = (struct diglist *)malloc(sizeof(struct diglist));
- curptr = topptr;
- curptr->nextptr = NULL;
- curptr->dig = *ptr - 48;
- curptr->carried = 0;
- }
- else
- {
- curptr->nextptr = (struct diglist *)malloc(sizeof(struct
diglist));
- curptr = curptr->nextptr;
- curptr->nextptr = NULL;
- curptr->dig = *ptr - 48;
- curptr->carried = 0;
- }
- }
+ /* no digits? no number!*/
+ if (!digits)
+ return 1;
+
+ /* truncate decimal digits */
+ if ( cr->n.scale > 0 && places > cr->n.scale)
+ places = cr->n.scale;
+
+ /* too digits, error */
+ if ( (digits+cr->n.scale) > cr->n.precision)
+ return 1;
+
+
+ /* FIXME: this can be optimized in a single step */
+
+ /* scale specified, pad out number with zeroes to the scale... */
+ ptr = mynumber+40-(cr->n.scale-places);
+ memset(ptr,48,cr->n.scale-places);
+ ptr -= places;
+ /* copy number without point */
+ memcpy(ptr,pdigits+digits+1,places);
+ ptr -= digits;
+ memcpy(ptr,pdigits,digits);
+ memset(mynumber,48,ptr-mynumber);
+
+ /* transform ASCII string into a numeric array */
+ for (ptr = mynumber; ptr != mynumber+40; ++ptr)
+ *ptr -= 48;
+
+ /*
+ * Packaged number explanation
+ * I package 8 decimal digit in one number
+ * This because 10^8 = 5^8 * 2^8 = 5^8 * 256
+ * So dividing 10^8 for 256 make no remainder
+ * So I can split for bytes in an optmized way
+ */

- memset(&binarylist[0], '\0', sizeof(short int) * 128);
- i = 127;
-
- while (carry_on)
+ /* transform to packaged one */
+ for(j=0;j<5;++j)
{
- carry_on = 0;
- for (curptr = topptr ; curptr != NULL; curptr = curptr->nextptr)
- {
- if (curptr->dig > 0)
- carry_on = 1;
-
- if (curptr->nextptr != NULL )
- {
- curptr->nextptr->carried = ( ( curptr->carried * 10 ) +
curptr->dig ) % 2;
- curptr->dig = ( ( curptr->carried * 10 ) + curptr->dig ) /
2;
- }
- else
- {
- if (carry_on)
- {
- binarylist[i--] = ( ( curptr->carried * 10 ) +
curptr->dig ) % 2;
- }
- curptr->dig = ( ( curptr->carried * 10 ) + curptr->dig ) /
2;
- }
- }
-
+ TDS_UINT n = mynumber[j*8];
+ for(i=1;i<8;++i)
+ {
+ n = n * 10 + mynumber[j*8+i];
+ }
+ packed_num[j] = n;
}

- memset(cr->n.array, '\0', 17);
- bits = 0;
- bytes = 1;
-
+ memset(cr->n.array,0,sizeof(cr->n.array));
cr->n.array[0] = sign;
-
- x = g__numeric_bytes_per_prec[cr->n.precision] - 1;
-
- for (i = 128 - (x * 8); i < 128 ; i++)
+ bytes = 1;
+
+ while (not_zero)
{
- if (binarylist[i])
- {
- c = c | masks[bits];
- }
- bits++;
- if (bits == 8)
+ not_zero = 0;
+ carry = 0;
+ for (i = 0; i < 5; ++i)
{
- cr->n.array[bytes] = c;
-
- bytes++;
- bits = 0;
- c = '\0';
+ TDS_UINT tmp;
+
+ if (packed_num[i] > 0)
+ not_zero = 1;
+
+ /* divide for 256 for find another byte */
+ tmp = packed_num[i];
+ /* carry * (25u*25u*25u*25u) = carry * 10^8 / 256u
+ * using unsigned number is just an optimization
+ * compiler can translate division to a shift and remainder
+ * to a binary and
+ */
+ packed_num[i] = carry * (25u*25u*25u*25u) + packed_num[i] / 256u;
+ carry = tmp % 256u;
+
+ if ( i == 4 && not_zero)
+ {
+ /* source number is limited to 38 decimal digit
+ * 10^39-1 < 2^128 (16 byte) so this cannot make an overflow
+ */
+ cr->n.array[bytes] = carry;
+ ++bytes;
+ }
}
}
-
- curptr = topptr;
- while (curptr != NULL)
- {
- freeptr = curptr;
- curptr = curptr->nextptr;
- free(freeptr);
- }
-
- return sizeof(TDS_NUMERIC);
+ return 0;
}
-
+
static int _tds_pad_string(char *dest, int destlen)
{
int i=0;
@@ -2184,7 +2231,7 @@
while(<DATA>) {
next if /^\s+To\s+$/;
next if /^From/;
- if( /^\s+CHAR TEXT/ ) {
+ if( /^\s+VARCHAR CHAR/ ) {
@to = split;
next;
}
@@ -2210,25 +2257,26 @@
__DATA__
To
From
- CHAR TEXT BINARY IMAGE INT1 INT2 INT4 FLT8 REAL NUMERIC
DECIMAL BIT MONEY MONEY4 DATETIME DATETIME4 BOUNDARY SENSITIVITY
-CHAR T T T T T T T T T T
T T T T T T T T
-TEXT T T T T T T T T T T
T T T T T T T T
-BINARY T T T T T T T T T T
T T T T F F F F
-IMAGE T T T T T T T T T T
T T T T F F F F
-INT1 T T T T T T T T T T
T T T T F F F F
-INT2 T T T T T T T T T T
T T T T F F F F
-INT4 T T T T T T T T T T
T T T T F F F F
-FLT8 T T T T T T T T T T
T T T T F F F F
-REAL T T T T T T T T T T
T T T T F F F F
-NUMERIC T T T T T T T T T T
T T T T F F F F
-DECIMAL T T T T T T T T T T
T T T T F F F F
-BIT T T T T T T T T T T
T T T T F F F F
-MONEY T T T T T T T T T T
T T T T F F F F
-MONEY4 T T T T T T T T T T
T T T T F F F F
-DATETIME T T T T F F F F F F
F F F F T T F F
-DATETIME4 T T T T F F F F F F
F F F F T T F F
-BOUNDARY T T F F F F F F F F
F F F F F F T F
-SENSITIVITY T T F F F F F F F F
F F F F F F F T
+ VARCHAR CHAR TEXT BINARY IMAGE INT1 INT2 INT4 FLT8 REAL
NUMERIC DECIMAL BIT MONEY MONEY4 DATETIME DATETIME4 BOUNDARY SENSITIVITY
+VARCHAR T T T T T T T T T T T
T T T T T T T T
+CHAR T T T T T T T T T T T
T T T T T T T T
+TEXT T T T T T T T T T T T
T T T T T T T T
+BINARY T T T T T T T T T T T
T T T T F F F F
+IMAGE T T T T T T T T T T T
T T T T F F F F
+INT1 T T T T T T T T T T T
T T T T F F F F
+INT2 T T T T T T T T T T T
T T T T F F F F
+INT4 T T T T T T T T T T T
T T T T F F F F
+FLT8 T T T T T T T T T T T
T T T T F F F F
+REAL T T T T T T T T T T T
T T T T F F F F
+NUMERIC T T T T T T T T T T T
T T T T F F F F
+DECIMAL T T T T T T T T T T T
T T T T F F F F
+BIT T T T T T T T T T T T
T T T T F F F F
+MONEY T T T T T T T T T T T
T T T T F F F F
+MONEY4 T T T T T T T T T T T
T T T T F F F F
+DATETIME T T T T T F F F F F F
F F F F T T F F
+DATETIME4 T T T T T F F F F F F
F F F F T T F F
+BOUNDARY T T T F F F F F F F F
F F F F F F T F
+SENSITIVITY T T T F F F F F F F F
F F F F F F F T
#endif
unsigned char
tds_willconvert(int srctype, int desttype)
@@ -2390,35 +2438,70 @@
* n.b. it is possible to embed all sorts of non-printable characters,
but we
* only check for spaces. at this time, no one on the project has
tested anything else.
*/
-#undef atoi
-int
-tds_atoi(const char *buf)
+TDS_INT
+tds_atoi(const char *buf,const char *pend,TDS_INT* res)
{
enum { blank = ' ' };
-char *s;
const char *p;
-int i;
-
- s = strchr( buf, blank );
- if( !s )
- return atoi(buf);
-
- while( *s++ == blank ); /* ignore trailing */
-
- if( *s == '\0' )
- return atoi(buf);
-
- s = (char*) malloc( strlen(buf) );
-
- for( i=0, p=buf; *p != '\0'; p++ ) {
- if( *p != blank )
- s[i++] = *p;
+int sign;
+unsigned int num,n; /* we use unsigned here for best overflow check */
+
+ p = buf;
+
+ /* ignore leading spaces */
+ while( p != pend && *p == blank )
+ ++p;
+ if (p==pend) return TDS_FAIL;
+
+ /* check for sign */
+ sign = 0;
+ switch ( *p ) {
+ case '-':
+ sign = 1;
+ /* fall thru */
+ case '+':
+ /* skip spaces between sign and number */
+ ++p;
+ while( p != pend && *p == blank )
+ ++p;
+ break;
}
- s[i] = '\0';
-
- i = atoi(s);
- free(s);
-
- return i;
+
+ /* a digit must be present */
+ if (p == pend )
+ return TDS_FAIL;
+
+ num = 0;
+ for(;p != pend;++p) {
+ /* check for trailing spaces */
+ if (*p == blank) {
+ while( p != pend && *++p == blank);
+ if (p==pend) return TDS_FAIL;
+ break;
+ }
+
+ /* must be a digit */
+ if (!isdigit(*p))
+ return TDS_FAIL;
+
+ /* add a digit to number and check for overflow */
+ n = num * 10 + (*p-'0');
+ if (n < num)
+ return TDS_FAIL;
+ num = n;
+ }
+
+ /* check for overflow and convert unsigned to signed */
+ if (sign) {
+ if (num > 2147483648u)
+ return TDS_FAIL;
+ num = -num;
+ } else {
+ if (num >= 2147483648u)
+ return TDS_FAIL;
+ }
+
+ *res = num;
+ return TDS_SUCCEED;
}

freddy77





  • convert.c: time to split?, Frediano Ziglio, 08/13/2002

Archive powered by MHonArc 2.6.24.

Top of Page