1/*
2 *  info.c
3 *
4 *  $Id: info.c,v 1.36 2007/01/05 12:22:39 source Exp $
5 *
6 *  Information functions
7 *
8 *  The iODBC driver manager.
9 *
10 *  Copyright (C) 1995 by Ke Jin <kejin@empress.com>
11 *  Copyright (C) 1996-2006 by OpenLink Software <iodbc@openlinksw.com>
12 *  All Rights Reserved.
13 *
14 *  This software is released under the terms of either of the following
15 *  licenses:
16 *
17 *      - GNU Library General Public License (see LICENSE.LGPL)
18 *      - The BSD License (see LICENSE.BSD).
19 *
20 *  Note that the only valid version of the LGPL license as far as this
21 *  project is concerned is the original GNU Library General Public License
22 *  Version 2, dated June 1991.
23 *
24 *  While not mandated by the BSD license, any patches you make to the
25 *  iODBC source code may be contributed back into the iODBC project
26 *  at your discretion. Contributions will benefit the Open Source and
27 *  Data Access community as a whole. Submissions may be made at:
28 *
29 *      http://www.iodbc.org
30 *
31 *
32 *  GNU Library Generic Public License Version 2
33 *  ============================================
34 *  This library is free software; you can redistribute it and/or
35 *  modify it under the terms of the GNU Library General Public
36 *  License as published by the Free Software Foundation; only
37 *  Version 2 of the License dated June 1991.
38 *
39 *  This library is distributed in the hope that it will be useful,
40 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
41 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
42 *  Library General Public License for more details.
43 *
44 *  You should have received a copy of the GNU Library General Public
45 *  License along with this library; if not, write to the Free
46 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
47 *
48 *
49 *  The BSD License
50 *  ===============
51 *  Redistribution and use in source and binary forms, with or without
52 *  modification, are permitted provided that the following conditions
53 *  are met:
54 *
55 *  1. Redistributions of source code must retain the above copyright
56 *     notice, this list of conditions and the following disclaimer.
57 *  2. Redistributions in binary form must reproduce the above copyright
58 *     notice, this list of conditions and the following disclaimer in
59 *     the documentation and/or other materials provided with the
60 *     distribution.
61 *  3. Neither the name of OpenLink Software Inc. nor the names of its
62 *     contributors may be used to endorse or promote products derived
63 *     from this software without specific prior written permission.
64 *
65 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
66 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
67 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
68 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OPENLINK OR
69 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
70 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
71 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
72 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
73 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
74 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
75 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
76 */
77
78
79#include <iodbc.h>
80
81#include <sql.h>
82#include <sqlext.h>
83#include <sqlucode.h>
84#include <odbcinst.h>
85
86#include <unicode.h>
87
88#include <dlproc.h>
89
90#include <herr.h>
91#include <henv.h>
92#include <hdbc.h>
93#include <hstmt.h>
94
95#include <itrace.h>
96
97#include <stdio.h>
98#include <ctype.h>
99
100#ifdef WIN32
101#define SECT1			"ODBC 32 bit Data Sources"
102#define SECT2			"ODBC 32 bit Drivers"
103#else
104#define SECT1			"ODBC Data Sources"
105#define SECT2			"ODBC Drivers"
106#endif
107
108#define MAX_ENTRIES		1024
109
110
111static int
112stricmp (const char *s1, const char *s2)
113{
114  int cmp;
115
116  while (*s1)
117    {
118      if ((cmp = toupper (*s1) - toupper (*s2)) != 0)
119	return cmp;
120      s1++;
121      s2++;
122    }
123  return (*s2) ? -1 : 0;
124}
125
126
127static int
128SectSorter (const void *p1, const void *p2)
129{
130  const char **s1 = (const char **) p1;
131  const char **s2 = (const char **) p2;
132
133  return stricmp (*s1, *s2);
134}
135
136
137SQLRETURN SQL_API
138SQLDataSources_Internal (
139  SQLHENV		  henv,
140  SQLUSMALLINT		  fDir,
141  SQLPOINTER		  szDSN,
142  SQLSMALLINT		  cbDSNMax,
143  SQLSMALLINT 		* pcbDSN,
144  SQLPOINTER		  szDesc,
145  SQLSMALLINT		  cbDescMax,
146  SQLSMALLINT 		* pcbDesc,
147  SQLCHAR		  waMode)
148{
149  GENV (genv, henv);
150  char buffer[4096], desc[1024], *ptr;
151  int i, j, usernum = 0;
152  static int cur_entry = -1;
153  static int num_entries = 0;
154  static void **sect = NULL;
155  SQLUSMALLINT fDirOld = fDir;
156
157  waMode = waMode; /*UNUSED*/
158
159  /* check argument */
160  if (cbDSNMax < 0 || cbDescMax < 0)
161    {
162      PUSHSQLERR (genv->herr, en_S1090);
163      return SQL_ERROR;
164    }
165
166  if (fDir != SQL_FETCH_FIRST && fDir != SQL_FETCH_NEXT &&
167      fDir != SQL_FETCH_FIRST_USER && fDir != SQL_FETCH_FIRST_SYSTEM)
168    {
169      PUSHSQLERR (genv->herr, en_S1103);
170      return SQL_ERROR;
171    }
172
173  if (cur_entry < 0 || fDir == SQL_FETCH_FIRST ||
174      fDir == SQL_FETCH_FIRST_USER || fDir == SQL_FETCH_FIRST_SYSTEM)
175    {
176      cur_entry = 0;
177      num_entries = 0;
178
179      /*
180       *  Free old section list
181       */
182      if (sect)
183	{
184	  for (i = 0; i < MAX_ENTRIES; i++)
185	    if (sect[i])
186	      free (sect[i]);
187	  free (sect);
188	}
189      if ((sect = (void **) calloc (MAX_ENTRIES, sizeof (void *))) == NULL)
190	{
191	  PUSHSQLERR (genv->herr, en_S1011);
192	  return SQL_ERROR;
193	}
194
195      if (fDirOld == SQL_FETCH_FIRST)
196        fDir = SQL_FETCH_FIRST_USER;
197
198      do {
199        SQLSetConfigMode (fDir == SQL_FETCH_FIRST_SYSTEM ? ODBC_SYSTEM_DSN : ODBC_USER_DSN);
200        SQLGetPrivateProfileString (SECT1, NULL, "", buffer, sizeof(buffer) / sizeof(SQLTCHAR), "odbc.ini");
201
202        /* For each datasources */
203        for(ptr = buffer, i = 1 ; *ptr && i ; ptr += STRLEN(ptr) + 1)
204          {
205            /* Add this section to the datasources list */
206            if (fDirOld == SQL_FETCH_FIRST && fDir == SQL_FETCH_FIRST_SYSTEM)
207              {
208                for(j = 0 ; j<usernum ; j++)
209                  {
210                    if(STREQ(sect[j<<1], ptr))
211                      j = usernum;
212                  }
213                if(j == usernum + 1)
214                  continue;
215              }
216
217            if ((num_entries << 1) >= MAX_ENTRIES)
218              {
219                i = 0;
220                break;
221              }			/* Skip the rest */
222
223            /* Copy the datasource name */
224            sect[num_entries<<1] = STRDUP (ptr);
225
226            /* ... and its description */
227            SQLSetConfigMode (fDir == SQL_FETCH_FIRST_SYSTEM ? ODBC_SYSTEM_DSN : ODBC_USER_DSN);
228            SQLGetPrivateProfileString (SECT1, ptr, "", desc, sizeof(desc) / sizeof(SQLTCHAR), "odbc.ini");
229            sect[(num_entries++<<1) + 1] = STRDUP (desc);
230          }
231
232        switch(fDir)
233          {
234            case SQL_FETCH_FIRST_USER:
235              fDir = SQL_FETCH_FIRST_SYSTEM;
236              usernum = num_entries;
237              break;
238            case SQL_FETCH_FIRST_SYSTEM:
239              fDir = SQL_FETCH_FIRST;
240              break;
241          };
242      } while (fDir!=SQL_FETCH_FIRST && fDirOld==SQL_FETCH_FIRST);
243
244      fDir = fDirOld;
245
246      /*
247       *  Sort all entries so we can present a nice list
248       */
249      if (num_entries > 1)
250	{
251          qsort (sect, num_entries, sizeof (char **) + sizeof (char **),
252            SectSorter);
253	}
254    }
255
256  /*
257   *  Try to get to the next item
258   */
259  if (cur_entry >= num_entries)
260    {
261      cur_entry = 0;		/* Next time, start all over again */
262      return SQL_NO_DATA_FOUND;
263    }
264
265  /*
266   *  Copy DSN information
267   */
268  STRNCPY (szDSN, sect[cur_entry << 1], cbDSNMax);
269
270  if (pcbDSN)
271    *pcbDSN = STRLEN (szDSN);
272
273  /*
274   *  And find the description that goes with this entry
275   */
276  STRNCPY (szDesc, sect[(cur_entry << 1) + 1], cbDescMax);
277
278  if (pcbDesc)
279    *pcbDesc = STRLEN (szDesc);
280
281  /*
282   *  Next record
283   */
284  cur_entry++;
285
286  return SQL_SUCCESS;
287}
288
289
290SQLRETURN SQL_API
291SQLDataSources (
292  SQLHENV		  henv,
293  SQLUSMALLINT		  fDir,
294  SQLCHAR 		* szDSN,
295  SQLSMALLINT		  cbDSNMax,
296  SQLSMALLINT 		* pcbDSN,
297  SQLCHAR 		* szDesc,
298  SQLSMALLINT		  cbDescMax,
299  SQLSMALLINT 		* pcbDesc)
300{
301  ENTER_HENV (henv,
302    trace_SQLDataSources (TRACE_ENTER,
303    	henv,
304	fDir,
305	szDSN, cbDSNMax, pcbDSN,
306	szDesc, cbDescMax, pcbDesc));
307
308  retcode = SQLDataSources_Internal (
309  	henv,
310	fDir,
311	szDSN, cbDSNMax, pcbDSN,
312	szDesc, cbDescMax, pcbDesc,
313	'A');
314
315  LEAVE_HENV (henv,
316    trace_SQLDataSources (TRACE_LEAVE,
317    	henv,
318	fDir,
319	szDSN, cbDSNMax, pcbDSN,
320	szDesc, cbDescMax, pcbDesc));
321}
322
323
324#if ODBCVER >= 0x0300
325SQLRETURN SQL_API
326SQLDataSourcesA (
327  SQLHENV		  henv,
328  SQLUSMALLINT		  fDir,
329  SQLCHAR 		* szDSN,
330  SQLSMALLINT		  cbDSNMax,
331  SQLSMALLINT 		* pcbDSN,
332  SQLCHAR 		* szDesc,
333  SQLSMALLINT		  cbDescMax,
334  SQLSMALLINT		* pcbDesc)
335{
336  ENTER_HENV (henv,
337    trace_SQLDataSources (TRACE_ENTER,
338    	henv,
339	fDir,
340	szDSN, cbDSNMax, pcbDSN,
341	szDesc, cbDescMax, pcbDesc));
342
343  retcode = SQLDataSources_Internal(
344  	henv,
345	fDir,
346	szDSN, cbDSNMax, pcbDSN,
347	szDesc, cbDescMax, pcbDesc,
348	'A');
349
350  LEAVE_HENV (henv,
351    trace_SQLDataSources (TRACE_LEAVE,
352    	henv,
353	fDir,
354	szDSN, cbDSNMax, pcbDSN,
355	szDesc, cbDescMax, pcbDesc));
356}
357
358
359SQLRETURN SQL_API
360SQLDataSourcesW (
361  SQLHENV		  henv,
362  SQLUSMALLINT		  fDir,
363  SQLWCHAR 		* szDSN,
364  SQLSMALLINT		  cbDSNMax,
365  SQLSMALLINT 		* pcbDSN,
366  SQLWCHAR 		* szDesc,
367  SQLSMALLINT		  cbDescMax,
368  SQLSMALLINT 		* pcbDesc)
369{
370  SQLCHAR *_DSN = NULL;
371  SQLCHAR *_Desc = NULL;
372
373  ENTER_HENV (henv,
374    trace_SQLDataSourcesW (TRACE_ENTER,
375    	henv,
376	fDir,
377	szDSN, cbDSNMax, pcbDSN,
378	szDesc, cbDescMax, pcbDesc));
379
380  if (cbDSNMax > 0)
381    {
382      if ((_DSN = (SQLCHAR *) malloc (cbDSNMax * UTF8_MAX_CHAR_LEN + 1)) == NULL)
383	{
384	  PUSHSQLERR (genv->herr, en_S1001);
385	  return SQL_ERROR;
386	}
387    }
388
389  if (cbDescMax > 0)
390    {
391      if ((_Desc = (SQLCHAR *) malloc (cbDescMax * UTF8_MAX_CHAR_LEN + 1)) == NULL)
392	{
393	  PUSHSQLERR (genv->herr, en_S1001);
394	  return SQL_ERROR;
395	}
396    }
397
398  retcode = SQLDataSources_Internal (
399  	henv,
400	fDir,
401	_DSN, cbDSNMax * UTF8_MAX_CHAR_LEN, pcbDSN,
402	_Desc, cbDescMax * UTF8_MAX_CHAR_LEN, pcbDesc,
403	'W');
404
405  if (SQL_SUCCEEDED (retcode))
406    {
407      dm_StrCopyOut2_U8toW (_DSN, szDSN, cbDSNMax, pcbDSN);
408      dm_StrCopyOut2_U8toW (_Desc, szDesc, cbDescMax, pcbDesc);
409    }
410
411  MEM_FREE (_DSN);
412  MEM_FREE (_Desc);
413
414  LEAVE_HENV (henv,
415    trace_SQLDataSourcesW (TRACE_LEAVE,
416    	henv,
417	fDir,
418	szDSN, cbDSNMax, pcbDSN,
419	szDesc, cbDescMax, pcbDesc));
420}
421#endif
422
423
424SQLRETURN SQL_API
425SQLDrivers_Internal (
426  SQLHENV		  henv,
427  SQLUSMALLINT		  fDir,
428  SQLPOINTER		  szDrvDesc,
429  SQLSMALLINT		  cbDrvDescMax,
430  SQLSMALLINT 		* pcbDrvDesc,
431  SQLPOINTER		  szDrvAttr,
432  SQLSMALLINT		  cbDrvAttrMax,
433  SQLSMALLINT 		* pcbDrvAttr,
434  SQLCHAR		  waMode)
435{
436  GENV (genv, henv);
437  char buffer[4096], desc[1024], *ptr;
438  int i, j, usernum = 0;
439  static int cur_entry = -1;
440  static int num_entries = 0;
441  static void **sect = NULL;
442  SQLUSMALLINT fDirOld = fDir;
443
444  waMode = waMode; /*UNUSED*/
445
446  /* check argument */
447  if (cbDrvDescMax < 0 || cbDrvAttrMax < 0)
448    {
449      PUSHSQLERR (genv->herr, en_S1090);
450      return SQL_ERROR;
451    }
452
453  if (fDir != SQL_FETCH_FIRST && fDir != SQL_FETCH_NEXT)
454    {
455      PUSHSQLERR (genv->herr, en_S1103);
456      return SQL_ERROR;
457    }
458
459  if (cur_entry < 0 || fDir == SQL_FETCH_FIRST)
460    {
461      cur_entry = 0;
462      num_entries = 0;
463
464      /*
465      *  Free old section list
466      */
467      if (sect)
468	{
469	  for (i = 0; i < MAX_ENTRIES; i++)
470	    if (sect[i])
471	      free (sect[i]);
472	  free (sect);
473	}
474      if ((sect = (void **) calloc (MAX_ENTRIES, sizeof (void *))) == NULL)
475	{
476	  PUSHSQLERR (genv->herr, en_S1011);
477	  return SQL_ERROR;
478	}
479
480      if (fDirOld == SQL_FETCH_FIRST)
481        fDir = SQL_FETCH_FIRST_USER;
482
483      do {
484        SQLSetConfigMode (fDir == SQL_FETCH_FIRST_SYSTEM ? ODBC_SYSTEM_DSN : ODBC_USER_DSN);
485        SQLGetPrivateProfileString (SECT2, NULL, "", buffer, sizeof(buffer) / sizeof(SQLTCHAR), "odbcinst.ini");
486
487        /* For each datasources */
488        for(ptr = buffer, i = 1 ; *ptr && i ; ptr += STRLEN(ptr) + 1)
489          {
490            /* Add this section to the datasources list */
491            if (fDirOld == SQL_FETCH_FIRST && fDir == SQL_FETCH_FIRST_SYSTEM)
492              {
493                for(j = 0 ; j<usernum ; j++)
494                  {
495                    if(STREQ(sect[j<<1], ptr))
496                      j = usernum;
497                  }
498                if(j == usernum + 1)
499                  continue;
500              }
501
502            if ((num_entries << 1) >= MAX_ENTRIES)
503              {
504                i = 0;
505                break;
506              }			/* Skip the rest */
507
508            /* ... and its description */
509            SQLSetConfigMode (fDir == SQL_FETCH_FIRST_SYSTEM ? ODBC_SYSTEM_DSN : ODBC_USER_DSN);
510            SQLGetPrivateProfileString (SECT2, ptr, "", desc, sizeof(desc) / sizeof(SQLTCHAR), "odbcinst.ini");
511
512            /* Check if the driver is installed */
513				if(!STRCASEEQ(desc, "Installed"))
514				  continue;
515
516            /* Copy the driver name */
517            sect[num_entries<<1] = STRDUP (ptr);
518            sect[(num_entries++<<1) + 1] = STRDUP (desc);
519          }
520
521        switch(fDir)
522          {
523            case SQL_FETCH_FIRST_USER:
524              fDir = SQL_FETCH_FIRST_SYSTEM;
525              usernum = num_entries;
526              break;
527            case SQL_FETCH_FIRST_SYSTEM:
528              fDir = SQL_FETCH_FIRST;
529              break;
530          };
531      } while (fDir!=SQL_FETCH_FIRST && fDirOld==SQL_FETCH_FIRST);
532
533      fDir = fDirOld;
534
535      /*
536       *  Sort all entries so we can present a nice list
537       */
538      if (num_entries > 1)
539	{
540          qsort (sect, num_entries, sizeof (char **) + sizeof (char **),
541            SectSorter);
542	}
543    }
544
545  /*
546   *  Try to get to the next item
547   */
548  if (cur_entry >= num_entries)
549    {
550      cur_entry = 0;		/* Next time, start all over again */
551      return SQL_NO_DATA_FOUND;
552    }
553
554  /*
555   *  Copy Driver information
556   */
557  STRNCPY (szDrvDesc, sect[cur_entry << 1], cbDrvDescMax);
558
559  if (pcbDrvDesc)
560    *pcbDrvDesc = STRLEN (szDrvDesc);
561
562  /*
563   *  And find the description that goes with this entry
564   */
565  STRNCPY (szDrvAttr, sect[(cur_entry << 1) + 1], cbDrvAttrMax);
566
567  if (pcbDrvAttr)
568    *pcbDrvAttr = STRLEN (szDrvAttr);
569
570  /*
571   *  Next record
572   */
573  cur_entry++;
574
575  return SQL_SUCCESS;
576}
577
578
579SQLRETURN SQL_API
580SQLDrivers (
581  SQLHENV		  henv,
582  SQLUSMALLINT		  fDir,
583  SQLCHAR 		* szDrvDesc,
584  SQLSMALLINT		  cbDrvDescMax,
585  SQLSMALLINT 		* pcbDrvDesc,
586  SQLCHAR 		* szDrvAttr,
587  SQLSMALLINT		  cbDrvAttrMax,
588  SQLSMALLINT 		* pcbDrvAttr)
589{
590  ENTER_HENV (henv,
591    trace_SQLDrivers (TRACE_ENTER,
592  	henv,
593	fDir,
594	szDrvDesc, cbDrvDescMax, pcbDrvDesc,
595	szDrvAttr, cbDrvAttrMax, pcbDrvAttr));
596
597  retcode = SQLDrivers_Internal(
598  	henv,
599	fDir,
600	szDrvDesc, cbDrvDescMax, pcbDrvDesc,
601	szDrvAttr, cbDrvAttrMax, pcbDrvAttr,
602	'A');
603
604  LEAVE_HENV (henv,
605    trace_SQLDrivers (TRACE_LEAVE,
606  	henv,
607	fDir,
608	szDrvDesc, cbDrvDescMax, pcbDrvDesc,
609	szDrvAttr, cbDrvAttrMax, pcbDrvAttr));
610}
611
612
613#if ODBCVER >= 0x0300
614SQLRETURN SQL_API
615SQLDriversA (
616  SQLHENV		  henv,
617  SQLUSMALLINT		  fDir,
618  SQLCHAR  		* szDrvDesc,
619  SQLSMALLINT		  cbDrvDescMax,
620  SQLSMALLINT 	 	* pcbDrvDesc,
621  SQLCHAR  		* szDrvAttr,
622  SQLSMALLINT		  cbDrvAttrMax,
623  SQLSMALLINT 	 	* pcbDrvAttr)
624{
625  ENTER_HENV (henv,
626    trace_SQLDrivers (TRACE_ENTER,
627  	henv,
628	fDir,
629	szDrvDesc, cbDrvDescMax, pcbDrvDesc,
630	szDrvAttr, cbDrvAttrMax, pcbDrvAttr));
631
632  retcode = SQLDrivers_Internal(
633  	henv,
634	fDir,
635	szDrvDesc, cbDrvDescMax, pcbDrvDesc,
636	szDrvAttr, cbDrvAttrMax, pcbDrvAttr,
637	'A');
638
639  LEAVE_HENV (henv,
640    trace_SQLDrivers (TRACE_LEAVE,
641  	henv,
642	fDir,
643	szDrvDesc, cbDrvDescMax, pcbDrvDesc,
644	szDrvAttr, cbDrvAttrMax, pcbDrvAttr));
645}
646
647
648SQLRETURN SQL_API
649SQLDriversW (SQLHENV henv,
650    SQLUSMALLINT 	  fDir,
651    SQLWCHAR		* szDrvDesc,
652    SQLSMALLINT		  cbDrvDescMax,
653    SQLSMALLINT		* pcbDrvDesc,
654    SQLWCHAR		* szDrvAttr,
655    SQLSMALLINT		  cbDrvAttrMax,
656    SQLSMALLINT		* pcbDrvAttr)
657{
658  SQLCHAR *_Driver = NULL;
659  SQLCHAR *_Attrs = NULL;
660
661  ENTER_HENV (henv,
662    trace_SQLDriversW (TRACE_ENTER,
663  	henv,
664	fDir,
665	szDrvDesc, cbDrvDescMax, pcbDrvDesc,
666	szDrvAttr, cbDrvAttrMax, pcbDrvAttr));
667
668  if (cbDrvDescMax > 0)
669    {
670      if ((_Driver = (SQLCHAR *) malloc (cbDrvDescMax * UTF8_MAX_CHAR_LEN + 1)) == NULL)
671	{
672	  PUSHSQLERR (genv->herr, en_S1001);
673	  return SQL_ERROR;
674	}
675    }
676
677  if (cbDrvAttrMax > 0)
678    {
679      if ((_Attrs = (SQLCHAR *) malloc (cbDrvAttrMax * UTF8_MAX_CHAR_LEN + 1)) == NULL)
680	{
681	  PUSHSQLERR (genv->herr, en_S1001);
682	  return SQL_ERROR;
683	}
684    }
685
686  retcode = SQLDrivers_Internal (
687  	henv,
688	fDir,
689	_Driver, cbDrvDescMax * UTF8_MAX_CHAR_LEN, pcbDrvDesc,
690	_Attrs, cbDrvAttrMax * UTF8_MAX_CHAR_LEN, pcbDrvAttr,
691	'W');
692
693  if (SQL_SUCCEEDED (retcode))
694    {
695      dm_StrCopyOut2_U8toW (_Driver, szDrvDesc, cbDrvDescMax, pcbDrvDesc);
696      dm_StrCopyOut2_U8toW (_Attrs, szDrvAttr, cbDrvAttrMax, pcbDrvAttr);
697    }
698
699  MEM_FREE (_Driver);
700  MEM_FREE (_Attrs);
701
702  LEAVE_HENV (henv,
703    trace_SQLDriversW (TRACE_LEAVE,
704  	henv,
705	fDir,
706	szDrvDesc, cbDrvDescMax, pcbDrvDesc,
707	szDrvAttr, cbDrvAttrMax, pcbDrvAttr));
708}
709#endif
710
711
712SQLRETURN SQL_API
713SQLGetInfo_Internal (
714    SQLHDBC		  hdbc,
715    SQLUSMALLINT	  fInfoType,
716    SQLPOINTER		  rgbInfoValue,
717    SQLSMALLINT		  cbInfoValueMax,
718    SQLSMALLINT		* pcbInfoValue,
719    SQLCHAR		  waMode)
720{
721  CONN (pdbc, hdbc);
722  ENVR (penv, pdbc->henv);
723  STMT (pstmt, NULL);
724  STMT (tpstmt, NULL);
725  HPROC hproc = SQL_NULL_HPROC;
726  SQLRETURN retcode = SQL_SUCCESS;
727  void * _InfoValue = NULL;
728  void * infoValueOut = rgbInfoValue;
729
730  DWORD dword = 0;
731  int size = 0, len = 0, ret = 0;
732  wchar_t buf[20] = {'\0'};
733
734  if (cbInfoValueMax < 0)
735    {
736      PUSHSQLERR (pdbc->herr, en_S1090);
737      return SQL_ERROR;
738    }
739
740#if (ODBCVER < 0x0300)
741  if (				/* fInfoType < SQL_INFO_FIRST || */
742      (fInfoType > SQL_INFO_LAST
743	  && fInfoType < SQL_INFO_DRIVER_START))
744    {
745      PUSHSQLERR (pdbc->herr, en_S1096);
746      return SQL_ERROR;
747    }
748#endif
749  if (fInfoType == SQL_ODBC_VER
750#if (ODBCVER >= 0x0300)
751  	|| fInfoType == SQL_DM_VER
752#endif
753	)
754    {
755#if (ODBCVER >= 0x0300)
756      if (fInfoType == SQL_DM_VER)
757	sprintf ((char*)buf, "%02d.%02d.%04d.%04d",
758	  	SQL_SPEC_MAJOR, SQL_SPEC_MINOR, IODBC_BUILD / 10000, IODBC_BUILD % 10000);
759      else
760#endif
761	sprintf ((char*)buf, "%02d.%02d.0000", SQL_SPEC_MAJOR, SQL_SPEC_MINOR);
762      if(waMode == 'W')
763        {
764          SQLWCHAR *prov = dm_SQL_U8toW((SQLCHAR *)buf, SQL_NTS);
765          if(prov)
766            {
767              WCSNCPY(buf, prov, sizeof(buf)/sizeof(wchar_t));
768              free(prov);
769            }
770          else
771            buf[0] = L'\0';
772        }
773
774
775      if (rgbInfoValue != NULL  && cbInfoValueMax > 0)
776	{
777	  len = (waMode != 'W' ? STRLEN (buf) : WCSLEN(buf));
778
779	  if (len > cbInfoValueMax - 1)
780	    {
781	      len = cbInfoValueMax - 1;
782	      PUSHSQLERR (pdbc->herr, en_01004);
783
784	      retcode = SQL_SUCCESS_WITH_INFO;
785	    }
786
787	  if (waMode != 'W')
788	    {
789	      STRNCPY (rgbInfoValue, buf, len);
790	      ((char *) rgbInfoValue)[len] = '\0';
791	    }
792	  else
793	    {
794	      WCSNCPY (rgbInfoValue, buf, len);
795	      ((wchar_t *) rgbInfoValue)[len] = L'\0';
796	    }
797	}
798
799      if (pcbInfoValue != NULL)
800	{
801	  *pcbInfoValue = (SWORD) len;
802	}
803
804      return retcode;
805    }
806
807  if (pdbc->state == en_dbc_allocated || pdbc->state == en_dbc_needdata)
808    {
809      PUSHSQLERR (pdbc->herr, en_08003);
810
811      return SQL_ERROR;
812    }
813
814  switch (fInfoType)
815    {
816    case SQL_DRIVER_HDBC:
817      dword = (DWORD) (pdbc->dhdbc);
818      size = sizeof (dword);
819      break;
820
821    case SQL_DRIVER_HENV:
822      penv = (ENV_t *) (pdbc->henv);
823      dword = (DWORD) (penv->dhenv);
824      size = sizeof (dword);
825      break;
826
827    case SQL_DRIVER_HLIB:
828      penv = (ENV_t *) (pdbc->henv);
829      dword = (DWORD) (penv->hdll);
830      size = sizeof (dword);
831      break;
832
833    case SQL_DRIVER_HSTMT:
834      if (rgbInfoValue != NULL)
835	{
836	  pstmt = *((STMT_t **) rgbInfoValue);
837	}
838
839      for (tpstmt = (STMT_t *) (pdbc->hstmt);
840	  tpstmt != NULL;
841	  tpstmt = tpstmt->next)
842	{
843	  if (tpstmt == pstmt)
844	    {
845	      break;
846	    }
847	}
848
849      if (tpstmt == NULL)
850	{
851	  PUSHSQLERR (pdbc->herr, en_S1009);
852
853	  return SQL_ERROR;
854	}
855
856      dword = (DWORD) (pstmt->dhstmt);
857      size = sizeof (dword);
858      break;
859
860    case SQL_DRIVER_NAME:
861    case SQL_DRIVER_ODBC_VER:
862    case SQL_DRIVER_VER:
863    case SQL_ODBC_INTERFACE_CONFORMANCE:
864      break;
865
866    default:
867      /* NOTE : this was before the switch, just move here to let some informations going through */
868      if (pdbc->state == en_dbc_allocated || pdbc->state == en_dbc_needdata)
869	{
870	  PUSHSQLERR (pdbc->herr, en_08003);
871	  return SQL_ERROR;
872	}
873    }
874
875  if (size)
876    {
877      if (rgbInfoValue != NULL)
878	{
879	  *((DWORD *) rgbInfoValue) = dword;
880	}
881
882      if (pcbInfoValue != NULL)
883	{
884	  *(pcbInfoValue) = (SWORD) size;
885	}
886
887      return SQL_SUCCESS;
888    }
889
890#if (ODBCVER >= 0x0300)
891  /*
892   *  This was a temp value in ODBC 2
893   */
894  if (((ENV_t *) pdbc->henv)->dodbc_ver == SQL_OV_ODBC2 &&
895	  fInfoType == SQL_OJ_CAPABILITIES)
896      fInfoType = 65003;
897#endif /* ODBCVER >= 0x0300 */
898
899  if ((penv->unicode_driver && waMode != 'W')
900      || (!penv->unicode_driver && waMode == 'W'))
901    {
902      switch(fInfoType)
903        {
904        case SQL_ACCESSIBLE_PROCEDURES:
905        case SQL_ACCESSIBLE_TABLES:
906        case SQL_CATALOG_NAME:
907        case SQL_CATALOG_NAME_SEPARATOR:
908        case SQL_CATALOG_TERM:
909        case SQL_COLLATION_SEQ:
910        case SQL_COLUMN_ALIAS:
911        case SQL_DATA_SOURCE_NAME:
912        case SQL_DATA_SOURCE_READ_ONLY:
913        case SQL_DATABASE_NAME:
914        case SQL_DBMS_NAME:
915        case SQL_DBMS_VER:
916        case SQL_DESCRIBE_PARAMETER:
917        case SQL_DRIVER_NAME:
918        case SQL_DRIVER_ODBC_VER:
919        case SQL_DRIVER_VER:
920        case SQL_ODBC_VER:
921        case SQL_EXPRESSIONS_IN_ORDERBY:
922        case SQL_IDENTIFIER_QUOTE_CHAR:
923        case SQL_INTEGRITY:
924        case SQL_KEYWORDS:
925        case SQL_LIKE_ESCAPE_CLAUSE:
926        case SQL_MAX_ROW_SIZE_INCLUDES_LONG:
927        case SQL_MULT_RESULT_SETS:
928        case SQL_MULTIPLE_ACTIVE_TXN:
929        case SQL_NEED_LONG_DATA_LEN:
930        case SQL_ORDER_BY_COLUMNS_IN_SELECT:
931        case SQL_PROCEDURE_TERM:
932        case SQL_PROCEDURES:
933        case SQL_ROW_UPDATES:
934        case SQL_SCHEMA_TERM:
935        case SQL_SEARCH_PATTERN_ESCAPE:
936        case SQL_SERVER_NAME:
937        case SQL_SPECIAL_CHARACTERS:
938        case SQL_TABLE_TERM:
939        case SQL_USER_NAME:
940        case SQL_XOPEN_CLI_YEAR:
941        case SQL_OUTER_JOINS:
942          if (waMode != 'W')
943            {
944            /* ansi=>unicode*/
945              if ((_InfoValue = malloc(cbInfoValueMax * sizeof(wchar_t) + 1)) == NULL)
946	        {
947                  PUSHSQLERR (pdbc->herr, en_HY001);
948                  return SQL_ERROR;
949                }
950              cbInfoValueMax *=  sizeof(wchar_t);
951            }
952          else
953            {
954            /* unicode=>ansi*/
955              if ((_InfoValue = malloc(cbInfoValueMax + 1)) == NULL)
956	        {
957                  PUSHSQLERR (pdbc->herr, en_HY001);
958                  return SQL_ERROR;
959                }
960              cbInfoValueMax /=  sizeof(wchar_t);
961            }
962          infoValueOut = _InfoValue;
963          break;
964        }
965    }
966
967  CALL_UDRIVER(hdbc, pdbc, retcode, hproc, penv->unicode_driver,
968    en_GetInfo, (pdbc->dhdbc, fInfoType, infoValueOut, cbInfoValueMax,
969    pcbInfoValue));
970
971  if (hproc == SQL_NULL_HPROC)
972    {
973      PUSHSQLERR (pdbc->herr, en_IM001);
974      return SQL_ERROR;
975    }
976
977  if (retcode == SQL_ERROR  && fInfoType == SQL_DRIVER_ODBC_VER)
978    {
979      if (waMode != 'W')
980        {
981          STRCPY (buf, "01.00");
982
983          if (rgbInfoValue != NULL && cbInfoValueMax > 0)
984	    {
985	      len = STRLEN (buf);
986
987	      if (len > cbInfoValueMax - 1)
988	        {
989	          len = cbInfoValueMax - 1;
990                  ret = -1;
991	        }
992              else
993                {
994                  ret = 0;
995                }
996
997	      STRNCPY (rgbInfoValue, buf, len);
998	      ((char *) rgbInfoValue)[len] = '\0';
999	    }
1000
1001          if (pcbInfoValue != NULL)
1002            *pcbInfoValue = (SWORD) len;
1003        }
1004      else
1005        {
1006          ret = dm_StrCopyOut2_A2W ((SQLCHAR *) "01.00",
1007		(SQLWCHAR *) rgbInfoValue,
1008    		cbInfoValueMax / sizeof(wchar_t), pcbInfoValue);
1009          if (pcbInfoValue)
1010            *pcbInfoValue = *pcbInfoValue * sizeof(wchar_t);
1011        }
1012
1013       if (ret == -1)
1014         {
1015           PUSHSQLERR (pdbc->herr, en_01004);
1016           retcode = SQL_SUCCESS_WITH_INFO;
1017         }
1018       else
1019         {
1020           retcode = SQL_SUCCESS;
1021         }
1022
1023    }
1024  else if (rgbInfoValue
1025          && SQL_SUCCEEDED (retcode)
1026          && ((penv->unicode_driver && waMode != 'W')
1027              || (!penv->unicode_driver && waMode == 'W')))
1028    {
1029      switch(fInfoType)
1030        {
1031        case SQL_ACCESSIBLE_PROCEDURES:
1032        case SQL_ACCESSIBLE_TABLES:
1033        case SQL_CATALOG_NAME:
1034        case SQL_CATALOG_NAME_SEPARATOR:
1035        case SQL_CATALOG_TERM:
1036        case SQL_COLLATION_SEQ:
1037        case SQL_COLUMN_ALIAS:
1038        case SQL_DATA_SOURCE_NAME:
1039        case SQL_DATA_SOURCE_READ_ONLY:
1040        case SQL_DATABASE_NAME:
1041        case SQL_DBMS_NAME:
1042        case SQL_DBMS_VER:
1043        case SQL_DESCRIBE_PARAMETER:
1044        case SQL_DRIVER_NAME:
1045        case SQL_DRIVER_ODBC_VER:
1046        case SQL_DRIVER_VER:
1047        case SQL_ODBC_VER:
1048        case SQL_EXPRESSIONS_IN_ORDERBY:
1049        case SQL_IDENTIFIER_QUOTE_CHAR:
1050        case SQL_INTEGRITY:
1051        case SQL_KEYWORDS:
1052        case SQL_LIKE_ESCAPE_CLAUSE:
1053        case SQL_MAX_ROW_SIZE_INCLUDES_LONG:
1054        case SQL_MULT_RESULT_SETS:
1055        case SQL_MULTIPLE_ACTIVE_TXN:
1056        case SQL_NEED_LONG_DATA_LEN:
1057        case SQL_ORDER_BY_COLUMNS_IN_SELECT:
1058        case SQL_PROCEDURE_TERM:
1059        case SQL_PROCEDURES:
1060        case SQL_ROW_UPDATES:
1061        case SQL_SCHEMA_TERM:
1062        case SQL_SEARCH_PATTERN_ESCAPE:
1063        case SQL_SERVER_NAME:
1064        case SQL_SPECIAL_CHARACTERS:
1065        case SQL_TABLE_TERM:
1066        case SQL_USER_NAME:
1067        case SQL_XOPEN_CLI_YEAR:
1068        case SQL_OUTER_JOINS:
1069          if (waMode != 'W')
1070            {
1071            /* ansi<=unicode*/
1072              ret = dm_StrCopyOut2_W2A ((SQLWCHAR *) infoValueOut,
1073		(SQLCHAR *) rgbInfoValue,
1074                cbInfoValueMax / sizeof(wchar_t), pcbInfoValue);
1075            }
1076          else
1077            {
1078            /* unicode<=ansi*/
1079              ret = dm_StrCopyOut2_A2W ((SQLCHAR *) infoValueOut,
1080		(SQLWCHAR *) rgbInfoValue,
1081		cbInfoValueMax, pcbInfoValue);
1082              if (pcbInfoValue)
1083                *pcbInfoValue = *pcbInfoValue * sizeof(wchar_t);
1084            }
1085
1086          if (ret == -1)
1087            {
1088              PUSHSQLERR (pdbc->herr, en_01004);
1089              retcode = SQL_SUCCESS_WITH_INFO;
1090            }
1091          break;
1092        }
1093    }
1094  MEM_FREE(_InfoValue);
1095
1096  return retcode;
1097}
1098
1099
1100SQLRETURN SQL_API
1101SQLGetInfo (SQLHDBC hdbc,
1102  SQLUSMALLINT		  fInfoType,
1103  SQLPOINTER		  rgbInfoValue,
1104  SQLSMALLINT		  cbInfoValueMax,
1105  SQLSMALLINT 		* pcbInfoValue)
1106{
1107  ENTER_HDBC (hdbc, 0,
1108    trace_SQLGetInfo (TRACE_ENTER,
1109    	hdbc,
1110	fInfoType,
1111	rgbInfoValue, cbInfoValueMax, pcbInfoValue));
1112
1113  retcode = SQLGetInfo_Internal(
1114  	hdbc,
1115	fInfoType,
1116	rgbInfoValue, cbInfoValueMax, pcbInfoValue,
1117	'A');
1118
1119  LEAVE_HDBC (hdbc, 0,
1120    trace_SQLGetInfo (TRACE_LEAVE,
1121    	hdbc,
1122	fInfoType,
1123	rgbInfoValue, cbInfoValueMax, pcbInfoValue));
1124}
1125
1126
1127#if ODBCVER >= 0x0300
1128SQLRETURN SQL_API
1129SQLGetInfoA (SQLHDBC hdbc,
1130  SQLUSMALLINT		  fInfoType,
1131  SQLPOINTER		  rgbInfoValue,
1132  SQLSMALLINT		  cbInfoValueMax,
1133  SQLSMALLINT 		* pcbInfoValue)
1134{
1135  ENTER_HDBC (hdbc, 0,
1136    trace_SQLGetInfo (TRACE_ENTER,
1137    	hdbc,
1138	fInfoType,
1139	rgbInfoValue, cbInfoValueMax, pcbInfoValue));
1140
1141  retcode = SQLGetInfo_Internal(
1142  	hdbc,
1143	fInfoType,
1144	rgbInfoValue, cbInfoValueMax, pcbInfoValue,
1145	'A');
1146
1147  LEAVE_HDBC (hdbc, 0,
1148    trace_SQLGetInfo (TRACE_LEAVE,
1149    	hdbc,
1150	fInfoType,
1151	rgbInfoValue, cbInfoValueMax, pcbInfoValue));
1152}
1153
1154
1155SQLRETURN SQL_API
1156SQLGetInfoW (
1157  SQLHDBC		  hdbc,
1158  SQLUSMALLINT		  fInfoType,
1159  SQLPOINTER		  rgbInfoValue,
1160  SQLSMALLINT		  cbInfoValueMax,
1161  SQLSMALLINT 		* pcbInfoValue)
1162{
1163  ENTER_HDBC (hdbc, 0,
1164    trace_SQLGetInfoW (TRACE_ENTER,
1165    	hdbc,
1166	fInfoType,
1167	rgbInfoValue, cbInfoValueMax, pcbInfoValue));
1168
1169  retcode = SQLGetInfo_Internal (
1170  	hdbc,
1171	fInfoType,
1172	rgbInfoValue, cbInfoValueMax, pcbInfoValue,
1173	'W');
1174
1175  LEAVE_HDBC (hdbc, 0,
1176    trace_SQLGetInfoW (TRACE_LEAVE,
1177    	hdbc,
1178	fInfoType,
1179	rgbInfoValue, cbInfoValueMax, pcbInfoValue));
1180}
1181#endif
1182
1183
1184static int FunctionNumbers[] =
1185{
1186    0
1187#define FUNCDEF(A,B,C)	,A
1188#include "henv.ci"
1189#undef FUNCDEF
1190};
1191
1192#if (ODBCVER >= 0x0300)
1193
1194#define SQL_ODBC3_SET_FUNC_ON(pfExists, uwAPI) \
1195	*( ((UWORD*) (pfExists)) + ((uwAPI) >> 4) ) |= (1 << ((uwAPI) & 0x000F))
1196
1197#define SQL_ODBC3_SET_FUNC_OFF(pfExists, uwAPI) \
1198	*( ((UWORD*) (pfExists)) + ((uwAPI) >> 4) ) &= !(1 << ((uwAPI) & 0x000F))
1199
1200#endif
1201
1202
1203static SQLRETURN
1204SQLGetFunctions_Internal (
1205  SQLHDBC		  hdbc,
1206  SQLUSMALLINT		  fFunc,
1207  SQLUSMALLINT		* pfExists)
1208{
1209  CONN (pdbc, hdbc);
1210  HPROC hproc;
1211  SQLRETURN retcode;
1212  int i;
1213  UWORD functions2[100];
1214#if (ODBCVER >= 0x0300)
1215  UWORD functions3[SQL_API_ODBC3_ALL_FUNCTIONS_SIZE];
1216#endif
1217
1218  if (pdbc->state == en_dbc_allocated
1219      || pdbc->state == en_dbc_needdata)
1220    {
1221      PUSHSQLERR (pdbc->herr, en_S1010);
1222
1223      return SQL_ERROR;
1224    }
1225
1226  if (pfExists == NULL)
1227    {
1228      return SQL_SUCCESS;
1229    }
1230
1231  /*
1232   *  These functions are supported by the iODBC driver manager
1233   */
1234  if (fFunc == SQL_API_SQLDATASOURCES
1235      || fFunc == SQL_API_SQLDRIVERS
1236#if (ODBCVER >= 0x0300)
1237      || fFunc == SQL_API_SQLGETENVATTR
1238      || fFunc == SQL_API_SQLSETENVATTR
1239#endif
1240      )
1241    {
1242      *pfExists = (UWORD) 1;
1243      return SQL_SUCCESS;
1244    }
1245
1246  /*
1247   *  Check if function number is within ODBC version context
1248   */
1249#if (ODBCVER < 0x0300)
1250  if (fFunc > SQL_EXT_API_LAST)
1251    {
1252      PUSHSQLERR (pdbc->herr, en_S1095);
1253
1254      return SQL_ERROR;
1255    }
1256#endif
1257
1258  /*
1259   *  In a ODBC 2.x driver context, the ODBC 3.x API calls are
1260   *  mapped by the driver manager.
1261   */
1262#if (ODBCVER >= 0x0300)
1263  if (((ENV_t *) pdbc->henv)->dodbc_ver == SQL_OV_ODBC2)
1264    {
1265      switch (fFunc)
1266	{
1267	case SQL_API_ALL_FUNCTIONS:
1268	case SQL_API_ODBC3_ALL_FUNCTIONS:
1269	  break;
1270
1271	  /* Mapped ODBC3 app -> ODBC2 driver functions */
1272	case SQL_API_SQLALLOCHANDLE:
1273	case SQL_API_SQLFREEHANDLE:
1274	case SQL_API_SQLSETCONNECTATTR:
1275	case SQL_API_SQLGETCONNECTATTR:
1276	case SQL_API_SQLGETSTMTATTR:
1277	case SQL_API_SQLSETSTMTATTR:
1278	case SQL_API_SQLCOLATTRIBUTE:
1279	case SQL_API_SQLENDTRAN:
1280	case SQL_API_SQLBULKOPERATIONS:
1281	case SQL_API_SQLFETCHSCROLL:
1282	case SQL_API_SQLGETDIAGREC:
1283	case SQL_API_SQLGETDIAGFIELD:
1284	  *pfExists = SQL_TRUE;
1285	  return SQL_SUCCESS;
1286
1287	case SQL_API_SQLBINDPARAM:
1288	  fFunc = SQL_API_SQLBINDPARAMETER;
1289	  break;
1290
1291	default:
1292	  if (fFunc > SQL_API_SQLBINDPARAMETER)
1293	    {
1294	      *pfExists = SQL_FALSE;
1295
1296	      return SQL_SUCCESS;
1297	    }
1298	  break;
1299	}
1300    }
1301#endif
1302
1303
1304  /*
1305   *  If the driver exports a SQLGetFunctions call, use it
1306   */
1307  hproc = _iodbcdm_getproc (pdbc, en_GetFunctions);
1308
1309  if (hproc != SQL_NULL_HPROC)
1310    {
1311      CALL_DRIVER (hdbc, pdbc, retcode, hproc, (pdbc->dhdbc, fFunc, pfExists));
1312
1313      return retcode;
1314    }
1315
1316  /*
1317   *  Map deprecated functions
1318   */
1319  if (fFunc == SQL_API_SQLSETPARAM)
1320    {
1321      fFunc = SQL_API_SQLBINDPARAMETER;
1322    }
1323
1324  /*
1325   *  Initialize intermediate result arrays
1326   */
1327  memset (functions2, '\0', sizeof (functions2));
1328#if (ODBCVER > 0x0300)
1329  memset (functions3, '\0', sizeof (functions3));
1330#endif
1331
1332  /*
1333   *  Build result array by scanning for all API calls
1334   */
1335  for (i = 1; i < __LAST_API_FUNCTION__; i++)
1336    {
1337      int j = FunctionNumbers[i];
1338
1339      hproc = _iodbcdm_getproc (pdbc, i);
1340
1341      if (hproc != SQL_NULL_HPROC)
1342	{
1343	  if (j < 100)
1344	    functions2[j] = 1;
1345#if (ODBCVER >= 0x0300)
1346	  functions3[j >> 4] |= (1 << (j & 0x000F));
1347#endif
1348	}
1349    }
1350
1351  /*
1352   *  Finally return the information
1353   */
1354  if (fFunc == SQL_API_ALL_FUNCTIONS)
1355    {
1356      memcpy (pfExists, &functions2, sizeof (functions2));
1357    }
1358#if (ODBCVER < 0x0300)
1359  else
1360    {
1361      *pfExists = functions2[fFunc];
1362    }
1363#else
1364  else if (fFunc == SQL_API_ODBC3_ALL_FUNCTIONS)
1365    {
1366      memcpy (pfExists, &functions3, sizeof (functions3));
1367    }
1368  else
1369    {
1370      *pfExists = SQL_FUNC_EXISTS (functions3, fFunc);
1371    }
1372#endif
1373
1374  return SQL_SUCCESS;
1375}
1376
1377
1378SQLRETURN SQL_API
1379SQLGetFunctions (
1380  SQLHDBC		  hdbc,
1381  SQLUSMALLINT		  fFunc,
1382  SQLUSMALLINT	 	* pfExists)
1383{
1384  ENTER_HDBC (hdbc, 0,
1385    trace_SQLGetFunctions (TRACE_ENTER,
1386    	hdbc,
1387	fFunc,
1388	pfExists));
1389
1390  retcode = SQLGetFunctions_Internal (
1391    	hdbc,
1392	fFunc,
1393	pfExists);
1394
1395  LEAVE_HDBC (hdbc, 0,
1396    trace_SQLGetFunctions (TRACE_LEAVE,
1397    	hdbc,
1398	fFunc,
1399	pfExists));
1400}
1401