1/*
2 *  inifile.c
3 *
4 *  $Id: inifile.c,v 1.8 2006/12/15 14:04:16 source Exp $
5 *
6 *  Configuration File Management
7 *
8 *  The iODBC driver manager.
9 *
10 *  Copyright (C) 1996-2006 by OpenLink Software <iodbc@openlinksw.com>
11 *  All Rights Reserved.
12 *
13 *  This software is released under the terms of either of the following
14 *  licenses:
15 *
16 *      - GNU Library General Public License (see LICENSE.LGPL)
17 *      - The BSD License (see LICENSE.BSD).
18 *
19 *  Note that the only valid version of the LGPL license as far as this
20 *  project is concerned is the original GNU Library General Public License
21 *  Version 2, dated June 1991.
22 *
23 *  While not mandated by the BSD license, any patches you make to the
24 *  iODBC source code may be contributed back into the iODBC project
25 *  at your discretion. Contributions will benefit the Open Source and
26 *  Data Access community as a whole. Submissions may be made at:
27 *
28 *      http://www.iodbc.org
29 *
30 *
31 *  GNU Library Generic Public License Version 2
32 *  ============================================
33 *  This library is free software; you can redistribute it and/or
34 *  modify it under the terms of the GNU Library General Public
35 *  License as published by the Free Software Foundation; only
36 *  Version 2 of the License dated June 1991.
37 *
38 *  This library is distributed in the hope that it will be useful,
39 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
40 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41 *  Library General Public License for more details.
42 *
43 *  You should have received a copy of the GNU Library General Public
44 *  License along with this library; if not, write to the Free
45 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
46 *
47 *
48 *  The BSD License
49 *  ===============
50 *  Redistribution and use in source and binary forms, with or without
51 *  modification, are permitted provided that the following conditions
52 *  are met:
53 *
54 *  1. Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 *  2. Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in
58 *     the documentation and/or other materials provided with the
59 *     distribution.
60 *  3. Neither the name of OpenLink Software Inc. nor the names of its
61 *     contributors may be used to endorse or promote products derived
62 *     from this software without specific prior written permission.
63 *
64 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
65 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
66 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
67 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OPENLINK OR
68 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
69 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
70 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
71 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
72 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
73 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
74 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75 */
76
77
78#include <iodbc.h>
79#include <odbcinst.h>
80
81#include <stdarg.h>
82#include <stdio.h>
83#include <string.h>
84#ifndef _MAC
85#include <sys/types.h>
86#include <sys/stat.h>
87#endif
88#include <unistd.h>
89#include <ctype.h>
90
91#include "inifile.h"
92#include "misc.h"
93
94
95extern BOOL ValidDSN (LPCSTR lpszDSN);
96
97static PCFGENTRY __iodbcdm_cfg_poolalloc (PCONFIG p, u_int count);
98static int __iodbcdm_cfg_parse (PCONFIG pconfig);
99
100/*** READ MODULE ****/
101
102#ifndef O_BINARY
103#define O_BINARY 0
104#endif
105
106#ifdef _MAC
107static int
108strcasecmp (const char *s1, const char *s2)
109{
110  int cmp;
111
112  while (*s1)
113    {
114      if ((cmp = toupper (*s1) - toupper (*s2)) != 0)
115	return cmp;
116      s1++;
117      s2++;
118    }
119  return (*s2) ? -1 : 0;
120}
121#endif
122
123/*
124 *  Initialize a configuration
125 */
126int
127_iodbcdm_cfg_init (PCONFIG *ppconf, const char *filename, int doCreate)
128{
129  PCONFIG pconfig;
130
131  *ppconf = NULL;
132
133  if (!filename)
134    return -1;
135
136  if ((pconfig = (PCONFIG) calloc (1, sizeof (TCONFIG))) == NULL)
137    return -1;
138
139  pconfig->fileName = strdup (filename);
140  if (pconfig->fileName == NULL)
141    {
142      _iodbcdm_cfg_done (pconfig);
143      return -1;
144    }
145
146  /* If the file does not exist, try to create it */
147  if (doCreate && access (pconfig->fileName, 0) == -1)
148    {
149      int fd;
150
151      fd = creat (filename, 0644);
152      if (fd)
153	close (fd);
154    }
155
156  if (_iodbcdm_cfg_refresh (pconfig) == -1)
157    {
158      _iodbcdm_cfg_done (pconfig);
159      return -1;
160    }
161  *ppconf = pconfig;
162
163  return 0;
164}
165
166
167/*
168 *  Free all data associated with a configuration
169 */
170int
171_iodbcdm_cfg_done (PCONFIG pconfig)
172{
173  if (pconfig)
174    {
175      _iodbcdm_cfg_freeimage (pconfig);
176      if (pconfig->fileName)
177	free (pconfig->fileName);
178      free (pconfig);
179    }
180
181  return 0;
182}
183
184
185/*
186 *  Free the content specific data of a configuration
187 */
188int
189_iodbcdm_cfg_freeimage (PCONFIG pconfig)
190{
191  char *saveName;
192  PCFGENTRY e;
193  u_int i;
194
195  if (pconfig->image)
196    free (pconfig->image);
197  if (pconfig->entries)
198    {
199      e = pconfig->entries;
200      for (i = 0; i < pconfig->numEntries; i++, e++)
201	{
202	  if (e->flags & CFE_MUST_FREE_SECTION)
203	    free (e->section);
204	  if (e->flags & CFE_MUST_FREE_ID)
205	    free (e->id);
206	  if (e->flags & CFE_MUST_FREE_VALUE)
207	    free (e->value);
208	  if (e->flags & CFE_MUST_FREE_COMMENT)
209	    free (e->comment);
210	}
211      free (pconfig->entries);
212    }
213
214  saveName = pconfig->fileName;
215  memset (pconfig, 0, sizeof (TCONFIG));
216  pconfig->fileName = saveName;
217
218  return 0;
219}
220
221
222/*
223 *  This procedure reads an copy of the file into memory
224 *  caching the content based on stat
225 */
226int
227_iodbcdm_cfg_refresh (PCONFIG pconfig)
228{
229  struct stat sb;
230  char *mem;
231  int fd;
232
233  if (pconfig == NULL || stat (pconfig->fileName, &sb) == -1)
234    return -1;
235
236  /*
237   *  If our image is dirty, ignore all local changes
238   *  and force a reread of the image, thus ignoring all mods
239   */
240  if (pconfig->dirty)
241    _iodbcdm_cfg_freeimage (pconfig);
242
243  /*
244   *  Check to see if our incore image is still valid
245   */
246  if (pconfig->image && sb.st_size == pconfig->size
247      && sb.st_mtime == pconfig->mtime)
248    return 0;
249
250  /*
251   *  Now read the full image
252   */
253  if ((fd = open (pconfig->fileName, O_RDONLY | O_BINARY)) == -1)
254    return -1;
255
256  mem = (char *) malloc (sb.st_size + 1);
257  if (mem == NULL || read (fd, mem, sb.st_size) != sb.st_size)
258    {
259      free (mem);
260      close (fd);
261      return -1;
262    }
263  mem[sb.st_size] = 0;
264
265  close (fd);
266
267  /*
268   *  Store the new copy
269   */
270  _iodbcdm_cfg_freeimage (pconfig);
271  pconfig->image = mem;
272  pconfig->size = sb.st_size;
273  pconfig->mtime = sb.st_mtime;
274
275  if (__iodbcdm_cfg_parse (pconfig) == -1)
276    {
277      _iodbcdm_cfg_freeimage (pconfig);
278      return -1;
279    }
280
281  return 1;
282}
283
284
285#define iseolchar(C) (strchr ("\n\r\x1a", C) != NULL)
286#define iswhite(C) (strchr ("\f\t ", C) != NULL)
287
288
289static char *
290__iodbcdm_cfg_skipwhite (char *s)
291{
292  while (*s && iswhite (*s))
293    s++;
294  return s;
295}
296
297
298static int
299__iodbcdm_cfg_getline (char **pCp, char **pLinePtr)
300{
301  char *start;
302  char *cp = *pCp;
303
304  while (*cp && iseolchar (*cp))
305    cp++;
306  start = cp;
307  if (pLinePtr)
308    *pLinePtr = cp;
309
310  while (*cp && !iseolchar (*cp))
311    cp++;
312  if (*cp)
313    {
314      *cp++ = 0;
315      *pCp = cp;
316
317      while (--cp >= start && iswhite (*cp));
318      cp[1] = 0;
319    }
320  else
321    *pCp = cp;
322
323  return *start ? 1 : 0;
324}
325
326
327static char *
328rtrim (char *str)
329{
330  char *endPtr;
331
332  if (str == NULL || *str == '\0')
333    return NULL;
334
335  for (endPtr = &str[strlen (str) - 1]; endPtr >= str && isspace (*endPtr);
336      endPtr--);
337  endPtr[1] = 0;
338  return endPtr >= str ? endPtr : NULL;
339}
340
341
342/*
343 *  Parse the in-memory copy of the configuration data
344 */
345static int
346__iodbcdm_cfg_parse (PCONFIG pconfig)
347{
348  int isContinue, inString;
349  char *imgPtr;
350  char *endPtr;
351  char *lp;
352  char *section;
353  char *id;
354  char *value;
355  char *comment;
356
357  if (_iodbcdm_cfg_valid (pconfig))
358    return 0;
359
360  endPtr = pconfig->image + pconfig->size;
361  for (imgPtr = pconfig->image; imgPtr < endPtr;)
362    {
363      if (!__iodbcdm_cfg_getline (&imgPtr, &lp))
364	continue;
365
366      section = id = value = comment = NULL;
367
368      /*
369         *  Skip leading spaces
370       */
371      if (iswhite (*lp))
372	{
373	  lp = __iodbcdm_cfg_skipwhite (lp);
374	  isContinue = 1;
375	}
376      else
377	isContinue = 0;
378
379      /*
380       *  Parse Section
381       */
382      if (*lp == '[')
383	{
384	  section = __iodbcdm_cfg_skipwhite (lp + 1);
385	  if ((lp = strchr (section, ']')) == NULL)
386	    continue;
387	  *lp++ = 0;
388	  if (rtrim (section) == NULL)
389	    {
390	      section = NULL;
391	      continue;
392	    }
393	  lp = __iodbcdm_cfg_skipwhite (lp);
394	}
395      else if (*lp != ';' && *lp != '#')
396	{
397	  /* Try to parse
398	   *   1. Key = Value
399	   *   2. Value (iff isContinue)
400	   */
401	  if (!isContinue)
402	    {
403	      /* Parse `<Key> = ..' */
404	      id = lp;
405	      if ((lp = strchr (id, '=')) == NULL)
406		continue;
407	      *lp++ = 0;
408	      rtrim (id);
409	      lp = __iodbcdm_cfg_skipwhite (lp);
410	    }
411
412	  /* Parse value */
413	  inString = 0;
414	  value = lp;
415	  while (*lp)
416	    {
417	      if (inString)
418		{
419		  if (*lp == inString)
420		    inString = 0;
421		}
422	      else if (*lp == '"' || *lp == '\'')
423		inString = *lp;
424	      else if ((*lp == ';' || *lp == '#') && iswhite (lp[-1]))
425		{
426		  *lp = 0;
427		  comment = lp + 1;
428		  rtrim (value);
429		  break;
430		}
431	      lp++;
432	    }
433	}
434
435      /*
436       *  Parse Comment
437       */
438      if (*lp == ';' || *lp == '#')
439	comment = lp + 1;
440
441      if (_iodbcdm_cfg_storeentry (pconfig, section, id, value, comment,
442	      0) == -1)
443	{
444	  pconfig->dirty = 1;
445	  return -1;
446	}
447    }
448
449  pconfig->flags |= CFG_VALID;
450
451  return 0;
452}
453
454
455int
456_iodbcdm_cfg_storeentry (
457    PCONFIG pconfig,
458    char *section,
459    char *id,
460    char *value,
461    char *comment,
462    int dynamic)
463{
464  PCFGENTRY data;
465
466  if ((data = __iodbcdm_cfg_poolalloc (pconfig, 1)) == NULL)
467    return -1;
468
469  data->flags = 0;
470  if (dynamic)
471    {
472      if (section)
473	section = strdup (section);
474      if (id)
475	id = strdup (id);
476      if (value)
477	value = strdup (value);
478      if (comment)
479	comment = strdup (value);
480
481      if (section)
482	data->flags |= CFE_MUST_FREE_SECTION;
483      if (id)
484	data->flags |= CFE_MUST_FREE_ID;
485      if (value)
486	data->flags |= CFE_MUST_FREE_VALUE;
487      if (comment)
488	data->flags |= CFE_MUST_FREE_COMMENT;
489    }
490
491  data->section = section;
492  data->id = id;
493  data->value = value;
494  data->comment = comment;
495
496  return 0;
497}
498
499
500static PCFGENTRY
501__iodbcdm_cfg_poolalloc (PCONFIG p, u_int count)
502{
503  PCFGENTRY newBase;
504  u_int newMax;
505
506  if (p->numEntries + count > p->maxEntries)
507    {
508      newMax =
509	  p->maxEntries ? count + p->maxEntries + p->maxEntries / 2 : count +
510	  4096 / sizeof (TCFGENTRY);
511      newBase = (PCFGENTRY) malloc (newMax * sizeof (TCFGENTRY));
512      if (newBase == NULL)
513	return NULL;
514      if (p->entries)
515	{
516	  memcpy (newBase, p->entries, p->numEntries * sizeof (TCFGENTRY));
517	  free (p->entries);
518	}
519      p->entries = newBase;
520      p->maxEntries = newMax;
521    }
522
523  newBase = &p->entries[p->numEntries];
524  p->numEntries += count;
525
526  return newBase;
527}
528
529
530/*** COMPATIBILITY LAYER ***/
531
532
533int
534_iodbcdm_cfg_rewind (PCONFIG pconfig)
535{
536  if (!_iodbcdm_cfg_valid (pconfig))
537    return -1;
538
539  pconfig->flags = CFG_VALID;
540  pconfig->cursor = 0;
541
542  return 0;
543}
544
545
546/*
547 *  returns:
548 *	 0 success
549 *	-1 no next entry
550 *
551 *	section	id	value	flags		meaning
552 *	!0	0	!0	SECTION		[value]
553 *	!0	!0	!0	DEFINE		id = value|id="value"|id='value'
554 *	!0	0	!0	0		value
555 *	0	0	0	EOF		end of file encountered
556 */
557int
558_iodbcdm_cfg_nextentry (PCONFIG pconfig)
559{
560  PCFGENTRY e;
561
562  if (!_iodbcdm_cfg_valid (pconfig) || _iodbcdm_cfg_eof (pconfig))
563    return -1;
564
565  pconfig->flags &= ~(CFG_TYPEMASK);
566  pconfig->id = pconfig->value = NULL;
567
568  while (1)
569    {
570      if (pconfig->cursor >= pconfig->numEntries)
571	{
572	  pconfig->flags |= CFG_EOF;
573	  return -1;
574	}
575      e = &pconfig->entries[pconfig->cursor++];
576
577      if (e->section)
578	{
579	  pconfig->section = e->section;
580	  pconfig->flags |= CFG_SECTION;
581	  return 0;
582	}
583      if (e->value)
584	{
585	  pconfig->value = e->value;
586	  if (e->id)
587	    {
588	      pconfig->id = e->id;
589	      pconfig->flags |= CFG_DEFINE;
590	    }
591	  else
592	    pconfig->flags |= CFG_CONTINUE;
593	  return 0;
594	}
595    }
596}
597
598
599int
600_iodbcdm_cfg_find (PCONFIG pconfig, char *section, char *id)
601{
602  int atsection;
603
604  if (!_iodbcdm_cfg_valid (pconfig) || _iodbcdm_cfg_rewind (pconfig))
605    return -1;
606
607  atsection = 0;
608  while (_iodbcdm_cfg_nextentry (pconfig) == 0)
609    {
610      if (atsection)
611	{
612	  if (_iodbcdm_cfg_section (pconfig))
613	    return -1;
614	  else if (_iodbcdm_cfg_define (pconfig))
615	    {
616	      char *szId = _iodbcdm_remove_quotes (pconfig->id);
617	      int bSame;
618	      if (szId)
619		{
620		  bSame = !strcasecmp (szId, id);
621		  free (szId);
622		  if (bSame)
623		    return 0;
624		}
625	    }
626	}
627      else if (_iodbcdm_cfg_section (pconfig)
628	  && !strcasecmp (pconfig->section, section))
629	{
630	  if (id == NULL)
631	    return 0;
632	  atsection = 1;
633	}
634    }
635  return -1;
636}
637
638
639/*** WRITE MODULE ****/
640
641
642/*
643 *  Change the configuration
644 *
645 *  section id    value		action
646 *  --------------------------------------------------------------------------
647 *   value  value value		update '<entry>=<string>' in section <section>
648 *   value  value NULL		delete '<entry>' from section <section>
649 *   value  NULL  NULL		delete section <section>
650 */
651int
652_iodbcdm_cfg_write (
653    PCONFIG pconfig,
654    char *section,
655    char *id,
656    char *value)
657{
658  PCFGENTRY e, e2, eSect;
659  int idx;
660  int i;
661
662  if (!_iodbcdm_cfg_valid (pconfig) || section == NULL)
663    return -1;
664
665  /* find the section */
666  e = pconfig->entries;
667  i = pconfig->numEntries;
668  eSect = 0;
669  while (i--)
670    {
671      if (e->section && !strcasecmp (e->section, section))
672	{
673	  eSect = e;
674	  break;
675	}
676      e++;
677    }
678
679  /* did we find the section? */
680  if (!eSect)
681    {
682      /* check for delete operation on a nonexisting section */
683      if (!id || !value)
684	return 0;
685
686      /* add section first */
687      if (_iodbcdm_cfg_storeentry (pconfig, section, NULL, NULL, NULL,
688	      1) == -1
689	  || _iodbcdm_cfg_storeentry (pconfig, NULL, id, value, NULL,
690	      1) == -1)
691	return -1;
692
693      pconfig->dirty = 1;
694      return 0;
695    }
696
697  /* ok - we have found the section - let's see what we need to do */
698
699  if (id)
700    {
701      if (value)
702	{
703	  /* add / update a key */
704	  while (i--)
705	    {
706	      e++;
707	      /* break on next section */
708	      if (e->section)
709		{
710		  /* insert new entry before e */
711		  idx = e - pconfig->entries;
712		  if (__iodbcdm_cfg_poolalloc (pconfig, 1) == NULL)
713		    return -1;
714		  memmove (e + 1, e,
715		      (pconfig->numEntries - idx) * sizeof (TCFGENTRY));
716		  e->section = NULL;
717		  e->id = strdup (id);
718		  e->value = strdup (value);
719		  e->comment = NULL;
720		  if (e->id == NULL || e->value == NULL)
721		    return -1;
722		  e->flags |= CFE_MUST_FREE_ID | CFE_MUST_FREE_VALUE;
723		  pconfig->dirty = 1;
724		  return 0;
725		}
726
727	      if (e->id && !strcasecmp (e->id, id))
728		{
729		  /* found key - do update */
730		  if (e->value && (e->flags & CFE_MUST_FREE_VALUE))
731		    {
732		      e->flags &= ~CFE_MUST_FREE_VALUE;
733		      free (e->value);
734		    }
735		  pconfig->dirty = 1;
736		  if ((e->value = strdup (value)) == NULL)
737		    return -1;
738		  e->flags |= CFE_MUST_FREE_VALUE;
739		  return 0;
740		}
741	    }
742
743	  /* last section in file - add new entry */
744	  if (_iodbcdm_cfg_storeentry (pconfig, NULL, id, value, NULL,
745		  1) == -1)
746	    return -1;
747	  pconfig->dirty = 1;
748	  return 0;
749	}
750      else
751	{
752	  /* delete a key */
753	  while (i--)
754	    {
755	      e++;
756	      /* break on next section */
757	      if (e->section)
758		return 0;	/* not found */
759
760	      if (e->id && !strcasecmp (e->id, id))
761		{
762		  /* found key - do delete */
763		  eSect = e;
764		  e++;
765		  goto doDelete;
766		}
767	    }
768	  /* key not found - that' ok */
769	  return 0;
770	}
771    }
772  else
773    {
774      /* delete entire section */
775
776      /* find e : next section */
777      while (i--)
778	{
779	  e++;
780	  /* break on next section */
781	  if (e->section)
782	    break;
783	}
784      if (i < 0)
785	e++;
786
787      /* move up e while comment */
788      e2 = e - 1;
789      while (e2->comment && !e2->section && !e2->id && !e2->value
790	  && (iswhite (e2->comment[0]) || e2->comment[0] == ';'))
791	e2--;
792      e = e2 + 1;
793
794    doDelete:
795      /* move up eSect while comment */
796      e2 = eSect - 1;
797      while (e2->comment && !e2->section && !e2->id && !e2->value
798	  && (iswhite (e2->comment[0]) || e2->comment[0] == ';'))
799	e2--;
800      eSect = e2 + 1;
801
802      /* delete everything between eSect .. e */
803      for (e2 = eSect; e2 < e; e2++)
804	{
805	  if (e2->flags & CFE_MUST_FREE_SECTION)
806	    free (e2->section);
807	  if (e2->flags & CFE_MUST_FREE_ID)
808	    free (e2->id);
809	  if (e2->flags & CFE_MUST_FREE_VALUE)
810	    free (e2->value);
811	  if (e2->flags & CFE_MUST_FREE_COMMENT)
812	    free (e2->comment);
813	}
814      idx = e - pconfig->entries;
815      memmove (eSect, e, (pconfig->numEntries - idx) * sizeof (TCFGENTRY));
816      pconfig->numEntries -= e - eSect;
817      pconfig->dirty = 1;
818    }
819
820  return 0;
821}
822
823
824/*
825 *  Write a formatted copy of the configuration to a file
826 *
827 *  This assumes that the inifile has already been parsed
828 */
829static void
830__iodbcdm_cfg_outputformatted (PCONFIG pconfig, FILE *fd)
831{
832  PCFGENTRY e = pconfig->entries;
833  int i = pconfig->numEntries;
834  int m = 0;
835  int j, l;
836  int skip = 0;
837
838  while (i--)
839    {
840      if (e->section)
841	{
842	  /* Add extra line before section, unless comment block found */
843	  if (skip)
844	    fprintf (fd, "\n");
845	  fprintf (fd, "[%s]", e->section);
846	  if (e->comment)
847	    fprintf (fd, "\t;%s", e->comment);
848
849	  /* Calculate m, which is the length of the longest key */
850	  m = 0;
851	  for (j = 1; j <= i; j++)
852	    {
853	      if (e[j].section)
854		break;
855	      if (e[j].id && (l = strlen (e[j].id)) > m)
856		m = l;
857	    }
858
859	  /* Add an extra lf next time around */
860	  skip = 1;
861	}
862      /*
863       *  Key = value
864       */
865      else if (e->id && e->value)
866	{
867	  if (m)
868	    fprintf (fd, "%-*.*s = %s", m, m, e->id, e->value);
869	  else
870	    fprintf (fd, "%s = %s", e->id, e->value);
871	  if (e->comment)
872	    fprintf (fd, "\t;%s", e->comment);
873	}
874      /*
875       *  Value only (continuation)
876       */
877      else if (e->value)
878	{
879	  fprintf (fd, "  %s", e->value);
880	  if (e->comment)
881	    fprintf (fd, "\t;%s", e->comment);
882	}
883      /*
884       *  Comment only - check if we need an extra lf
885       *
886       *  1. Comment before section gets an extra blank line before
887       *     the comment starts.
888       *
889       *          previousEntry = value
890       *          <<< INSERT BLANK LINE HERE >>>
891       *          ; Comment Block
892       *          ; Sticks to section below
893       *          [new section]
894       *
895       *  2. Exception on 1. for commented out definitions:
896       *     (Immediate nonwhitespace after ;)
897       *          [some section]
898       *          v1 = 1
899       *          ;v2 = 2   << NO EXTRA LINE >>
900       *          v3 = 3
901       *
902       *  3. Exception on 2. for ;; which certainly is a section comment
903       *          [some section]
904       *          definitions
905       *          <<< INSERT BLANK LINE HERE >>>
906       *          ;; block comment
907       *          [new section]
908       */
909      else if (e->comment)
910	{
911	  if (skip && (iswhite (e->comment[0]) || e->comment[0] == ';'))
912	    {
913	      for (j = 1; j <= i; j++)
914		{
915		  if (e[j].section)
916		    {
917		      fprintf (fd, "\n");
918		      skip = 0;
919		      break;
920		    }
921		  if (e[j].id || e[j].value)
922		    break;
923		}
924	    }
925	  fprintf (fd, ";%s", e->comment);
926	}
927      fprintf (fd, "\n");
928      e++;
929    }
930}
931
932
933/*
934 *  Write the changed file back
935 */
936int
937_iodbcdm_cfg_commit (PCONFIG pconfig)
938{
939  FILE *fp;
940
941  if (!_iodbcdm_cfg_valid (pconfig))
942    return -1;
943
944  if (pconfig->dirty)
945    {
946      if ((fp = fopen (pconfig->fileName, "w")) == NULL)
947	return -1;
948
949      __iodbcdm_cfg_outputformatted (pconfig, fp);
950
951      fclose (fp);
952
953      pconfig->dirty = 0;
954    }
955
956  return 0;
957}
958
959
960int
961_iodbcdm_cfg_next_section(PCONFIG pconfig)
962{
963  do
964    if (0 != _iodbcdm_cfg_nextentry (pconfig))
965      return -1;
966  while (!_iodbcdm_cfg_section (pconfig));
967
968  return 0;
969}
970
971
972int
973_iodbcdm_cfg_search_init(PCONFIG *ppconf, const char *filename, int doCreate)
974{
975  char pathbuf[1024];
976
977  if (strstr (filename, "odbc.ini") || strstr (filename, "ODBC.INI"))
978    return _iodbcdm_cfg_init (ppconf, _iodbcadm_getinifile (pathbuf,
979	    sizeof (pathbuf), FALSE, doCreate), doCreate);
980  else if (strstr (filename, "odbcinst.ini")
981      || strstr (filename, "ODBCINST.INI"))
982    return _iodbcdm_cfg_init (ppconf, _iodbcadm_getinifile (pathbuf,
983	    sizeof (pathbuf), TRUE, doCreate), doCreate);
984  else if (doCreate || (!doCreate && access(filename, R_OK) == 0))
985    return _iodbcdm_cfg_init (ppconf, filename, doCreate);
986  else
987    return -1;
988}
989
990
991int
992_iodbcdm_list_sections (PCONFIG pCfg, LPSTR lpszRetBuffer, int cbRetBuffer)
993{
994  int curr = 0, sect_len = 0;
995  lpszRetBuffer[0] = 0;
996
997  if (0 == _iodbcdm_cfg_rewind (pCfg))
998    {
999      while (curr < cbRetBuffer && 0 == _iodbcdm_cfg_next_section (pCfg)
1000	  && pCfg->section)
1001	{
1002	  sect_len = strlen (pCfg->section) + 1;
1003	  sect_len =
1004	      sect_len > cbRetBuffer - curr ? cbRetBuffer - curr : sect_len;
1005
1006	  memmove (lpszRetBuffer + curr, pCfg->section, sect_len);
1007
1008	  curr += sect_len;
1009	}
1010      if (curr < cbRetBuffer)
1011	lpszRetBuffer[curr] = 0;
1012      return curr;
1013    }
1014  return 0;
1015}
1016
1017
1018int
1019_iodbcdm_list_entries (PCONFIG pCfg, LPCSTR lpszSection, LPSTR lpszRetBuffer, int cbRetBuffer)
1020{
1021  int curr = 0, sect_len = 0;
1022  lpszRetBuffer[0] = 0;
1023
1024  if (0 == _iodbcdm_cfg_rewind (pCfg))
1025    {
1026      while (curr < cbRetBuffer && 0 == _iodbcdm_cfg_nextentry (pCfg))
1027	{
1028	  if (_iodbcdm_cfg_define (pCfg)
1029	      && !strcmp (pCfg->section, lpszSection) && pCfg->id)
1030	    {
1031	      sect_len = strlen (pCfg->id) + 1;
1032	      sect_len =
1033		  sect_len >
1034		  cbRetBuffer - curr ? cbRetBuffer - curr : sect_len;
1035
1036	      memmove (lpszRetBuffer + curr, pCfg->id, sect_len);
1037
1038	      curr += sect_len;
1039	    }
1040	}
1041      if (curr < cbRetBuffer)
1042	lpszRetBuffer[curr] = 0;
1043      return curr;
1044    }
1045  return 0;
1046}
1047
1048
1049BOOL
1050do_create_dsns (PCONFIG pCfg, PCONFIG pInfCfg, LPSTR szDriver, LPSTR szDSNS, LPSTR szDiz)
1051{
1052  char *szValue = strdup (szDSNS), *szCurr = szValue, *szComma;
1053  int hasMore = FALSE;
1054  BOOL retcode = FALSE;
1055
1056  do
1057    {
1058      szComma = strchr (szCurr, ',');
1059      if (szComma)
1060	{
1061	  *szComma = 0;
1062	  hasMore = TRUE;
1063	}
1064      else
1065	hasMore = FALSE;
1066
1067#ifdef WIN32
1068      if (_iodbcdm_cfg_write (pCfg, "ODBC 32 bit Data Sources", szCurr,
1069	      szDiz))
1070#else
1071      if (_iodbcdm_cfg_write (pCfg, "ODBC Data Sources", szCurr, szDiz))
1072#endif
1073	goto error;
1074
1075      if (!ValidDSN (szCurr) || _iodbcdm_cfg_write (pCfg, szCurr, NULL, NULL))
1076	goto error;
1077
1078      if (_iodbcdm_cfg_find (pInfCfg, szCurr, NULL)
1079	  && !_iodbcdm_cfg_write (pCfg, szCurr, NULL, NULL))
1080	{
1081	  if (_iodbcdm_cfg_write (pCfg, szCurr, "Driver", szDriver))
1082	    goto error;
1083	  while (!_iodbcdm_cfg_nextentry (pInfCfg)
1084	      && _iodbcdm_cfg_define (pInfCfg))
1085	    {
1086	      if (_iodbcdm_cfg_write (pCfg, szCurr, pInfCfg->id,
1087		      pInfCfg->value))
1088		goto error;
1089	    }
1090	}
1091
1092      szCurr = szComma + 1;
1093    }
1094  while (hasMore);
1095
1096  retcode = TRUE;
1097
1098error:
1099  free (szValue);
1100  return retcode;
1101}
1102
1103
1104BOOL
1105install_from_ini (PCONFIG pCfg, PCONFIG pOdbcCfg, LPSTR szInfFile, LPSTR szDriver, BOOL drivers)
1106{
1107  PCONFIG pInfCfg;
1108  char *szKeysSection = NULL, *szDriverFile = NULL, *szSetupFile = NULL,
1109      *szValue = NULL, *szId = NULL, *szComma, *szComma1;
1110  BOOL ret = FALSE;
1111
1112  if (_iodbcdm_cfg_write (pCfg, szDriver, NULL, NULL))
1113    return ret;
1114
1115  if (_iodbcdm_cfg_init (&pInfCfg, szInfFile, FALSE))
1116    return ret;
1117
1118  if (_iodbcdm_cfg_find (pInfCfg,
1119	  drivers ? "ODBC Drivers" : "ODBC Translators", szDriver))
1120    goto error;
1121
1122#ifdef WIN32
1123  if (_iodbcdm_cfg_write (pCfg,
1124	  drivers ? "ODBC 32 bit Drivers" : "ODBC 32 bit Translators",
1125	  szDriver, "Installed"))
1126#else
1127  if (_iodbcdm_cfg_write (pCfg, drivers ? "ODBC Drivers" : "ODBC Translators",
1128	  szDriver, "Installed"))
1129#endif
1130    goto error;
1131
1132  if (_iodbcdm_cfg_find (pInfCfg, szDriver,
1133	  drivers ? "Driver" : "Translator"))
1134    goto error;
1135
1136  szComma = strchr (pInfCfg->value, ',');
1137  szComma1 = strchr (szComma + 1, ',');
1138  if (!szComma || !szComma1 || szComma + 1 == szComma1)
1139    goto error;
1140
1141  *szComma1 = 0;
1142  szDriverFile = strdup (szComma + 1);
1143  if (_iodbcdm_cfg_write (pCfg, szDriver, drivers ? "Driver" : "Translator",
1144	  szDriverFile))
1145    goto error;
1146
1147  if (!_iodbcdm_cfg_find (pInfCfg, szDriver, "Setup"))
1148    {
1149      szComma = strchr (pInfCfg->value, ',');
1150      szComma1 = strchr (szComma + 1, ',');
1151      if (!szComma || !szComma1 || szComma + 1 == szComma1)
1152	goto error;
1153
1154      *szComma1 = 0;
1155      szSetupFile = strdup (szComma + 1);
1156
1157      if (_iodbcdm_cfg_write (pCfg, szDriver, "Setup", szSetupFile))
1158	goto error;
1159    }
1160
1161  if (!_iodbcdm_cfg_find (pInfCfg, szDriver, NULL))
1162    {
1163      while (!_iodbcdm_cfg_nextentry (pInfCfg)
1164	  && _iodbcdm_cfg_define (pInfCfg))
1165	if (strcmp (pInfCfg->id, drivers ? "\"Driver\"" : "\"Translator\"")
1166	    && strcmp (pInfCfg->id, "\"Setup\""))
1167	  {
1168	    szComma = strchr (pInfCfg->value, ',');
1169	    szComma1 = strchr (szComma + 1, ',');
1170	    if (!szComma || !szComma1 || szComma + 1 == szComma1)
1171	      szValue = strdup ("");
1172	    else
1173	      {
1174		*szComma1 = 0;
1175		szValue = strdup (szComma + 1);
1176	      }
1177
1178	    szComma = strchr (pInfCfg->id, '"');
1179	    szComma1 = strchr (szComma + 1, '"');
1180	    if (!szComma || !szComma1 || szComma + 1 == szComma1)
1181	      goto loop_cont;
1182	    else
1183	      {
1184		*szComma1 = 0;
1185		szId = strdup (szComma + 1);
1186	      }
1187
1188	    if (_iodbcdm_cfg_write (pCfg, szDriver, szId, szValue))
1189	      goto error;
1190
1191	  loop_cont:
1192	    if (szValue)
1193	      {
1194		free (szValue);
1195		szValue = NULL;
1196	      }
1197	    if (szId)
1198	      {
1199		free (szId);
1200		szId = NULL;
1201	      }
1202	  }
1203    }
1204
1205  if (!drivers)
1206    goto quit;
1207
1208  szKeysSection = (char *) calloc (strlen (szDriver) + 6, sizeof (char));
1209  strcpy (szKeysSection, szDriver);
1210  strcat (szKeysSection, "-Keys");
1211
1212  if (!_iodbcdm_cfg_find (pInfCfg, szKeysSection, NULL))
1213    {
1214      while (!_iodbcdm_cfg_nextentry (pInfCfg)
1215	  && _iodbcdm_cfg_define (pInfCfg))
1216	{
1217	  if (strcmp (pInfCfg->id, "CreateDSN"))
1218	    {
1219	      if (_iodbcdm_cfg_write (pCfg, szDriver, pInfCfg->id,
1220		      pInfCfg->value))
1221		goto error;
1222	    }
1223	  else if (!do_create_dsns (pOdbcCfg, pCfg, szDriverFile,
1224		  pInfCfg->value, szDriver))
1225	    goto error;
1226	}
1227    }
1228
1229quit:
1230  ret = TRUE;
1231
1232error:
1233  if (szKeysSection)
1234    free (szKeysSection);
1235  if (szDriverFile)
1236    free (szDriverFile);
1237  if (szSetupFile)
1238    free (szSetupFile);
1239  if (szValue)
1240    free (szValue);
1241  if (szId)
1242    free (szId);
1243  _iodbcdm_cfg_done (pInfCfg);
1244  return ret;
1245}
1246
1247
1248int
1249install_from_string (PCONFIG pCfg, PCONFIG pOdbcCfg, LPSTR lpszDriver, BOOL drivers)
1250{
1251  char *szCurr = (char *) lpszDriver, *szDiz = lpszDriver;
1252  char *szAsignment, *szEqual, *szValue, *szDriver = NULL;
1253
1254  if (_iodbcdm_cfg_write (pCfg, lpszDriver, NULL, NULL))
1255    return FALSE;
1256
1257#ifdef WIN32
1258  if (_iodbcdm_cfg_write (pCfg,
1259	  drivers ? "ODBC 32 bit Drivers" : "ODBC 32 bit Translators",
1260	  lpszDriver, "Installed"))
1261#else
1262  if (_iodbcdm_cfg_write (pCfg, drivers ? "ODBC Drivers" : "ODBC Translators",
1263	  lpszDriver, "Installed"))
1264#endif
1265    return FALSE;
1266
1267  for (szCurr = lpszDriver + strlen (lpszDriver) + 1; *szCurr;
1268      szCurr += strlen (szCurr) + 1)
1269    {
1270      szAsignment = strdup (szCurr);
1271      szEqual = strchr (szAsignment, '=');
1272      szValue = szEqual + 1;
1273
1274      if (szEqual)
1275	*szEqual = 0;
1276      else
1277	goto loop_error;
1278
1279      if ((drivers && !strcmp (szAsignment, "Driver")) || (!drivers
1280	      && !strcmp (szAsignment, "Translator")))
1281	{
1282	  if (szDriver)
1283	    free (szDriver);
1284	  szDriver = strdup (szValue);
1285	}
1286
1287      if (drivers)
1288	{
1289	  if (strcmp (szAsignment, "CreateDSN"))
1290	    {
1291	      if (_iodbcdm_cfg_write (pCfg, lpszDriver, szAsignment, szValue))
1292		goto loop_error;
1293	    }
1294	  else if (!do_create_dsns (pOdbcCfg, pCfg, szDriver, szValue, szDiz))
1295	    goto loop_error;
1296	}
1297      else if (_iodbcdm_cfg_write (pCfg, lpszDriver, szAsignment, szValue))
1298	goto loop_error;
1299
1300      free (szAsignment);
1301      continue;
1302
1303    loop_error:
1304      if (szDriver)
1305	free (szDriver);
1306      free (szAsignment);
1307      return FALSE;
1308    }
1309
1310  if (szDriver)
1311    free (szDriver);
1312  else
1313    return FALSE;
1314
1315  return TRUE;
1316}
1317