Logo Search packages:      
Sourcecode: unixodbc version File versions  Download package

utility.c

/***************************************************************************  
 * Copyright (C) 1995-2002 MySQL AB, www.mysql.com                         *
 *                                                                         *
 * This program is free software; you can redistribute it and/or modify    *
 * it under the terms of the GNU General Public License as published by    *
 * the Free Software Foundation; either version 2 of the License, or       *
 * (at your option) any later version.                                     *
 *                                                                         *
 * This program 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 General Public License for more details.                            *
 *                                                                         *
 * You should have received a copy of the GNU General Public License       *
 * along with this program; if not, write to the Free Software Foundation  *
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA           *
****************************************************************************/

/*************************************************************************** 
 * UTILITY.C                                                               *
 *                                                                         *
 * @description: Utility functions                                         *
 *                                                                         *
 * @author     : MySQL AB(monty@mysql.com, venu@mysql.com)                 *
 * @date       : 2001-Aug-15                                               *
 * @product    : myodbc3                                                   *
 *                                                                         *
****************************************************************************/

#include "myodbc3.h"
#include "errmsg.h"
#include "m_ctype.h"

/*
  @type    : myodbc internal
  @purpose : executes the specified sql statement
*/

SQLRETURN odbc_stmt(DBC FAR *dbc, const char *query)
{
  SQLRETURN result=SQL_SUCCESS;
  DBUG_ENTER("odbc_stmt");
  DBUG_PRINT("enter",("stmt: %s",query));

  pthread_mutex_lock(&dbc->lock);
  if (check_if_server_is_alive(dbc) ||
      mysql_real_query(&dbc->mysql,query,strlen(query)))
  {
    result = set_handle_error(SQL_HANDLE_DBC,dbc,MYERR_S1000,
            mysql_error(&dbc->mysql),
            mysql_errno(&dbc->mysql));
  }
  pthread_mutex_unlock(&dbc->lock);
  DBUG_RETURN(result);
}

/*
  @type    : myodbc internal
  @purpose : use own fields instead of sql fields
*/

void mysql_link_fields(STMT *stmt,MYSQL_FIELD *fields,uint field_count)
{
  MYSQL_RES *result=stmt->result;
  result->fields=fields;
  result->field_count=field_count;
  result->current_field=0;
  fix_result_types(stmt);
}

/*
  @type    : myodbc internal
  @purpose : fixes the result types
*/

void fix_result_types(STMT *stmt)
{
  uint i;
  MYSQL_RES *result=stmt->result;
  DBUG_ENTER("fix_result_types");

  stmt->state=ST_EXECUTED;  /* Mark set found */
  if ((stmt->odbc_types=(SQLSMALLINT*)
       my_malloc(sizeof(SQLSMALLINT)*result->field_count, MYF(0))))
  {
    for (i=0 ; i < result->field_count ; i++)
    {
      MYSQL_FIELD *field=result->fields+i;
      stmt->odbc_types[i]=(SQLSMALLINT) unireg_to_c_datatype(field);
    }
  }
  /*
    Fix default values for bound columns
    Normally there isn't any bound columns at this stage !
  */
  if (stmt->bind)
  {
    if (stmt->bound_columns < result->field_count)
    {
      if (!(stmt->bind=(BIND*) my_realloc((char*) stmt->bind,
            sizeof(BIND) * result->field_count,
            MYF(MY_FREE_ON_ERROR))))
      {
  /* We should in principle give an error here */
  stmt->bound_columns=0;
  DBUG_VOID_RETURN;
      }
      bzero((gptr) (stmt->bind+stmt->bound_columns),
      (result->field_count -stmt->bound_columns)*sizeof(BIND));
      stmt->bound_columns=result->field_count;
    }
    /* Fix default types and pointers to fields */

    mysql_field_seek(result,0);
    for (i=0; i < result->field_count ; i++)
    {
      if (stmt->bind[i].fCType == SQL_C_DEFAULT)
  stmt->bind[i].fCType=stmt->odbc_types[i];
      stmt->bind[i].field=mysql_fetch_field(result);
    }
  }
  DBUG_VOID_RETURN;
}

/*
  @type    : myodbc internal
  @purpose : change a string + length to a zero terminated string
*/

char *fix_str(char *to,char *from,int length)
{
  if (!from)
    return "";
  if (length == SQL_NTS)
    return from;
  strmake(to,from,length);
  return to;
}

/*
  @type    : myodbc internal
  @purpose : duplicate the string
*/

char *dupp_str(char *from,int length)
{
  char *to;
  if (!from)
    return my_strdup("",MYF(MY_WME));
  if (length == SQL_NTS)
    length=strlen(from);
  if ((to=my_malloc(length+1,MYF(MY_WME))))
  {
    memcpy(to,from,length);
    to[length]=0;
  }
  return to;
}

/*
  @type    : myodbc internal
  @purpose : returns 1 if from is a null pointer or a empty string
*/

bool empty_str(char *from,int length)
{
  if (!from)
    return 1;
  if (length == SQL_NTS)
    return from[0] == 0;
  return !length;
}

/*
  @type    : myodbc internal
  @purpose : copies the string data to rgbValue buffer. If rgbValue
       is NULL, then returns warning with full length, else
       copies the cbValueMax length from 'src' and returns it.
*/

SQLRETURN copy_str_data(SQLSMALLINT HandleType, SQLHANDLE Handle,
      SQLCHAR FAR *rgbValue,
      SQLSMALLINT cbValueMax,
      SQLSMALLINT FAR *pcbValue,char FAR *src)
{
  SQLSMALLINT dummy;

  if (!pcbValue)
    pcbValue = &dummy;

  if (cbValueMax == SQL_NTS)
    cbValueMax = *pcbValue = strlen(src);

  else if (cbValueMax < 0)
    return set_handle_error(HandleType,Handle,MYERR_S1090,NULL,0);

  else
  {
    cbValueMax = cbValueMax - 1;
    *pcbValue = strlen(src);
  }

  if (rgbValue)
    strmake(rgbValue, src, cbValueMax);

  if (min(*pcbValue , cbValueMax ) != *pcbValue)
    return (SQL_SUCCESS_WITH_INFO);

  return (SQL_SUCCESS);
}

/*
  @type    : myodbc internal
  @purpose : returns (possibly truncated) results
       if result is truncated the result length contains
       length of the truncted result
*/

SQLRETURN
copy_lresult(DBC FAR *dbc,SQLCHAR FAR *rgbValue,
       SQLINTEGER cbValueMax,
       SQLINTEGER FAR *pcbValue,char *src,long src_length,
       long max_length,long fill_length,long *offset,
       my_bool binary_data)
{
  char *dst=(char*) rgbValue;
  ulong length;

  if (src && src_length == SQL_NTS)
    src_length= strlen(src);

  if (cbValueMax && !binary_data)   /* If not length check */
    cbValueMax--;       /* Room for end null */
  else if (!cbValueMax)
    dst=0;          /* Don't copy anything! */
  if (max_length)       /* If limit on char lengths */
  {
    set_if_smaller(cbValueMax,(long) max_length);
    set_if_smaller(src_length,max_length);
    set_if_smaller(fill_length,max_length);
  }
  if (fill_length < src_length || !dbc || !(dbc->flag & FLAG_PAD_SPACE))
    fill_length=src_length;
  if (*offset == (ulong) ~0L)
    *offset=0;          /* First call */
  else if (*offset >= fill_length)
    return SQL_NO_DATA_FOUND;
  src+= *offset ; src_length-= *offset; fill_length-= *offset;
  length=min(fill_length, cbValueMax);
  (*offset)+=length;        /* Fix for next call */
  if (pcbValue)
    *pcbValue=fill_length;
  if (dst)          /* Bind allows null pointers */
  {
    ulong copy_length= ((long) src_length >= (long) length ? length :
      ((long) src_length >= 0 ? src_length : 0L));
    memcpy(dst,src,copy_length);
    bfill(dst+copy_length,length-copy_length,' ');
    if (!binary_data || length != (ulong) cbValueMax)
      dst[length]=0;
  }
  if (!dst || cbValueMax >= fill_length)
    return SQL_SUCCESS;
  DBUG_PRINT("info",("Returned %ld characters from offset: %ld",
         length,*offset - length));
  if (dbc)
    set_dbc_error(dbc,"01004","Data truncated",4002);
  return SQL_SUCCESS_WITH_INFO;
}

/*
  @type    : myodbc internal
  @purpose : is used when converting a binary string to a SQL_C_CHAR
*/

extern char NEAR _dig_vec[];

SQLRETURN
copy_binary_result(DBC FAR *dbc,SQLCHAR FAR *rgbValue,SQLINTEGER cbValueMax,
       SQLINTEGER FAR *pcbValue,char *src,ulong src_length,
       ulong max_length,ulong *offset)
{
  char *dst=(char*) rgbValue;
  ulong length;

  if (!cbValueMax)
    dst=0;      /* Don't copy anything! */
  if (max_length) /* If limit on char lengths */
  {
    set_if_smaller(cbValueMax,(long) max_length+1);
    set_if_smaller(src_length,(max_length+1)/2);
  }
  if (*offset == (ulong) ~0L)
    *offset=0;    /* First call */
  else if (*offset >= src_length)
    return SQL_NO_DATA_FOUND;
  src+= *offset ; src_length-= *offset;
  length=min(src_length,(ulong) (cbValueMax-1)/2);
  (*offset)+=length;      /* Fix for next call */
  if (pcbValue)
    *pcbValue=src_length*2;
  if (dst)      /* Bind allows null pointers */
  {
    ulong i;
    for (i=0 ; i < length ; i++)
    {
      *dst++= _dig_vec[(uchar) *src >> 4];
      *dst++= _dig_vec[(uchar) *src++ & 15];
    }
    *dst=0;
  }
  if (!dst || (ulong) cbValueMax > length*2)
    return SQL_SUCCESS;
  DBUG_PRINT("info",("Returned %ld characters from offset: %ld",
         length,*offset - length));
  if (dbc)
    set_dbc_error(dbc,"01004","Data truncated",4002);
  return SQL_SUCCESS_WITH_INFO;
}

/*
  @type    : myodbc internal
  @purpose : get type, transfer length and precision for a unireg column
       note that timestamp is changed to YYYY-MM-DD HH:MM:SS type
*/

int unireg_to_sql_datatype(STMT FAR *stmt, MYSQL_FIELD *field, char *buff,
         ulong *transfer_length, ulong *precision,
         ulong *display_size)
{
  char *pos;
  if (stmt->dbc->flag & (FLAG_FIELD_LENGTH | FLAG_SAFE))
    *transfer_length= *precision= *display_size= max(field->length,
                 field->max_length);
  else
    *transfer_length= *precision= *display_size= field->max_length;

  switch(field->type) {
  case FIELD_TYPE_DECIMAL:
    *display_size= max(field->length,field->max_length) -
      test(!(field->flags & UNSIGNED_FLAG)) -
      test(field->decimals);
    *precision= *display_size;
    if (buff) strmov(buff,"decimal");
    return SQL_DECIMAL;
  case FIELD_TYPE_CHAR:
    if (buff)
    {
      pos=strmov(buff,"tinyint");
      if (field->flags & UNSIGNED_FLAG)
  strmov(pos," unsigned");
    }
    *transfer_length=1;
    return SQL_TINYINT;
  case FIELD_TYPE_SHORT:
    if (buff)
    {
      pos=strmov(buff,"smallint");
      if (field->flags & UNSIGNED_FLAG)
  strmov(pos," unsigned");
    }
    *transfer_length=2;
    return SQL_SMALLINT;
  case FIELD_TYPE_INT24:
    if (buff)
    {
      pos=strmov(buff,"mediumint");
      if (field->flags & UNSIGNED_FLAG)
  strmov(pos," unsigned");
    }
    *transfer_length=4;
    return SQL_INTEGER;
  case FIELD_TYPE_LONG:
    if (buff)
    {
      pos= strmov(buff,"integer");
      if (field->flags & UNSIGNED_FLAG)
  strmov(pos," unsigned");
    }
    *transfer_length=4;
    return SQL_INTEGER;
  case FIELD_TYPE_LONGLONG:
    if (buff)
    {
      pos= strmov(buff,"bigint");
      if (field->flags & UNSIGNED_FLAG)
  strmov(pos," unsigned");
    }
    *transfer_length=20;
    return (stmt->dbc->flag & FLAG_NO_BIGINT) ? SQL_INTEGER : SQL_BIGINT;
  case FIELD_TYPE_FLOAT:
    if (buff)
    {
      pos= strmov(buff,"float");
      if (field->flags & UNSIGNED_FLAG)
  strmov(pos," unsigned");
    }
    *transfer_length=4;
    return SQL_REAL;
  case FIELD_TYPE_DOUBLE:
    if (buff)
    {
      pos= strmov(buff,"double");
      if (field->flags & UNSIGNED_FLAG)
  strmov(pos," unsigned");
    }
    *transfer_length=8;
    return SQL_DOUBLE;
  case FIELD_TYPE_NULL:
    if (buff) strmov(buff,"null");
    return SQL_VARCHAR;
  case FIELD_TYPE_YEAR:
    if (buff)
      pos=strmov(buff,"year");
    *transfer_length=2;
    return SQL_SMALLINT;
  case FIELD_TYPE_TIMESTAMP:
    if (buff) strmov(buff,"timestamp");
    *transfer_length=16;      /* size of timestamp_struct */
    *precision= *display_size=19;
    if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC3)
      return SQL_TYPE_TIMESTAMP;
    return SQL_TIMESTAMP;
  case FIELD_TYPE_DATETIME:
    if (buff) strmov(buff,"datetime");
    *transfer_length=16;      /* size of timestamp_struct */
    *precision= *display_size=19;
    if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC3)
      return SQL_TYPE_TIMESTAMP;
    return SQL_TIMESTAMP;
  case FIELD_TYPE_NEWDATE:
  case FIELD_TYPE_DATE:
    if (buff) strmov(buff,"date");
    *transfer_length=6;       /* size of date struct */
    *precision= *display_size=10;
    if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC3)
      return SQL_TYPE_DATE;
    return SQL_DATE;
  case FIELD_TYPE_TIME:
    if (buff) strmov(buff,"time");
    *transfer_length=6;       /* size of time struct */
    *precision= *display_size=8;
    if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC3)
      return SQL_TYPE_TIME;
    return SQL_TIME;
  case FIELD_TYPE_STRING:
    if (buff) strmov(buff,"char");
    return SQL_CHAR;
  case FIELD_TYPE_VAR_STRING:
    if (buff) strmov(buff,"varchar");
    return SQL_VARCHAR;
  case FIELD_TYPE_TINY_BLOB:
    if (buff)
      strmov(buff,(field->flags & BINARY_FLAG) ? "tinyblob" : "tinytext");
    if (stmt->dbc->flag & (FLAG_FIELD_LENGTH | FLAG_SAFE))
      *transfer_length= *precision= *display_size= 255;
    return (field->flags & BINARY_FLAG) ? SQL_LONGVARBINARY : SQL_LONGVARCHAR;
  case FIELD_TYPE_BLOB:
    if (buff)
      strmov(buff,(field->flags & BINARY_FLAG) ? "blob" : "text");
    if (stmt->dbc->flag & (FLAG_FIELD_LENGTH | FLAG_SAFE))
      *transfer_length= *precision= *display_size= 65535;
    return (field->flags & BINARY_FLAG) ? SQL_LONGVARBINARY : SQL_LONGVARCHAR;
  case FIELD_TYPE_MEDIUM_BLOB:
    if (buff)
      strmov(buff,((field->flags & BINARY_FLAG) ? "mediumblob" : "mediumtext"));
    if (stmt->dbc->flag & (FLAG_FIELD_LENGTH | FLAG_SAFE))
      *transfer_length= *precision= *display_size= (1L << 24)-1L;
    return (field->flags & BINARY_FLAG) ? SQL_LONGVARBINARY : SQL_LONGVARCHAR;
  case FIELD_TYPE_LONG_BLOB:
    if (buff)
      strmov(buff,((field->flags & BINARY_FLAG) ? "longblob": "longtext"));
    if (stmt->dbc->flag & (FLAG_FIELD_LENGTH | FLAG_SAFE))
      *transfer_length= *precision= *display_size= INT_MAX32;
    return (field->flags & BINARY_FLAG) ? SQL_LONGVARBINARY : SQL_LONGVARCHAR;
  case FIELD_TYPE_ENUM:
    if (buff) strmov(buff,"enum");
    return SQL_CHAR;
  case FIELD_TYPE_SET:
    if (buff) strmov(buff,"set");
    return SQL_CHAR;
  }
  return 0; /* Impossible */
}

/*
  @type    : myodbc internal
  @purpose : returns internal type to C type
*/

int unireg_to_c_datatype(MYSQL_FIELD *field)
{
  switch(field->type) {
  case FIELD_TYPE_LONGLONG:     /* Must be returned as char */
  default:
    return SQL_C_CHAR;
  case FIELD_TYPE_CHAR:
    return SQL_C_TINYINT;
  case FIELD_TYPE_YEAR:
  case FIELD_TYPE_SHORT:
    return SQL_C_SHORT;
  case FIELD_TYPE_INT24:
  case FIELD_TYPE_LONG:
    return SQL_C_LONG;
  case FIELD_TYPE_FLOAT:
    return SQL_C_FLOAT;
  case FIELD_TYPE_DOUBLE:
    return SQL_C_DOUBLE;
  case FIELD_TYPE_TIMESTAMP:
  case FIELD_TYPE_DATETIME:
    return SQL_C_TIMESTAMP;
  case FIELD_TYPE_NEWDATE:
  case FIELD_TYPE_DATE:
    return SQL_C_DATE;
  case FIELD_TYPE_TIME:
    return SQL_C_TIME;
  }
}

/*
  @type    : myodbc internal
  @purpose : returns default C type for a given SQL type
*/

int default_c_type(int sql_data_type)
{
  switch (sql_data_type) {
  case SQL_CHAR:
  case SQL_VARCHAR:
  case SQL_LONGVARCHAR:
  case SQL_DECIMAL:
  case SQL_NUMERIC:
  default:
    return SQL_C_CHAR;
  case SQL_BIGINT:
    return SQL_C_SBIGINT;
  case SQL_BIT:
    return SQL_C_BIT;
  case SQL_TINYINT:
    return SQL_C_TINYINT;
  case SQL_SMALLINT:
    return SQL_C_SHORT;
  case SQL_INTEGER:
    return SQL_C_LONG;
  case SQL_REAL:
  case SQL_FLOAT:
    return SQL_C_FLOAT;
  case SQL_DOUBLE:
    return SQL_C_DOUBLE;
  case SQL_BINARY:
  case SQL_VARBINARY:
  case SQL_LONGVARBINARY:
    return SQL_C_BINARY;
  case SQL_DATE:
  case SQL_TYPE_DATE:
    return SQL_C_DATE;
  case SQL_TIME:
  case SQL_TYPE_TIME:
    return SQL_C_TIME;
  case SQL_TIMESTAMP:
  case SQL_TYPE_TIMESTAMP:
    return SQL_C_TIMESTAMP;
  }
}

/*
  @type    : myodbc internal
  @purpose : returns bind length
*/

ulong bind_length(int sql_data_type,ulong length)
{
  switch (sql_data_type) {
  default:          /* For CHAR, VARCHAR, BLOB...*/
    return length;
  case SQL_C_BIT:
  case SQL_C_TINYINT:
  case SQL_C_STINYINT:
  case SQL_C_UTINYINT:
    return 1;
  case SQL_C_SHORT:
  case SQL_C_SSHORT:
  case SQL_C_USHORT:
    return 2;
  case SQL_C_LONG:
  case SQL_C_SLONG:
  case SQL_C_ULONG:
    return sizeof(long);
  case SQL_C_FLOAT:
    return sizeof(float);
  case SQL_C_DOUBLE:
    return sizeof(double);
  case SQL_C_DATE:
  case SQL_C_TYPE_DATE:
    return sizeof(DATE_STRUCT);
  case SQL_C_TIME:
  case SQL_C_TYPE_TIME:
    return sizeof(TIME_STRUCT);
  case SQL_C_TIMESTAMP:
  case SQL_C_TYPE_TIMESTAMP:
    return sizeof(TIMESTAMP_STRUCT);
  case SQL_C_SBIGINT:
  case SQL_C_UBIGINT:
    return sizeof(longlong);
  }
}

/*
  @type    : myodbc internal
  @purpose : convert a possible string to a data value
*/

my_bool str_to_date(DATE_STRUCT *rgbValue, const char *str,uint length)
{
  uint field_length,year_length,digits,i,date[3];
  const char *pos;
  const char *end=str+length;
  for (; !isdigit(*str) && str != end ; str++) ;
  /*
    Calculate first number of digits.
    If length= 4, 8 or >= 14 then year is of format YYYY
    (YYYY-MM-DD,  YYYYMMDD)
  */
  for (pos=str; pos != end && isdigit(*pos) ; pos++) ;
  digits= (uint) (pos-str);
  year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
  field_length=year_length-1;

  for (i=0 ; i < 3 && str != end; i++)
  {
    uint tmp_value=(uint) (uchar) (*str++ - '0');
    while (str != end && isdigit(str[0]) && field_length--)
    {
      tmp_value=tmp_value*10 + (uint) (uchar) (*str - '0');
      str++;
    }
    date[i]=tmp_value;
    while (str != end && !isdigit(*str))
      str++;
    field_length=1;   /* Rest fields can only be 2 */
  }
  if (i <= 1 || date[1] == 0)   /* Wrong date */
    return 1;
  while (i < 3)
    date[i++]=1;

  rgbValue->year=date[0];
  rgbValue->month=date[1];
  rgbValue->day=date[2];
  return 0;
}

/*
  @type    : myodbc internal
  @purpose : convert a time string to a (ulong) value.
       At least following formats are recogniced
       HHMMSS HHMM HH HH.MM.SS   {t HH:MM:SS }
  @return  : HHMMSS
*/

ulong str_to_time(const char *str,uint length)
{
  uint i,date[3];
  const char *end=str+length;
  for (; !isdigit(*str) && str != end ; str++) ;

  for (i=0 ; i < 3 && str != end; i++)
  {
    uint tmp_value=(uint) (uchar) (*str++ - '0');
    if (str != end && isdigit(str[0]))
    {
      tmp_value=tmp_value*10 + (uint) (uchar) (*str - '0');
      str++;
    }
    date[i]=tmp_value;
    while (str != end && !isdigit(*str))
      str++;
  }
  while (i < 3)
    date[i++]=0;
  return (ulong) date[0]*10000L + (ulong) (date[1]*100+date[2]);
}

/*
  @type    : myodbc internal
  @purpose : if there was a long time since last question, check that
       the server is up with mysql_ping (to force a reconnect)
*/

int check_if_server_is_alive(DBC FAR *dbc)
{
  time_t seconds= (time_t) time((time_t*) 0);
  int result=0;
  if ((ulong) (seconds - dbc->last_query_time) >= CHECK_IF_ALIVE)
  {
    if (mysql_ping(&dbc->mysql) &&
  mysql_errno(&dbc->mysql) == CR_SERVER_GONE_ERROR)
      result=1;
  }
  dbc->last_query_time=seconds;
  return result;
}

/*
  @type    : myodbc3 internal
  @purpose : appends quoted string to dynamic string
*/

my_bool dynstr_append_quoted_name(DYNAMIC_STRING *str, const char *name)
{
  uint tmp=strlen(name);
  char *pos;
  if (dynstr_realloc(str,tmp+3))
    return 1;
  pos=str->str+str->length;
  *pos='`';
  memcpy(pos+1,name,tmp);
  pos[tmp+1]='`';
  pos[tmp+2]=0;         /* Safety */
  str->length+=tmp+2;
  return 0;
}


#ifndef _UNIX_

/* Define functions that dosen't exist in a dll */

/* _exit is called by safemalloc, mystatic & my_malloc */
#ifndef __WIN__
void exit(int exit)
{
  abort();
}
#endif /* !__WIN__ */

/* perror is called by dbug.c */
void perror(const char *str)
{
}

/* clock is called by dbug.c when profiling */
long clock(void)
{
  return 0L;
}


#ifndef THREAD
long getpid()
{
  return 0;
}
#else

int pthread_dummy(int return_value)
{
  return return_value;
}

#endif /* !THREAD */
#endif /* !_UNIX_ */

Generated by  Doxygen 1.6.0   Back to index