⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 read.c

📁 在Linux/Unix下面访问WINDOWS SQLSERVER 的ODBC驱动程序
💻 C
字号:
/* FreeTDS - Library of routines accessing Sybase and Microsoft databases * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004  Brian Bruns * Copyright (C) 2005, 2006, 2007  Frediano Ziglio * * 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#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_UNISTD_H#include <unistd.h>#endif /* HAVE_UNISTD_H */#include <assert.h>#include "tds.h"#include "tdsiconv.h"#include "tdsbytes.h"#ifdef DMALLOC#include <dmalloc.h>#endifTDS_RCSID(var, "$Id: read.c,v 1.106 2007/12/27 13:45:23 freddy77 Exp $");static int read_and_convert(TDSSOCKET * tds, const TDSICONV * char_conv,			    size_t * wire_size, char **outbuf, size_t * outbytesleft);/** * \ingroup libtds * \defgroup network Network functions * Functions for reading or writing from network. *//** * \addtogroup network * @{  *//*** Return a single byte from the input buffer*/unsigned chartds_get_byte(TDSSOCKET * tds){	int rc;	if (tds->in_pos >= tds->in_len) {		do {			if (IS_TDSDEAD(tds) || (rc = tds_read_packet(tds)) < 0)				return 0;		} while (!rc);	}	return tds->in_buf[tds->in_pos++];}/** * Unget will always work as long as you don't call it twice in a row.  It * it may work if you call it multiple times as long as you don't backup * over the beginning of network packet boundary which can occur anywhere in * the token stream. */voidtds_unget_byte(TDSSOCKET * tds){	/* this is a one trick pony...don't call it twice */	tds->in_pos--;}unsigned chartds_peek(TDSSOCKET * tds){	unsigned char result = tds_get_byte(tds);	if (tds->in_pos > 0)		--tds->in_pos;	return result;}				/* tds_peek()  *//** * Get an int16 from the server. */TDS_SMALLINTtds_get_smallint(TDSSOCKET * tds){	unsigned char bytes[2];	tds_get_n(tds, bytes, 2);#if WORDS_BIGENDIAN	if (tds->emul_little_endian)		return (TDS_SMALLINT) TDS_GET_A2LE(bytes);#endif	return (TDS_SMALLINT) TDS_GET_A2(bytes);}/** * Get an int32 from the server. */TDS_INTtds_get_int(TDSSOCKET * tds){	unsigned char bytes[4];	tds_get_n(tds, bytes, 4);#if WORDS_BIGENDIAN	if (tds->emul_little_endian)		return (TDS_INT) TDS_GET_A4LE(bytes);#endif	return (TDS_INT) TDS_GET_A4(bytes);}TDS_INT8tds_get_int8(TDSSOCKET * tds){	TDS_INT h;	TDS_UINT l;	unsigned char bytes[8];	tds_get_n(tds, bytes, 8);#if WORDS_BIGENDIAN	if (tds->emul_little_endian) {		l = TDS_GET_A4LE(bytes);		h = (TDS_INT) TDS_GET_A4LE(bytes+4);	} else {		h = (TDS_INT) TDS_GET_A4(bytes);		l = TDS_GET_A4(bytes+4);	}#else	l = TDS_GET_A4(bytes);	h = (TDS_INT) TDS_GET_A4(bytes+4);#endif	return (((TDS_INT8) h) << 32) | l;}#if ENABLE_EXTRA_CHECKS# define TEMP_INIT(s) char* temp = (char*)malloc(32); const size_t temp_size = 32# define TEMP_FREE free(temp);# define TEMP_SIZE temp_size#else# define TEMP_INIT(s) char temp[s]# define TEMP_FREE ;# define TEMP_SIZE sizeof(temp)#endif/** * Fetch a string from the wire. * Output string is NOT null terminated. * If TDS version is 7 or 8 read unicode string and convert it. * This function should be use to read server default encoding strings like  * columns name, table names, etc, not for data (use tds_get_char_data instead) * @return bytes written to \a dest * @param tds  connection information * @param string_len length of string to read from wire  *        (in server characters, bytes for tds4-tds5, ucs2 for tds7+) * @param dest destination buffer, if NULL string is read and discarded * @param dest_size destination buffer size, in bytes */inttds_get_string(TDSSOCKET * tds, int string_len, char *dest, size_t dest_size){	size_t wire_bytes;	/*	 * FIX: 02-Jun-2000 by Scott C. Gray (SCG)	 * Bug to malloc(0) on some platforms.	 */	if (string_len == 0) {		return 0;	}	assert(string_len >= 0 && dest_size >= 0);	wire_bytes = IS_TDS7_PLUS(tds) ? string_len * 2 : string_len;	if (IS_TDS7_PLUS(tds)) {		if (dest == NULL) {			tds_get_n(tds, NULL, wire_bytes);			return string_len;		}		return read_and_convert(tds, tds->char_convs[client2ucs2], &wire_bytes, &dest, &dest_size);	} else {		/* FIXME convert to client charset */		assert(dest_size >= (size_t) string_len);		tds_get_n(tds, dest, string_len);		return string_len;	}}/** * Fetch character data the wire. * Output is NOT null terminated. * If \a char_conv is not NULL, convert data accordingly. * \param tds         state information for the socket and the TDS protocol * \param row_buffer  destination buffer in current_row. Can't be NULL * \param wire_size   size to read from wire (in bytes) * \param curcol      column information * \return TDS_SUCCEED or TDS_FAIL (probably memory error on text data) * \todo put a TDSICONV structure in every TDSCOLUMN */inttds_get_char_data(TDSSOCKET * tds, char *row_buffer, size_t wire_size, TDSCOLUMN * curcol){	size_t in_left;	TDSBLOB *blob = NULL;	char *dest = row_buffer;	if (is_blob_type(curcol->column_type)) {		blob = (TDSBLOB *) row_buffer;		dest = blob->textvalue;	}	/* 	 * dest is usually a column buffer, allocated when the column's metadata are processed 	 * and reused for each row.  	 * For blobs, dest is blob->textvalue, and can be reallocated or freed	 * TODO: reallocate if blob and no space 	 */	 	/* silly case, empty string */	if (wire_size == 0) {		curcol->column_cur_size = 0;		if (blob)			TDS_ZERO_FREE(blob->textvalue);		return TDS_SUCCEED;	}	if (curcol->char_conv) {		/*		 * TODO The conversion should be selected from curcol and tds version		 * TDS8/single -> use curcol collation		 * TDS7/single -> use server single byte		 * TDS7+/unicode -> use server (always unicode)		 * TDS5/4.2 -> use server 		 * TDS5/UTF-8 -> use server		 * TDS5/UTF-16 -> use UTF-16		 */		in_left = blob ? curcol->column_cur_size : curcol->column_size;		curcol->column_cur_size = read_and_convert(tds, curcol->char_conv, &wire_size, &dest, &in_left);		if (wire_size > 0) {			tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: discarded %u on wire while reading %d into client. \n", 							 (unsigned int) wire_size, curcol->column_cur_size);			return TDS_FAIL;		}	} else {		curcol->column_cur_size = wire_size;		if (tds_get_n(tds, dest, wire_size) == NULL) {			tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: failed to read %u from wire. \n",				    (unsigned int) wire_size);			return TDS_FAIL;		}	}	return TDS_SUCCEED;}/** * Get N bytes from the buffer and return them in the already allocated space   * given to us.  We ASSUME that the person calling this function has done the   * bounds checking for us since they know how many bytes they want here. * dest of NULL means we just want to eat the bytes.   (tetherow@nol.org) */void *tds_get_n(TDSSOCKET * tds, void *dest, int need){	int have;	assert(need >= 0);	have = (tds->in_len - tds->in_pos);	while (need > have) {		/* We need more than is in the buffer, copy what is there */		if (dest != NULL) {			memcpy((char *) dest, tds->in_buf + tds->in_pos, have);			dest = (char *) dest + have;		}		need -= have;		if (tds_read_packet(tds) < 0)			return NULL;		have = tds->in_len - tds->in_pos;	}	if (need > 0) {		/* get the remainder if there is any */		if (dest != NULL) {			memcpy((char *) dest, tds->in_buf + tds->in_pos, need);		}		tds->in_pos += need;	}	return dest;}/** * Return the number of bytes needed by specified type. */inttds_get_size_by_type(int servertype){	switch (servertype) {	case SYBINT1:		return 1;		break;	case SYBINT2:		return 2;		break;	case SYBINT4:		return 4;		break;	case SYB5INT8:	case SYBINT8:		return 8;		break;	case SYBREAL:		return 4;		break;	case SYBFLT8:		return 8;		break;	case SYBDATETIME:		return 8;		break;	case SYBDATETIME4:		return 4;		break;	case SYBBIT:		return 1;		break;	case SYBBITN:		return 1;		break;	case SYBMONEY:		return 8;		break;	case SYBMONEY4:		return 4;		break;	case SYBUNIQUE:		return 16;		break;	/* this strange type is used just for null placeholder in rpc */	case SYBVOID:		return 0;		break;	default:		return -1;		break;	}}/* * For UTF-8 and similar, tds_iconv() may encounter a partial sequence when the chunk boundary * is not aligned with the character boundary.  In that event, it will return an error, and * some number of bytes (less than a character) will remain in the tail end of temp[].  They are   * moved to the beginning, ptemp is adjusted to point just behind them, and the next chunk is read. */static intread_and_convert(TDSSOCKET * tds, const TDSICONV * char_conv, size_t * wire_size, char **outbuf,		 size_t * outbytesleft){	TEMP_INIT(256);	/*	 * temp (above) is the "preconversion" buffer, the place where the UCS-2 data 	 * are parked before converting them to ASCII.  It has to have a size, 	 * and there's no advantage to allocating dynamically.  	 * This also avoids any memory allocation error.  	 */	const char *bufp;	size_t bufleft = 0;	const size_t max_output = *outbytesleft;	/* cast away const for message suppression sub-structure */	TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress;	memset(suppress, 0, sizeof(char_conv->suppress));		for (bufp = temp; *wire_size > 0 && *outbytesleft > 0; bufp = temp + bufleft) {		assert(bufp >= temp);		/* read a chunk of data */		bufleft = TEMP_SIZE - bufleft;		if (bufleft > *wire_size)			bufleft = *wire_size;		tds_get_n(tds, (char *) bufp, bufleft);		*wire_size -= bufleft;		bufleft += bufp - temp;		/* Convert chunk and write to dest. */		bufp = temp; /* always convert from start of buffer */		suppress->einval = *wire_size > 0; /* EINVAL matters only on the last chunk. */		if ((size_t)-1 == tds_iconv(tds, char_conv, to_client, &bufp, &bufleft, outbuf, outbytesleft)) {			tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: tds_iconv returned errno %d\n", errno);			if (errno != EILSEQ) {				tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: "							     "Gave up converting %u bytes due to error %d.\n",							     (unsigned int) bufleft, errno);				tdsdump_dump_buf(TDS_DBG_NETWORK, "Troublesome bytes:", bufp, bufleft);			}			if (bufp == temp) {	/* tds_iconv did not convert anything, avoid infinite loop */				tdsdump_log(TDS_DBG_NETWORK, "No conversion possible: draining remaining %u bytes.\n",							     (unsigned int) *wire_size);				tds_get_n(tds, NULL, *wire_size); /* perhaps we should read unconverted data into outbuf? */				*wire_size = 0;				break;			}							if (bufleft) {				memmove(temp, bufp, bufleft);			}		}	}	assert(*wire_size == 0 || *outbytesleft == 0);	TEMP_FREE;	return max_output - *outbytesleft;}/** @} */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -