1/****************************************************
2 * SQLGetPrivateProfileString
3 *
4 * Mostly used with odbc.ini files but can be used for odbcinst.ini
5 *
6 * IF pszFileName[0] == '/' THEN
7 * use pszFileName
8 * ELSE
9 * use _odbcinst_ConfigModeINI() to get the complete file name for the current mode.
10 *
11 **************************************************
12 * This code was created by Peter Harvey @ CodeByDesign.
13 * Released under LGPL 28.JAN.99
14 *
15 * Contributions from...
16 * -----------------------------------------------
17 * Peter Harvey - pharvey@codebydesign.com
18 **************************************************/
19#include <config.h>
20#include <time.h>
21#include <odbcinstext.h>
22
23#ifdef ENABLE_INI_CACHING
24
25#ifdef HAVE_LIBPTH
26
27#include <pth.h>
28
29static pth_mutex_t mutex_ini = PTH_MUTEX_INIT;
30static int pth_init_called = 0;
31
32static int mutex_entry( pth_mutex_t *mutex )
33{
34 if ( !pth_init_called )
35 {
36 pth_init();
37 pth_init_called = 1;
38 }
39 return pth_mutex_acquire( mutex, 0, NULL );
40}
41
42static int mutex_exit( pth_mutex_t *mutex )
43{
44 return pth_mutex_release( mutex );
45}
46
47#elif HAVE_LIBPTHREAD
48
49#include <pthread.h>
50
51static pthread_mutex_t mutex_ini = PTHREAD_MUTEX_INITIALIZER;
52
53static int mutex_entry( pthread_mutex_t *mutex )
54{
55 return pthread_mutex_lock( mutex );
56}
57
58static int mutex_exit( pthread_mutex_t *mutex )
59{
60 return pthread_mutex_unlock( mutex );
61}
62
63#elif HAVE_LIBTHREAD
64
65#include <thread.h>
66
67static mutex_t mutex_ini;
68
69static int mutex_entry( mutex_t *mutex )
70{
71 return mutex_lock( mutex );
72}
73
74static int mutex_exit( mutex_t *mutex )
75{
76 return mutex_unlock( mutex );
77}
78
79#else
80
81#define mutex_entry(x)
82#define mutex_exit(x)
83
84#endif
85
86static struct ini_cache *ini_cache_head = NULL;
87
88static int _check_ini_cache( int *ret,
89 LPCSTR pszSection,
90 LPCSTR pszEntry,
91 LPCSTR pszDefault,
92 LPSTR pRetBuffer,
93 int nRetBuffer,
94 LPCSTR pszFileName )
95{
96 struct ini_cache *ini_cache = ini_cache_head, *prev = NULL;
97 UWORD config_mode;
98 long tstamp = time( NULL );
99
100 if ( pszSection == NULL || pszEntry == NULL )
101 {
102 return 0;
103 }
104
105 config_mode = __get_config_mode();
106
107 /*
108 * look for expired entries, remove one each call
109 */
110
111 for ( prev = NULL, ini_cache = ini_cache_head; ini_cache; ini_cache = ini_cache -> next )
112 {
113 if ( ini_cache -> timestamp < tstamp )
114 {
115 if ( prev )
116 {
117 prev -> next = ini_cache -> next;
118 }
119 else
120 {
121 ini_cache_head = ini_cache -> next;
122 }
123
124 if ( ini_cache -> fname )
125 free( ini_cache -> fname );
126
127 if ( ini_cache -> section )
128 free( ini_cache -> section );
129
130 if ( ini_cache -> entry )
131 free( ini_cache -> entry );
132
133 if ( ini_cache -> value )
134 free( ini_cache -> value );
135
136 if ( ini_cache -> default_value )
137 free( ini_cache -> default_value );
138
139 free( ini_cache );
140
141 break;
142 }
143
144 prev = ini_cache;
145 }
146
147 for ( ini_cache = ini_cache_head; ini_cache; ini_cache = ini_cache -> next )
148 {
149 if ( !pszFileName && ini_cache -> fname )
150 continue;
151 if ( pszFileName && !ini_cache -> fname )
152 continue;
153 if ( pszFileName && ini_cache -> fname && strcmp( pszFileName, ini_cache -> fname ))
154 continue;
155
156 if ( ini_cache -> config_mode != config_mode )
157 continue;
158
159 if ( !pszSection && ini_cache -> section )
160 continue;
161 if ( pszSection && !ini_cache -> section )
162 continue;
163 if ( pszSection && ini_cache -> section && strcmp( pszSection, ini_cache -> section ))
164 continue;
165
166 if ( !pszEntry && ini_cache -> entry )
167 continue;
168 if ( pszEntry && !ini_cache -> entry )
169 continue;
170 if ( pszEntry && ini_cache -> entry && strcmp( pszEntry, ini_cache -> entry ))
171 continue;
172
173 if ( !pszDefault && ini_cache -> default_value )
174 continue;
175 if ( pszDefault && !ini_cache -> default_value )
176 continue;
177 if ( pszDefault && ini_cache -> default_value && strcmp( pszDefault, ini_cache -> default_value ))
178 continue;
179
180 if ( !pRetBuffer && ini_cache -> value )
181 continue;
182 if ( pRetBuffer && !ini_cache -> value )
183 continue;
184
185 if ( nRetBuffer < ini_cache -> buffer_size )
186 continue;
187
188 if ( pRetBuffer )
189 {
190 if ( ini_cache -> value )
191 if ( nRetBuffer < strlen( ini_cache -> value )) {
192 strncpy( pRetBuffer, ini_cache -> value, nRetBuffer );
193 pRetBuffer[ nRetBuffer - 1 ] = '\0';
194 }
195 else {
196 strcpy( pRetBuffer, ini_cache -> value );
197 }
198
199 *ret = ini_cache -> ret_value;
200 return 1;
201 }
202 }
203
204 return 0;
205}
206
207static void _save_ini_cache( int ret,
208 LPCSTR pszSection,
209 LPCSTR pszEntry,
210 LPCSTR pszDefault,
211 LPSTR pRetBuffer,
212 int nRetBuffer,
213 LPCSTR pszFileName )
214{
215 struct ini_cache *ini_cache;
216 UWORD config_mode;
217 long tstamp = time( NULL ) + 20; /* expiry every 20 seconds */
218
219 ini_cache = calloc( sizeof( struct ini_cache ), 1 );
220 if ( !ini_cache )
221 {
222 return;
223 }
224
225 if ( pszFileName )
226 ini_cache -> fname = strdup( pszFileName );
227
228 if ( pszSection )
229 ini_cache -> section = strdup( pszSection );
230
231 if ( pszEntry )
232 ini_cache -> entry = strdup( pszEntry );
233
234 if ( pRetBuffer && ret >= 0 )
235 ini_cache -> value = strdup( pRetBuffer );
236
237 if ( pszDefault )
238 ini_cache -> default_value = strdup( pszDefault );
239
240 ini_cache -> buffer_size = nRetBuffer;
241 ini_cache -> ret_value = ret;
242
243 config_mode = __get_config_mode();
244 ini_cache -> config_mode = config_mode;
245
246 ini_cache -> timestamp = tstamp;
247
248 ini_cache -> next = ini_cache_head;
249 ini_cache_head = ini_cache;
250}
251
252static void _clear_ini_cache( void )
253{
254 struct ini_cache *ini_cache = ini_cache_head, *prev = NULL;
255
256 while (( ini_cache = ini_cache_head ) != NULL )
257 {
258 ini_cache_head = ini_cache -> next;
259
260 if ( ini_cache -> fname )
261 free( ini_cache -> fname );
262
263 if ( ini_cache -> section )
264 free( ini_cache -> section );
265
266 if ( ini_cache -> entry )
267 free( ini_cache -> entry );
268
269 if ( ini_cache -> value )
270 free( ini_cache -> value );
271
272 if ( ini_cache -> default_value )
273 free( ini_cache -> default_value );
274
275 free( ini_cache );
276 }
277}
278
279/*
280 * wrappers to provide thread safety
281 */
282
283static int check_ini_cache( int *ret,
284 LPCSTR pszSection,
285 LPCSTR pszEntry,
286 LPCSTR pszDefault,
287 LPSTR pRetBuffer,
288 int nRetBuffer,
289 LPCSTR pszFileName )
290{
291 int rval;
292
293 mutex_entry( &mutex_ini );
294
295 rval = _check_ini_cache( ret, pszSection, pszEntry, pszDefault,
296 pRetBuffer, nRetBuffer, pszFileName );
297
298 mutex_exit( &mutex_ini );
299
300 return rval;
301}
302
303static void save_ini_cache( int ret,
304 LPCSTR pszSection,
305 LPCSTR pszEntry,
306 LPCSTR pszDefault,
307 LPSTR pRetBuffer,
308 int nRetBuffer,
309 LPCSTR pszFileName )
310{
311 int cval;
312
313 mutex_entry( &mutex_ini );
314
315 /*
316 * check its not been inserted since the last check
317 */
318
319 if ( !_check_ini_cache( &cval, pszSection, pszEntry, pszDefault,
320 pRetBuffer, nRetBuffer, pszFileName )) {
321
322 _save_ini_cache( ret, pszSection, pszEntry, pszDefault,
323 pRetBuffer, nRetBuffer, pszFileName );
324 }
325
326 mutex_exit( &mutex_ini );
327}
328
329void __clear_ini_cache( void )
330{
331 mutex_entry( &mutex_ini );
332
333 _clear_ini_cache();
334
335 mutex_exit( &mutex_ini );
336}
337
338#else
339
340static int check_ini_cache( int *ret,
341 LPCSTR pszSection,
342 LPCSTR pszEntry,
343 LPCSTR pszDefault,
344 LPSTR pRetBuffer,
345 int nRetBuffer,
346 LPCSTR pszFileName )
347{
348 return 0;
349}
350
351static void save_ini_cache( int ret,
352 LPCSTR pszSection,
353 LPCSTR pszEntry,
354 LPCSTR pszDefault,
355 LPSTR pRetBuffer,
356 int nRetBuffer,
357 LPCSTR pszFileName )
358{
359}
360
361void __clear_ini_cache( void )
362{
363}
364
365#endif
366
367int SQLGetPrivateProfileString( LPCSTR pszSection,
368 LPCSTR pszEntry,
369 LPCSTR pszDefault,
370 LPSTR pRetBuffer,
371 int nRetBuffer,
372 LPCSTR pszFileName
373 )
374{
375 HINI hIni;
376 int nBufPos = 0;
377 char szValue[INI_MAX_PROPERTY_VALUE+1];
378 char szFileName[ODBC_FILENAME_MAX+1];
379 UWORD nConfigMode;
380 int ini_done = 0;
381 int ret;
382
383 inst_logClear();
384
385 if ( check_ini_cache( &ret, pszSection, pszEntry, pszDefault, pRetBuffer, nRetBuffer, pszFileName ))
386 {
387 return ret;
388 }
389
390 /* SANITY CHECKS */
391 if ( pRetBuffer == NULL || nRetBuffer < 2 )
392 {
393 inst_logPushMsg( __FILE__, __FILE__, __LINE__, LOG_CRITICAL, ODBC_ERROR_GENERAL_ERR, "" );
394 return -1;
395 }
396 if ( pszSection != NULL && pszEntry != NULL && pszDefault == NULL )
397 {
398 inst_logPushMsg( __FILE__, __FILE__, __LINE__, LOG_CRITICAL, ODBC_ERROR_GENERAL_ERR, "need default value - try empty string" );
399 return -1;
400 }
401
402 *pRetBuffer = '\0';
403
404 /*****************************************************
405 * SOME MS CODE (ie some drivers) MAY USE THIS FUNCTION TO GET ODBCINST INFO SO...
406 *****************************************************/
407 if ( pszFileName != NULL )
408 {
409 if ( strstr( pszFileName, "odbcinst" ) || strstr( pszFileName, "ODBCINST" ) )
410 {
411 ret = _SQLGetInstalledDrivers( pszSection, pszEntry, pszDefault, pRetBuffer, nRetBuffer );
412
413 if ( ret == -1 )
414 {
415 /* try to use any default provided */
416 if ( pRetBuffer && nRetBuffer > 0 )
417 {
418 if ( pszDefault )
419 {
420 strncpy( pRetBuffer, pszDefault, nRetBuffer );
421 pRetBuffer[ nRetBuffer - 1 ] = '\0';
422 }
423 }
424 }
425 else
426 {
427 save_ini_cache( ret, pszSection, pszEntry, pszDefault, pRetBuffer, nRetBuffer, pszFileName );
428 }
429
430 return ret;
431 }
432 }
433
434 /*****************************************************
435 * GATHER ALL RELEVANT DSN INFORMATION INTO AN hIni
436 *****************************************************/
437 if ( pszFileName != 0 && pszFileName[0] == '/' )
438 {
439#ifdef __OS2__
440 if ( iniOpen( &hIni, (char*)pszFileName, "#;", '[', ']', '=', TRUE, 1L )
441 != INI_SUCCESS )
442#else
443 if ( iniOpen( &hIni, (char*)pszFileName, "#;", '[', ']', '=', TRUE )
444 != INI_SUCCESS )
445#endif
446 {
447 inst_logPushMsg( __FILE__, __FILE__, __LINE__, LOG_CRITICAL,
448 ODBC_ERROR_COMPONENT_NOT_FOUND, "" );
449 return -1;
450 }
451 }
452 else
453 {
454 nConfigMode = __get_config_mode();
455 nBufPos = 0;
456 szFileName[0] = '\0';
457
458 switch ( nConfigMode )
459 {
460 case ODBC_BOTH_DSN:
461 if ( _odbcinst_UserINI( szFileName, TRUE ))
462 {
463#ifdef __OS2__
464 if ( iniOpen( &hIni, (char*) szFileName, "#;", '[', ']', '=', TRUE, 1L )
465 == INI_SUCCESS )
466#else
467 if ( iniOpen( &hIni, (char*) szFileName, "#;", '[', ']', '=', TRUE )
468 == INI_SUCCESS )
469#endif
470 {
471 ini_done = 1;
472 }
473 }
474 _odbcinst_SystemINI( szFileName, TRUE );
475 if ( !ini_done )
476 {
477#ifdef __OS2__
478 if ( iniOpen( &hIni, szFileName, "#;", '[', ']', '=', TRUE, 1L )
479 != INI_SUCCESS )
480#else
481 if ( iniOpen( &hIni, szFileName, "#;", '[', ']', '=', TRUE )
482 != INI_SUCCESS )
483#endif
484 {
485 inst_logPushMsg( __FILE__, __FILE__, __LINE__,
486 LOG_CRITICAL, ODBC_ERROR_COMPONENT_NOT_FOUND, "" );
487 return -1;
488 }
489 }
490 else
491 {
492 iniAppend( hIni, szFileName );
493 }
494 break;
495
496 case ODBC_USER_DSN:
497 _odbcinst_UserINI( szFileName, TRUE );
498#ifdef __OS2__
499 if ( iniOpen( &hIni, szFileName, "#;", '[', ']', '=', TRUE, 1L )
500 != INI_SUCCESS )
501#else
502 if ( iniOpen( &hIni, szFileName, "#;", '[', ']', '=', TRUE )
503 != INI_SUCCESS )
504#endif
505 {
506 inst_logPushMsg( __FILE__, __FILE__, __LINE__, LOG_CRITICAL,
507 ODBC_ERROR_COMPONENT_NOT_FOUND, "" );
508 return -1;
509 }
510 break;
511
512 case ODBC_SYSTEM_DSN:
513 _odbcinst_SystemINI( szFileName, TRUE );
514#ifdef __OS2__
515 if ( iniOpen( &hIni, szFileName, "#;", '[', ']', '=', TRUE, 1L )
516 != INI_SUCCESS )
517#else
518 if ( iniOpen( &hIni, szFileName, "#;", '[', ']', '=', TRUE )
519 != INI_SUCCESS )
520#endif
521 {
522 inst_logPushMsg( __FILE__, __FILE__, __LINE__, LOG_CRITICAL,
523 ODBC_ERROR_COMPONENT_NOT_FOUND, "" );
524 return -1;
525 }
526 break;
527
528 default:
529 inst_logPushMsg( __FILE__, __FILE__, __LINE__, LOG_CRITICAL,
530 ODBC_ERROR_GENERAL_ERR, "Invalid Config Mode" );
531 return -1;
532 }
533 }
534
535 /*****************************************************
536 * EXTRACT SECTIONS
537 *****************************************************/
538 if ( pszSection == NULL )
539 {
540 _odbcinst_GetSections( hIni, pRetBuffer, nRetBuffer, &nBufPos );
541 }
542 /*****************************************************
543 * EXTRACT ENTRIES
544 *****************************************************/
545 else if ( pszEntry == NULL )
546 {
547 _odbcinst_GetEntries( hIni, pszSection, pRetBuffer, nRetBuffer, &nBufPos );
548 }
549 /*****************************************************
550 * EXTRACT AN ENTRY
551 *****************************************************/
552 else
553 {
554 if ( pszSection == NULL || pszEntry == NULL || pszDefault == NULL )
555 {
556 inst_logPushMsg( __FILE__, __FILE__, __LINE__, LOG_CRITICAL, ODBC_ERROR_GENERAL_ERR, "" );
557 return -1;
558 }
559
560 /* TRY TO GET THE ONE ITEM MATCHING Section & Entry */
561 if ( iniPropertySeek( hIni, (char *)pszSection, (char *)pszEntry, "" ) != INI_SUCCESS )
562 {
563 /*
564 * (NG) this seems to be ignoring the length of pRetBuffer !!!
565 */
566 /* strncpy( pRetBuffer, pszDefault, INI_MAX_PROPERTY_VALUE ); */
567 if ( pRetBuffer && nRetBuffer > 0 && pszDefault )
568 {
569 strncpy( pRetBuffer, pszDefault, nRetBuffer );
570 pRetBuffer[ nRetBuffer - 1 ] = '\0';
571 }
572 }
573 else
574 {
575 iniValue( hIni, szValue );
576 if ( pRetBuffer )
577 {
578 strncpy( pRetBuffer, szValue, nRetBuffer );
579 pRetBuffer[ nRetBuffer - 1 ] = '\0';
580 }
581 nBufPos = strlen( szValue );
582 }
583 }
584
585 iniClose( hIni );
586
587 ret = strlen( pRetBuffer );
588
589 save_ini_cache( ret, pszSection, pszEntry, pszDefault, pRetBuffer, nRetBuffer, pszFileName );
590
591 return ret;
592}
593
594int INSTAPI SQLGetPrivateProfileStringW( LPCWSTR lpszSection,
595 LPCWSTR lpszEntry,
596 LPCWSTR lpszDefault,
597 LPWSTR lpszRetBuffer,
598 int cbRetBuffer,
599 LPCWSTR lpszFilename)
600{
601 int ret;
602 char *sect;
603 char *entry;
604 char *def;
605 char *buf;
606 char *name;
607
608 inst_logClear();
609
610 sect = lpszSection ? _single_string_alloc_and_copy( lpszSection ) : (char*)NULL;
611 entry = lpszEntry ? _single_string_alloc_and_copy( lpszEntry ) : (char*)NULL;
612 def = lpszDefault ? _single_string_alloc_and_copy( lpszDefault ) : (char*)NULL;
613 name = lpszFilename ? _single_string_alloc_and_copy( lpszFilename ) : (char*)NULL;
614
615 if ( lpszRetBuffer )
616 {
617 if ( cbRetBuffer > 0 )
618 {
619 buf = calloc( cbRetBuffer + 1, 1 );
620 }
621 else
622 {
623 buf = NULL;
624 }
625 }
626 else
627 {
628 buf = NULL;
629 }
630
631 ret = SQLGetPrivateProfileString( sect, entry, def, buf, cbRetBuffer, name );
632
633 if ( sect )
634 free( sect );
635 if ( entry )
636 free( entry );
637 if ( def )
638 free( def );
639 if ( name )
640 free( name );
641
642 if ( ret > 0 )
643 {
644 if ( buf && lpszRetBuffer )
645 {
646 _single_copy_to_wide( lpszRetBuffer, buf, ret + 1 );
647 }
648 }
649
650 if ( buf )
651 {
652 free( buf );
653 }
654
655 return ret;
656}
657