📄 convert.c
字号:
/* FreeTDS - Library of routines accessing Sybase and Microsoft databases * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */#if HAVE_CONFIG_H#include <config.h>#endif#include <stdarg.h>#include <stdio.h>#if TIME_WITH_SYS_TIME# if HAVE_SYS_TIME_H# include <sys/time.h># endif# include <time.h>#else# if HAVE_SYS_TIME_H# include <sys/time.h># else# include <time.h># endif#endif#include <assert.h>#include <ctype.h>#if HAVE_ERRNO_H#include <errno.h>#endif /* HAVE_ERRNO_H */#if HAVE_STDLIB_H#include <stdlib.h>#endif /* HAVE_STDLIB_H */#if HAVE_STRING_H#include <string.h>#endif /* HAVE_STRING_H */#if HAVE_STRINGS_H#include <strings.h>#endif /* HAVE_STRINGS_H */#include "tds.h"#include "tdsconvert.h"#include "tdsbytes.h"#include "replacements.h"#ifdef DMALLOC#include <dmalloc.h>#endifTDS_RCSID(var, "$Id: convert.c,v 1.179.2.1 2008/02/04 07:55:39 freddy77 Exp $");typedef unsigned short utf16_t;static TDS_INT tds_convert_int1(int srctype, const TDS_CHAR * src, int desttype, CONV_RESULT * cr);static TDS_INT tds_convert_int2(int srctype, const TDS_CHAR * src, int desttype, CONV_RESULT * cr);static TDS_INT tds_convert_int4(int srctype, const TDS_CHAR * src, int desttype, CONV_RESULT * cr);static TDS_INT tds_convert_int8(int srctype, const TDS_CHAR * src, int desttype, CONV_RESULT * cr);static int string_to_datetime(const char *datestr, int desttype, CONV_RESULT * cr);static int is_dd_mon_yyyy(char *t);static int store_dd_mon_yyy_date(char *datestr, struct tds_time *t);#define test_alloc(x) {if ((x)==NULL) return TDS_CONVERT_NOMEM;}#define IS_TINYINT(x) ( 0 <= (x) && (x) <= 0xff )#define IS_SMALLINT(x) ( -32768 <= (x) && (x) <= 32767 )/* * f77: I don't write -2147483648, some compiler seem to have some problem * with this constant although is a valid 32bit value */#define TDS_INT_MIN (-2147483647l-1l)#define TDS_INT_MAX 2147483647l#define IS_INT(x) (TDS_INT_MIN <= (x) && (x) <= TDS_INT_MAX)#define TDS_INT8_MAX ((((TDS_INT8) 0x7fffffffl) << 32) + 0xfffffffflu)#define TDS_INT8_MIN (((TDS_INT8) (-0x7fffffffl-1)) << 32)#define IS_INT8(x) (TDS_INT8_MIN <= (x) && (x) <= TDS_INT8_MAX)/** * \ingroup libtds * \defgroup convert Conversion * Conversions between datatypes. Supports, for example, dbconvert(). *//** * \addtogroup convert * @{ *//** * convert a number in string to a TDSNUMERIC * @return sizeof(TDS_NUMERIC) on success, TDS_CONVERT_* failure code on failure */static int string_to_numeric(const char *instr, const char *pend, CONV_RESULT * cr);/** * convert a zero terminated string to NUMERIC * @return sizeof(TDS_NUMERIC) on success, TDS_CONVERT_* failure code on failure */static int stringz_to_numeric(const char *instr, CONV_RESULT * cr);static TDS_INT string_to_int(const char *buf, const char *pend, TDS_INT * res);static TDS_INT string_to_int8(const char *buf, const char *pend, TDS_INT8 * res);static int store_hour(const char *, const char *, struct tds_time *);static int store_time(const char *, struct tds_time *);static int store_yymmdd_date(const char *, struct tds_time *);static int store_monthname(const char *, struct tds_time *);static int store_numeric_date(const char *, struct tds_time *);static int store_mday(const char *, struct tds_time *);static int store_year(int, struct tds_time *);/* static int days_this_year (int years); */static int is_timeformat(const char *);static int is_numeric(const char *);static int is_alphabetic(const char *);static int is_ampm(const char *);#define is_monthname(s) (store_monthname(s, NULL) >= 0)static int is_numeric_dateformat(const char *);#if 0static TDS_UINT utf16len(const utf16_t * s);static const char *tds_prtype(int token);#endifconst char tds_hex_digits[16] = "0123456789abcdef";/** * Return type suitable for conversions (convert all nullable types to * fixed type) * @param srctype type to convert * @param colsize size of type * @result type for conversion */inttds_get_conversion_type(int srctype, int colsize){ switch (srctype) { case SYBINTN: switch (colsize) { case 8: return SYBINT8; case 4: return SYBINT4; case 2: return SYBINT2; case 1: return SYBINT1; } break; case SYBFLTN: switch (colsize) { case 8: return SYBFLT8; case 4: return SYBREAL; } break; case SYBDATETIMN: switch (colsize) { case 8: return SYBDATETIME; case 4: return SYBDATETIME4; } break; case SYBMONEYN: switch (colsize) { case 8: return SYBMONEY; case 4: return SYBMONEY4; } break; /* * altough tds_convert handle SYBBITN other routine use this * function to retrieve not variant type */ case SYBBITN: return SYBBIT; break; } return srctype;}/** * Copy a terminated string to result and return len or TDS_CONVERT_NOMEM */static TDS_INTstring_to_result(int desttype, const char *s, CONV_RESULT * cr){ size_t len = strlen(s); if (desttype != TDS_CONVERT_CHAR) { cr->c = (TDS_CHAR *) malloc(len + 1); test_alloc(cr->c); memcpy(cr->c, s, len + 1); } else { memcpy(cr->cc.c, s, len < cr->cc.len ? len : cr->cc.len); } return len;}#define string_to_result(s, cr) \ string_to_result(desttype, s, cr)/** * Copy binary data to to result and return len or TDS_CONVERT_NOMEM */static TDS_INTbinary_to_result(int desttype, const void *data, size_t len, CONV_RESULT * cr){ if (desttype != TDS_CONVERT_BINARY) { cr->ib = (TDS_CHAR *) malloc(len); test_alloc(cr->ib); memcpy(cr->ib, data, len); } else { memcpy(cr->cb.ib, data, len < cr->cb.len ? len : cr->cb.len); } return len;}#define binary_to_result(data, len, cr) \ binary_to_result(desttype, data, len, cr)#define CASE_ALL_CHAR \ SYBCHAR: case SYBVARCHAR: case SYBTEXT: case XSYBCHAR: case XSYBVARCHAR#define CASE_ALL_BINARY \ SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY: case TDS_CONVERT_BINARY/* TODO implement me *//*static TDS_INT tds_convert_ntext(int srctype,TDS_CHAR *src,TDS_UINT srclen, int desttype, CONV_RESULT *cr){ return TDS_CONVERT_NOAVAIL;}*/static TDS_INTtds_convert_binary(int srctype, const TDS_UCHAR * src, TDS_INT srclen, int desttype, CONV_RESULT * cr){ int cplen; int s; char *c; switch (desttype) { case TDS_CONVERT_CHAR: cplen = srclen * 2; if (cplen > cr->cc.len) cplen = cr->cc.len; c = cr->cc.c; for (s = 0; cplen >= 2; ++s, cplen -= 2) { *c++ = tds_hex_digits[src[s]>>4]; *c++ = tds_hex_digits[src[s]&0xF]; } if (cplen) *c++ = tds_hex_digits[src[s]>>4]; return srclen * 2; case CASE_ALL_CHAR: /* * NOTE: Do not prepend 0x to string. * The libraries all expect a bare string, without a 0x prefix. * Applications such as isql and query analyzer provide the "0x" prefix. */ /* 2 * source length + 1 for terminator */ cr->c = (TDS_CHAR *) malloc((srclen * 2) + 1); test_alloc(cr->c); c = cr->c; for (s = 0; s < srclen; s++) { *c++ = tds_hex_digits[src[s]>>4]; *c++ = tds_hex_digits[src[s]&0xF]; } *c = '\0'; return (srclen * 2); break; case CASE_ALL_BINARY: return binary_to_result(src, srclen, cr); break; case SYBINT1: case SYBINT2: case SYBINT4: case SYBINT8: case SYBMONEY4: case SYBMONEY: case SYBREAL: case SYBFLT8: cplen = tds_get_size_by_type(desttype); if (srclen >= cplen) srclen = cplen; memcpy(cr, src, srclen); memset(((char*) cr) + srclen, 0, cplen - srclen); return cplen; break; /* conversions not allowed */ case SYBDATETIME4: case SYBDATETIME: case SYBDATETIMN: /* TODO should we do some test for these types or work as ints ?? */ case SYBDECIMAL: case SYBNUMERIC: case SYBBIT: case SYBBITN: default: break; } return TDS_CONVERT_NOAVAIL;}static TDS_INTtds_convert_char(int srctype, const TDS_CHAR * src, TDS_UINT srclen, int desttype, CONV_RESULT * cr){ unsigned int i, j; unsigned char hex1; TDS_INT8 mymoney; char mynumber[39]; const char *ptr, *pend; int point_found; unsigned int places; TDS_INT tds_i; TDS_INT8 tds_i8; TDS_INT rc; TDS_CHAR *ib; switch (desttype) { case TDS_CONVERT_CHAR: memcpy(cr->cc.c, src, srclen < cr->cc.len ? srclen : cr->cc.len); return srclen; case CASE_ALL_CHAR: cr->c = (TDS_CHAR *) malloc(srclen + 1); test_alloc(cr->c); memcpy(cr->c, src, srclen); cr->c[srclen] = 0; return srclen; break; case CASE_ALL_BINARY: /* skip leading "0x" or "0X" */ if (src[0] == '0' && (src[1] == 'x' || src[1] == 'X')) { src += 2; srclen -= 2; } /* ignore trailing blanks and nulls */ /* FIXME is good to ignore null ?? */ while (srclen > 0 && (src[srclen - 1] == ' ' || src[srclen - 1] == '\0')) --srclen; /* a binary string output will be half the length of */ /* the string which represents it in hexadecimal */ /* if srclen if odd we must add a "0" before ... */ j = 0; /* number where to start converting */ if (srclen & 1) { ++srclen; j = 1; --src; } ib = cr->cb.ib; if (desttype != TDS_CONVERT_BINARY) { cr->ib = (TDS_CHAR *) malloc(srclen / 2); test_alloc(cr->ib); ib = cr->ib; } for (i = srclen; i > j;) { hex1 = src[--i]; if ('0' <= hex1 && hex1 <= '9') hex1 &= 0x0f; else { hex1 &= 0x20 ^ 0xff; /* mask off 0x20 to ensure upper case */ if ('A' <= hex1 && hex1 <= 'F') { hex1 -= ('A' - 10); } else { tdsdump_log(TDS_DBG_INFO1, "error_handler: attempt to convert data stopped by syntax error in source field \n"); return TDS_CONVERT_SYNTAX; } } assert(hex1 < 0x10); if (desttype == TDS_CONVERT_BINARY && (i/2) >= cr->cb.len) continue; if (i & 1) ib[i / 2] = hex1; else ib[i / 2] |= hex1 << 4; } return srclen / 2; break; case SYBINT1: if ((rc = string_to_int(src, src + srclen, &tds_i)) < 0) return rc; if (!IS_TINYINT(tds_i)) return TDS_CONVERT_OVERFLOW; cr->ti = tds_i; return sizeof(TDS_TINYINT); break; case SYBINT2: if ((rc = string_to_int(src, src + srclen, &tds_i)) < 0) return rc; if (!IS_SMALLINT(tds_i)) return TDS_CONVERT_OVERFLOW; cr->si = tds_i; return sizeof(TDS_SMALLINT); break; case SYBINT4: if ((rc = string_to_int(src, src + srclen, &tds_i)) < 0) return rc; cr->i = tds_i; return sizeof(TDS_INT); break; case SYBINT8: if ((rc = string_to_int8(src, src + srclen, &tds_i8)) < 0) return rc; cr->bi = tds_i8; return sizeof(TDS_INT8); break; case SYBFLT8: /* FIXME not null terminated */ /* TODO check syntax and overflow */ cr->f = atof(src); return sizeof(TDS_FLOAT); break; case SYBREAL: /* FIXME not null terminated */ /* TODO check syntax and overflow */ cr->r = atof(src); return sizeof(TDS_REAL); break; case SYBBIT: case SYBBITN: if ((rc = string_to_int(src, src + srclen, &tds_i)) < 0) return rc; cr->ti = tds_i ? 1 : 0; return sizeof(TDS_TINYINT); break; case SYBMONEY: case SYBMONEY4: /* TODO code similar to string_to_numeric... */ i = 0; places = 0; point_found = 0; pend = src + srclen; /* skip leading blanks */ for (ptr = src; ptr != pend && *ptr == ' '; ++ptr); switch (ptr != pend ? *ptr : 0) { case '-': mynumber[i++] = '-'; /* fall through */ case '+': ptr++; for (; ptr != pend && *ptr == ' '; ++ptr); break; } for (; ptr != pend; ptr++) { /* deal with the rest */ if (isdigit((unsigned char) *ptr)) { /* it's a number */ /* no more than 4 decimal digits */ if (places < 4) mynumber[i++] = *ptr; /* assure not buffer overflow */ if (i == 30) return TDS_CONVERT_OVERFLOW; if (point_found) { /* if we passed a decimal point */ /* count digits after that point */ ++places; } } else if (*ptr == '.') { /* found a decimal point */ if (point_found) /* already had one. error */ return TDS_CONVERT_SYNTAX; point_found = 1; } else /* first invalid character */ return TDS_CONVERT_SYNTAX; /* lose the rest. */ } for (j = places; j < 4; j++) mynumber[i++] = '0'; mynumber[i] = 0; /* FIXME overflow not handled */ if (desttype == SYBMONEY) { mymoney = atoll(mynumber); cr->m.mny = mymoney; return sizeof(TDS_MONEY); } else { cr->m4.mny4 = atol(mynumber); return sizeof(TDS_MONEY4); } break; case SYBDATETIME: /* FIXME not null terminated */ return string_to_datetime(src, SYBDATETIME, cr); break; case SYBDATETIME4: /* FIXME not null terminated */ return string_to_datetime(src, SYBDATETIME4, cr); break; case SYBNUMERIC: case SYBDECIMAL: return string_to_numeric(src, src + srclen, cr); break; case SYBUNIQUE:{ unsigned n = 0; char c; /* * format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX * or {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} * or XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX * or {XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX} * SQL seem to ignore the additional braces. */ if (srclen < (32 + 3)) return TDS_CONVERT_SYNTAX; if (src[0] == '{') { TDS_UINT last = (src[8+1 + 4+1 + 4+1 + 4 + 1] == '-') ? 32+4+1 : 32+3+1; if (srclen <= last || src[last] != '}') return TDS_CONVERT_SYNTAX; ++src; } /* * Test each character and get value. * sscanf works if the number terminates with less digits. */ for (i = 0; i < 32 + 3; ++i) { c = src[i]; switch (i) { case 8: if (c != '-') return TDS_CONVERT_SYNTAX; cr->u.Data1 = n; n = 0; break; case 8+1 + 4: if (c != '-') return TDS_CONVERT_SYNTAX;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -