1 /*
2  * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
3  *
4  * Copyright (C) 2001-2009, William Chia-Wei Cheng.
5  *
6  * This file may be distributed under the terms of the Q Public License
7  * as defined by Trolltech AS of Norway and appearing in the file
8  * LICENSE.QPL included in the packaging of this file.
9  *
10  * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
11  * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
12  * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
13  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
16  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/ini.c,v 1.7 2011/05/16 16:21:58 william Exp $
19  */
20 
21 #define _INCLUDE_FROM_INI_C_
22 
23 #include "tgifdefs.h"
24 
25 #include "dialog.e"
26 #include "ini.e"
27 #include "msg.e"
28 #include "strtbl.e"
29 #include "util.e"
30 
31 typedef struct TgIniEntryInfoStruct {
32    char *pszEntry;
33    char *pszValue;
34    int bScanned;
35    struct TgIniEntryInfoStruct *pNextEntry, *pPrevEntry;
36 } TGINIENTRYINFO;
37 
38 typedef struct TgIniSectionInfoStruct {
39    char *pszSection;
40    int bAllowDupKey;
41    int bValuelessKey;
42    TGINIENTRYINFO *pFirstEntry, *pLastEntry;
43    struct TgIniSectionInfoStruct *pNextSection, *pPrevSection;
44 } TGINISECTIONINFO;
45 
46 typedef struct TgIniFileInfoStruct {
47    char *pszFile;
48    int bModified;
49    int bStripQuotes;
50    int bCheckFileTime;
51    time_t stFileTime;
52    TGINISECTIONINFO *pFirstSection, *pLastSection;
53    struct TgIniFileInfoStruct *pNextInfo, *pPrevInfo;
54 } TGINIFILEINFO;
55 
56 typedef struct TgIniStruct {
57    TGINIFILEINFO *pFirstInfo, *pLastInfo;
58 } TGINI;
59 
60 static TGINI tgIni;
61 
62 /* -------------------- Utility Functions -------------------- */
63 
64 static
InvalidPath(pszFile)65 int InvalidPath(pszFile)
66    char *pszFile;
67 {
68    sprintf(gszMsgBox, TgLoadString(STID_INVALID_GIVEN_PATH_MUST_FULL), pszFile);
69    fprintf(stderr, "%s\n", gszMsgBox);
70    MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
71    return FALSE;
72 }
73 
74 static
GetLastModifiedTime(pszFile,pFileTime)75 int GetLastModifiedTime(pszFile, pFileTime)
76    char *pszFile;
77    time_t *pFileTime;
78 {
79    struct stat stat_buf;
80 
81    if (stat(pszFile, &stat_buf) == 0) {
82       memcpy(pFileTime, &stat_buf.st_mtime, sizeof(time_t));
83       return TRUE;
84    }
85    return FALSE;
86 }
87 
88 static
CompareFileTime(pNewFileTime,pOldFileTime)89 int CompareFileTime(pNewFileTime, pOldFileTime)
90    time_t *pNewFileTime, *pOldFileTime;
91 {
92    long lval=((long)(*pNewFileTime)) - ((long)(*pOldFileTime));
93    int ival=(int)lval;
94 
95    if (lval > 0) {
96       if (ival < 0) return (-ival);
97    } else if (lval < 0) {
98       if (ival > 0) return (-ival);
99    } else {
100       ival = 0;
101    }
102    return ival;
103 }
104 
105 /* -------------------- Discard Functions -------------------- */
106 
107 static
UnlinkFileInfo(ptifi)108 void UnlinkFileInfo(ptifi)
109    TGINIFILEINFO *ptifi;
110 {
111    if (ptifi->pPrevInfo != NULL) {
112       ptifi->pPrevInfo->pNextInfo = ptifi->pNextInfo;
113    } else {
114       tgIni.pFirstInfo = ptifi->pNextInfo;
115    }
116    if (ptifi->pNextInfo != NULL) {
117       ptifi->pNextInfo->pPrevInfo = ptifi->pPrevInfo;
118    } else {
119       tgIni.pLastInfo = ptifi->pPrevInfo;
120    }
121 }
122 
123 static
DiscardEntryInfo(ptiei)124 void DiscardEntryInfo(ptiei)
125    TGINIENTRYINFO *ptiei;
126 {
127    UtilFree(ptiei->pszEntry);
128    UtilFree(ptiei->pszValue);
129 }
130 
131 static
DiscardSectionInfo(ptisi)132 void DiscardSectionInfo(ptisi)
133    TGINISECTIONINFO *ptisi;
134 {
135    TGINIENTRYINFO *ptiei=NULL, *ptiei_next;
136 
137    for (ptiei=ptisi->pFirstEntry; ptiei != NULL; ptiei=ptiei_next) {
138       ptiei_next = ptiei->pNextEntry;
139       DiscardEntryInfo(ptiei);
140       free(ptiei);
141    }
142    UtilFree(ptisi->pszSection);
143 }
144 
145 static
WriteOutIniFile(ptifi,pszAltFile)146 int WriteOutIniFile(ptifi, pszAltFile)
147    TGINIFILEINFO *ptifi;
148    char *pszAltFile;
149 {
150    int ok=TRUE;
151    char *pszFile=(pszAltFile==NULL ? ptifi->pszFile : pszAltFile);
152    char *c_ptr=strchr(pszFile, DIR_SEP);
153    FILE *pFile=NULL;
154    TGINISECTIONINFO *ptisi=NULL;
155 
156    if (c_ptr == NULL) {
157       return InvalidPath(pszFile);
158    }
159    if ((pFile=fopen(pszFile, "w")) == NULL) {
160       return FailToWriteFileMessage(pszFile);
161    }
162    for (ptisi=ptifi->pFirstSection; ok && ptisi != NULL;
163          ptisi=ptisi->pNextSection) {
164       int bValuelessKey=ptisi->bValuelessKey;
165       TGINIENTRYINFO *ptiei=NULL;
166 
167       /* do not translate -- program constants */
168       if (fprintf(pFile, "[%s]\n", ptisi->pszSection) <= 0) {
169          ok = FALSE;
170          break;
171       }
172       for (ptiei=ptisi->pFirstEntry; ok && ptiei != NULL;
173             ptiei=ptiei->pNextEntry) {
174          /* remember that the key is ";" for a comment line */
175          if (strcmp(ptiei->pszEntry, ";") == 0) {
176             if (fprintf(pFile, "%s\n", ptiei->pszValue) <= 0) {
177                ok = FailToWriteFileMessage(pszFile);
178                break;
179             }
180          } else if (bValuelessKey) {
181             if (fprintf(pFile, "%s\n", ptiei->pszEntry) <= 0) {
182                ok = FailToWriteFileMessage(pszFile);
183                break;
184             }
185          } else {
186             if (fprintf(pFile, "%s=%s\n", ptiei->pszEntry,
187                   ptiei->pszValue) <= 0) {
188                ok = FailToWriteFileMessage(pszFile);
189                break;
190             }
191          }
192       }
193       if (ok && ptisi->pNextSection != NULL) {
194          if (fprintf(pFile, "\n") <= 0) {
195             ok = FailToWriteFileMessage(pszFile);
196             break;
197          }
198       }
199    }
200    if (fclose(pFile) == EOF) {
201       return FailToWriteFileMessage(pszFile);
202    }
203    ptifi->bModified = FALSE;
204    return ok;
205 }
206 
207 static
DiscardFileInfo(ptifi)208 int DiscardFileInfo(ptifi)
209    TGINIFILEINFO *ptifi;
210 {
211    TGINISECTIONINFO *ptisi=NULL, *ptisi_next;
212    int ok=TRUE;
213 
214    if (ptifi->bModified) {
215       ok = WriteOutIniFile(ptifi, NULL);
216    }
217    for (ptisi=ptifi->pFirstSection; ptisi != NULL; ptisi=ptisi_next) {
218       ptisi_next = ptisi->pNextSection;
219       DiscardSectionInfo(ptisi);
220       free(ptisi);
221    }
222    UtilFree(ptifi->pszFile);
223    return ok;
224 }
225 
226 /* -------------------- Allocation Functions -------------------- */
227 
228 static
AllocEntryInfo(ptisi,pszEntry,pszValue)229 TGINIENTRYINFO *AllocEntryInfo(ptisi, pszEntry, pszValue)
230    TGINISECTIONINFO *ptisi;
231    char *pszEntry, *pszValue;
232 {
233    /* for a comment line, make the key to be ";" */
234    char *buf1=UtilStrDup(pszEntry==NULL ? ";" : pszEntry);
235    char *buf2=UtilStrDup(pszValue);
236    TGINIENTRYINFO *ptiei=
237          (TGINIENTRYINFO*)malloc(sizeof(TGINIENTRYINFO));
238 
239    if (buf1 == NULL || buf2 == NULL || ptiei == NULL) {
240       UtilFree(buf1);
241       UtilFree(buf2);
242       free(ptiei);
243       FailAllocMessage();
244       return NULL;
245    }
246    memset(ptiei, 0, sizeof(TGINIENTRYINFO));
247 
248    /* first-in-first-out */
249    ptiei->pNextEntry = NULL;
250    ptiei->pPrevEntry = ptisi->pLastEntry;
251    if (ptisi->pLastEntry == NULL) {
252       ptisi->pFirstEntry = ptiei;
253    } else {
254       ptisi->pLastEntry->pNextEntry = ptiei;
255    }
256    ptisi->pLastEntry = ptiei;
257 
258    ptiei->pszEntry = buf1;
259    ptiei->pszValue = buf2;
260    ptiei->bScanned = FALSE;
261    return ptiei;
262 }
263 
264 static
AllocSectionInfo(ptifi,pszSection,bValuelessKey)265 TGINISECTIONINFO *AllocSectionInfo(ptifi, pszSection, bValuelessKey)
266    TGINIFILEINFO *ptifi;
267    char *pszSection;
268    int bValuelessKey;
269 {
270    char *buf=UtilStrDup(pszSection);
271    TGINISECTIONINFO *ptisi=
272          (TGINISECTIONINFO*)malloc(sizeof(TGINISECTIONINFO));
273 
274    if (buf == NULL || ptisi == NULL) {
275       UtilFree(buf);
276       free(ptisi);
277       FailAllocMessage();
278       return NULL;
279    }
280    memset(ptisi, 0, sizeof(TGINISECTIONINFO));
281 
282    /* first-in-first-out */
283    ptisi->pNextSection = NULL;
284    ptisi->pPrevSection = ptifi->pLastSection;
285    if (ptifi->pLastSection == NULL) {
286       ptifi->pFirstSection = ptisi;
287    } else {
288       ptifi->pLastSection->pNextSection = ptisi;
289    }
290    ptifi->pLastSection = ptisi;
291 
292    ptisi->pszSection = buf;
293    ptisi->bAllowDupKey = FALSE;
294    ptisi->bValuelessKey = bValuelessKey;
295    return ptisi;
296 }
297 
298 static
AllocFileInfo(pszFile)299 TGINIFILEINFO *AllocFileInfo(pszFile)
300    char *pszFile;
301 {
302    char *buf=UtilStrDup(pszFile);
303    TGINIFILEINFO *ptifi=(TGINIFILEINFO*)malloc(sizeof(TGINIFILEINFO));
304    if (buf == NULL || ptifi == NULL) {
305       UtilFree(buf);
306       free(ptifi);
307       FailAllocMessage();
308       return NULL;
309    }
310    memset(ptifi, 0, sizeof(TGINIFILEINFO));
311 
312    /* last-in-first-out */
313    ptifi->pNextInfo = tgIni.pFirstInfo;
314    ptifi->pPrevInfo = NULL;
315    if (tgIni.pFirstInfo == NULL) {
316       tgIni.pLastInfo = ptifi;
317    } else {
318       tgIni.pFirstInfo->pPrevInfo = ptifi;
319    }
320    tgIni.pFirstInfo = ptifi;
321 
322    ptifi->pszFile = buf;
323    ptifi->bModified = FALSE;
324    ptifi->bStripQuotes = TRUE;
325    ptifi->bCheckFileTime = TRUE;
326    memset(&ptifi->stFileTime, 0, sizeof(time_t));
327 
328    if (!GetLastModifiedTime(pszFile, &ptifi->stFileTime)) {
329       memset(&ptifi->stFileTime, 0, sizeof(time_t));
330    }
331    return ptifi;
332 }
333 
334 /* -------------------- Parse Functions -------------------- */
335 
336 static
ParseFile(ptifi)337 int ParseFile(ptifi)
338    TGINIFILEINFO *ptifi;
339 {
340    char *buf;
341    TGINISECTIONINFO *ptisi=NULL;
342    char *pszFile=ptifi->pszFile;
343    char *c_ptr=strchr(pszFile, DIR_SEP);
344    FILE *pFile=NULL;
345 
346    if (c_ptr == NULL) {
347       return InvalidPath(pszFile);
348    }
349    if ((pFile=fopen(pszFile, "r")) == NULL) return TRUE;
350 
351    while ((buf=UtilGetALine(pFile)) != NULL) {
352       char *c_ptr;
353 
354       UtilTrimBlanks(buf);
355       if (*buf == ';') {
356          /* handle comments, remember that the key is ";" for a comment line */
357          if (ptisi != NULL) {
358             if (AllocEntryInfo(ptisi, NULL, buf) == NULL) return FALSE;
359          }
360       } else if (*buf == '[') {
361          if ((c_ptr=strchr(&buf[1], ']')) == NULL) {
362             ptisi = NULL;
363          } else {
364             *c_ptr = '\0';
365             /*
366              * Don't need to call UtilTrimBlanks(&buf[1]) because that's how
367              *      GetProfileString() works!
368              */
369             if ((ptisi=AllocSectionInfo(ptifi, &buf[1], FALSE)) == NULL) {
370                return FALSE;
371             }
372          }
373       } else if (ptisi != NULL) {
374          if ((c_ptr=strchr(buf, '=')) != NULL) {
375             char *psz=(&c_ptr[1]);
376 
377             *c_ptr = '\0';
378             c_ptr = psz;
379             UtilTrimBlanks(buf);
380             UtilTrimBlanks(c_ptr);
381             if (AllocEntryInfo(ptisi, buf, c_ptr) == NULL) return FALSE;
382             if (ptisi->bValuelessKey) {
383 #ifdef _DEBUG /* debug, do not translate */
384                fprintf(stderr, "%s [%s] of '%s'.\n",
385                      "WARNING: Ambiguous valueless key section",
386                      ptisi->pszSection, pszFile);
387 #endif /* _DEBUG */
388             }
389          } else if (*buf != '\0') {
390             char cNull='\0';
391 
392             if (AllocEntryInfo(ptisi, buf, &cNull) == NULL) return FALSE;
393             ptisi->bValuelessKey = TRUE;
394          }
395       }
396       UtilFree(buf);
397    }
398    fclose(pFile);
399    return TRUE;
400 }
401 
402 /* -------------------- External Functions -------------------- */
403 
404 static
FindEntryInfo(ptisi,pszEntry)405 TGINIENTRYINFO *FindEntryInfo(ptisi, pszEntry)
406    TGINISECTIONINFO *ptisi;
407    char *pszEntry;
408 {
409    if (ptisi == NULL) return NULL;
410 
411     /* finding a comment line is not allowed */
412    if (pszEntry == NULL || strcmp(pszEntry, ";") == 0) return NULL;
413 
414    if (ptisi->bAllowDupKey) {
415       TGINIENTRYINFO *ptiei=NULL;
416 
417       for (ptiei=ptisi->pFirstEntry; ptiei != NULL;
418             ptiei=ptiei->pNextEntry) {
419          if (!ptiei->bScanned && UtilStrICmp(pszEntry, ptiei->pszEntry) == 0) {
420             ptiei->bScanned = TRUE;
421             return ptiei;
422          }
423       }
424    } else {
425       TGINIENTRYINFO *ptiei=NULL;
426 
427       for (ptiei=ptisi->pFirstEntry; ptiei != NULL;
428             ptiei=ptiei->pNextEntry) {
429          if (UtilStrICmp(pszEntry, ptiei->pszEntry) == 0) {
430             return ptiei;
431          }
432       }
433    }
434    return NULL;
435 }
436 
437 static
FindSectionInfo(ptifi,pszSection)438 TGINISECTIONINFO *FindSectionInfo(ptifi, pszSection)
439    TGINIFILEINFO *ptifi;
440    char *pszSection;
441 {
442    TGINISECTIONINFO *ptisi=NULL;
443 
444    if (ptifi == NULL) return NULL;
445 
446    for (ptisi=ptifi->pFirstSection; ptisi != NULL;
447          ptisi=ptisi->pNextSection) {
448       if (UtilStrICmp(pszSection, ptisi->pszSection) == 0) {
449          return ptisi;
450       }
451    }
452    return NULL;
453 }
454 
455 static
TgIniFindFileInfo(pszFile,bAutoLoad)456 TGINIFILEINFO *TgIniFindFileInfo(pszFile, bAutoLoad)
457    char *pszFile;
458    int bAutoLoad;
459 {
460    TGINIFILEINFO *ptifi=NULL;
461 
462    if (pszFile == NULL) return NULL;
463 
464    for (ptifi=tgIni.pFirstInfo; ptifi != NULL; ptifi=ptifi->pNextInfo) {
465       if (UtilStrICmp(pszFile, ptifi->pszFile) == 0) {
466          if (ptifi->bCheckFileTime) {
467             time_t stFileTime;
468 
469             memset(&stFileTime, 0, sizeof(time_t));
470             if (!GetLastModifiedTime(pszFile, &stFileTime) ||
471                   CompareFileTime(&stFileTime, &ptifi->stFileTime) > 0) {
472                DiscardFileInfo(ptifi);
473                UnlinkFileInfo(ptifi);
474                free(ptifi);
475                ptifi = NULL;
476                break;
477             }
478          }
479          if (ptifi != NULL) return ptifi;
480       }
481    }
482    if (!bAutoLoad) return NULL;
483    if ((ptifi=AllocFileInfo(pszFile)) == NULL) return NULL;
484    if (!ParseFile(ptifi)) {
485       DiscardFileInfo(ptifi);
486       UnlinkFileInfo(ptifi);
487       free(ptifi);
488       return NULL;
489    }
490    return ptifi;
491 }
492 
493 static
TgIniDiscardFileInfo(pszFile)494 int TgIniDiscardFileInfo(pszFile)
495    char *pszFile;
496 {
497    TGINIFILEINFO *ptifi=TgIniFindFileInfo(pszFile, FALSE);
498    int ok=TRUE;
499 
500    if (ptifi == NULL) return TRUE;
501 
502    ok = DiscardFileInfo(ptifi);
503 
504    UnlinkFileInfo(ptifi);
505    free(ptifi);
506    return ok;
507 }
508 
509 static
TgIniBeginFastProfile(ptifi)510 void TgIniBeginFastProfile(ptifi)
511    TGINIFILEINFO *ptifi;
512 {
513    if (ptifi != NULL) ptifi->bCheckFileTime = FALSE;
514 }
515 
516 static
TgIniEndFastProfile(ptifi)517 void TgIniEndFastProfile(ptifi)
518    TGINIFILEINFO *ptifi;
519 {
520    if (ptifi != NULL) ptifi->bCheckFileTime = TRUE;
521 }
522 
523 static
TgIniSetProfileStripQuotes(ptifi,bStripQuotes)524 int TgIniSetProfileStripQuotes(ptifi, bStripQuotes)
525    TGINIFILEINFO *ptifi;
526    int bStripQuotes;
527 {
528    if (ptifi != NULL) {
529       int bReturn=ptifi->bStripQuotes;
530 
531       ptifi->bStripQuotes = bStripQuotes;
532       return bReturn;
533    }
534    return FALSE;
535 }
536 
537 static
TgIniGetProfileString(pszSection,pszEntry,ptifi)538 char *TgIniGetProfileString(pszSection, pszEntry, ptifi)
539    char *pszSection, *pszEntry;
540    TGINIFILEINFO *ptifi;
541 {
542    char *buf=NULL;
543    int buf_sz=0, cur_len=0;
544 
545    if (pszSection == NULL) {
546       TGINISECTIONINFO *ptisi=NULL;
547 
548       for (ptisi=ptifi->pFirstSection; ptisi != NULL;
549             ptisi=ptisi->pNextSection) {
550          int len=strlen(ptisi->pszSection);
551 
552          while (len+cur_len+6 >= buf_sz) {
553             buf_sz += 0x100;
554             if (buf == NULL) {
555                buf = (char*)malloc(buf_sz);
556             } else {
557                buf = (char*)realloc(buf, (size_t)buf_sz);
558             }
559             if (buf == NULL) {
560                FailAllocMessage();
561                return NULL;
562             }
563          }
564          strcpy(&buf[cur_len], ptisi->pszSection);
565          cur_len = (int)((&buf[len+cur_len+1]) - buf);
566       }
567       if (buf != NULL) buf[cur_len] = '\0';
568    } else if (pszEntry == NULL) {
569       TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
570 
571       if (ptisi != NULL) {
572          TGINIENTRYINFO *ptiei=NULL;
573 
574          for (ptiei=ptisi->pFirstEntry; ptiei != NULL;
575                ptiei=ptiei->pNextEntry) {
576             int len=0;
577 
578             if (strcmp(ptiei->pszEntry, ";") == 0) {
579                /*
580                 * skip comments, remember that the key is ";"
581                 * for a comment line
582                 */
583                continue;
584             }
585             len = strlen(ptiei->pszEntry);
586 
587             while (len+cur_len+6 >= buf_sz) {
588                buf_sz += 0x100;
589                if (buf == NULL) {
590                   buf = (char*)malloc(buf_sz);
591                } else {
592                   buf = (char*)realloc(buf, (size_t)buf_sz);
593                }
594                if (buf == NULL) {
595                   FailAllocMessage();
596                   return NULL;
597                }
598             }
599             strcpy(&buf[cur_len], ptiei->pszEntry);
600             cur_len = (int)((&buf[len+cur_len+1]) - buf);
601          }
602       }
603       if (buf != NULL) buf[cur_len] = '\0';
604    } else {
605       TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
606 
607       if (ptisi != NULL) {
608          TGINIENTRYINFO *ptiei=FindEntryInfo(ptisi, pszEntry);
609 
610          if (ptiei != NULL) {
611             buf = UtilStrDup(ptiei->pszValue);
612             if (buf != NULL && ptifi->bStripQuotes) {
613                int nNumChars=strlen(buf);
614 
615                if (nNumChars > 1 && buf[0] == buf[nNumChars-1]) {
616                   char *pszLastChar=(&buf[nNumChars-1]);
617 
618                   if (*buf == '\'' || *buf == '"') {
619                      char *pby=buf, *pby1=(&buf[1]);
620 
621                      while (pby1 != pszLastChar) {
622                         *pby++ = *pby1++;
623                      }
624                      *((char*)pby) = '\0';
625                   }
626                }
627             }
628          }
629       }
630    }
631    return buf;
632 }
633 
634 static
TgIniWriteProfileString(pszSection,pszEntry,pszValue,ptifi)635 int TgIniWriteProfileString(pszSection, pszEntry, pszValue, ptifi)
636    char *pszSection, *pszEntry, *pszValue;
637    TGINIFILEINFO *ptifi;
638 {
639    TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
640 
641    if (ptisi == NULL) {
642       /* append */
643       if ((ptisi=AllocSectionInfo(ptifi, pszSection, FALSE)) == NULL) {
644          return FALSE;
645       }
646    }
647    if (pszEntry == NULL) {
648       TGINIENTRYINFO *ptiei=NULL, *ptiei_next;
649 
650       for (ptiei=ptisi->pFirstEntry; ptiei != NULL; ptiei=ptiei_next) {
651          ptiei_next = ptiei->pNextEntry;
652          DiscardEntryInfo(ptiei);
653          free(ptiei);
654       }
655       ptisi->pFirstEntry = ptisi->pLastEntry = NULL;
656    } else if (strcmp(pszEntry, ";") == 0) {
657       /* can't write a comment line into an INI file */
658       return FALSE;
659    } else {
660       TGINIENTRYINFO *ptiei=NULL;
661 
662       if (!ptisi->bAllowDupKey) {
663          ptiei = FindEntryInfo(ptisi, pszEntry);
664       }
665       if (ptiei == NULL) {
666          /* append */
667          if (AllocEntryInfo(ptisi, pszEntry,
668                (pszValue==NULL ? "" : pszValue)) == NULL) {
669             return FALSE;
670          }
671       } else if (pszValue == NULL) {
672          if (ptiei->pPrevEntry == NULL) {
673             ptisi->pFirstEntry = ptiei->pNextEntry;
674          } else {
675             ptiei->pPrevEntry->pNextEntry = ptiei->pNextEntry;
676          }
677          if (ptiei->pNextEntry == NULL) {
678             ptisi->pLastEntry = ptiei->pPrevEntry;
679          } else {
680             ptiei->pNextEntry->pPrevEntry = ptiei->pPrevEntry;
681          }
682          DiscardEntryInfo(ptiei);
683          free(ptiei);
684       } else {
685          if (ptisi->bValuelessKey) {
686 #ifdef _DEBUG /* debug, do not translate */
687             fprintf(stderr, "%s %s section [%s] of '%s'.\n",
688                   "WARNING: tgWriteProfileString() is called with a",
689                   "non-NULL pszValue for a valueless key",
690                   ptisi->pszSection, ptifi->pszFile);
691 #endif /* _DEBUG */
692          }
693          /* replace */
694          UtilFree(ptiei->pszValue);
695          if ((ptiei->pszValue=UtilStrDup(pszValue)) ==
696                NULL) {
697             return FALSE;
698          }
699       }
700    }
701    ptifi->bModified = TRUE;
702    return TRUE;
703 }
704 
705 static
TgIniBeginDupKeySection(pszSection,ptifi)706 void TgIniBeginDupKeySection(pszSection, ptifi)
707    char *pszSection;
708    TGINIFILEINFO *ptifi;
709 {
710    TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
711    TGINIENTRYINFO *ptiei=NULL;
712 
713    if (ptisi == NULL) return;
714 
715    for (ptiei=ptisi->pFirstEntry; ptiei != NULL; ptiei=ptiei->pNextEntry) {
716       ptiei->bScanned = FALSE;
717    }
718    ptisi->bAllowDupKey = TRUE;
719 }
720 
721 static
TgIniEndDupKeySection(pszSection,ptifi)722 void TgIniEndDupKeySection(pszSection, ptifi)
723    char *pszSection;
724    TGINIFILEINFO *ptifi;
725 {
726    TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
727 
728    if (ptisi == NULL) return;
729 
730    ptisi->bAllowDupKey = FALSE;
731 }
732 
733 static
TgIniBeginValuelessKeySection(pszSection,ptifi)734 int TgIniBeginValuelessKeySection(pszSection, ptifi)
735    char *pszSection;
736    TGINIFILEINFO *ptifi;
737 {
738    TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
739    int bValuelessKey=FALSE;
740 
741    if (ptisi == NULL) {
742       if ((ptisi=AllocSectionInfo(ptifi, pszSection, TRUE)) == NULL) {
743          return FALSE;
744       }
745    }
746    if (ptisi->pFirstEntry == NULL) {
747       if (!ptisi->bValuelessKey) {
748          ptisi->bValuelessKey = TRUE;
749       }
750    } else {
751       if (!ptisi->bValuelessKey) {
752 #ifdef _DEBUG /* debug, do not translate */
753          fprintf(stderr, "%s non-valueless section [%s] of '%s'.\n",
754                "WARNING: tgBeginValuelessKeySection() is called for a",
755                pszSection, ptifi->pszFile);
756 #endif /* _DEBUG */
757       }
758    }
759    bValuelessKey = ptisi->bValuelessKey;
760 
761    ptisi->bValuelessKey = TRUE;
762    return bValuelessKey;
763 }
764 
765 static
TgIniEndValuelessKeySection(pszSection,ptifi,bValuelessKey)766 void TgIniEndValuelessKeySection(pszSection, ptifi, bValuelessKey)
767    char *pszSection;
768    TGINIFILEINFO *ptifi;
769    int bValuelessKey;
770 {
771    TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
772 
773    if (ptisi == NULL) return;
774 
775    if (ptisi->bValuelessKey != bValuelessKey) {
776 #ifdef _DEBUG /* debug, do not translate */
777       fprintf(stderr, "%s valuess-ness for section [%s] of '%s'.\n",
778             "WARNING: tgEndValuelessKeySection() changes the",
779             pszSection, ptifi->pszFile);
780 #endif /* _DEBUG */
781    }
782    ptisi->bValuelessKey = bValuelessKey;
783 }
784 
785 static
TgIniIsValuelessKeySection(pszSection,ptifi)786 int TgIniIsValuelessKeySection(pszSection, ptifi)
787    char *pszSection;
788    TGINIFILEINFO *ptifi;
789 {
790    TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
791 
792    if (ptisi == NULL) {
793 #ifdef _DEBUG /* debug, do not translate */
794       fprintf(stderr, "%s section [%s] of '%s'.\n",
795             "WARNING: tgIsValuelessKeySection() is called with a non-existant",
796             pszSection, ptifi->pszFile);
797 #endif /* _DEBUG */
798       return FALSE;
799    }
800    return ptisi->bValuelessKey;
801 }
802 
803 static
TgIniDeleteDupKeyValue(pszSection,pszEntry,pszValue,ptifi)804 int TgIniDeleteDupKeyValue(pszSection, pszEntry, pszValue, ptifi)
805    char *pszSection, *pszEntry, *pszValue;
806    TGINIFILEINFO *ptifi;
807 {
808    TGINISECTIONINFO *ptisi=FindSectionInfo(ptifi, pszSection);
809    TGINIENTRYINFO *ptiei=NULL;
810 
811    if (ptisi == NULL) {
812       return TRUE;
813    }
814    TgIniBeginDupKeySection(pszSection, ptifi);
815    for (ptiei=FindEntryInfo(ptisi, pszEntry); ptiei != NULL;
816          ptiei=FindEntryInfo(ptisi, pszEntry)) {
817       if (UtilStrICmp(pszValue, ptiei->pszValue) == 0) {
818          if (ptiei->pPrevEntry == NULL) {
819             ptisi->pFirstEntry = ptiei->pNextEntry;
820          } else {
821             ptiei->pPrevEntry->pNextEntry = ptiei->pNextEntry;
822          }
823          if (ptiei->pNextEntry == NULL) {
824             ptisi->pLastEntry = ptiei->pPrevEntry;
825          } else {
826             ptiei->pNextEntry->pPrevEntry = ptiei->pPrevEntry;
827          }
828          DiscardEntryInfo(ptiei);
829          free(ptiei);
830          ptifi->bModified = TRUE;
831          break;
832       }
833    }
834    TgIniEndDupKeySection(pszSection, ptifi);
835    return TRUE;
836 }
837 
838 static
TgIniCleanUp()839 void TgIniCleanUp()
840 {
841    TGINIFILEINFO *ptifi=NULL, *ptifi_next;
842 
843    for (ptifi=tgIni.pFirstInfo; ptifi != NULL; ptifi=ptifi_next) {
844       ptifi_next = ptifi->pNextInfo;
845       DiscardFileInfo(ptifi);
846       free(ptifi);
847    }
848    tgIni.pFirstInfo = tgIni.pLastInfo = NULL;
849 }
850 
851 /* -------------------- Exported Functions -------------------- */
852 
tgCleanUpProfile()853 void tgCleanUpProfile()
854 {
855    TgIniCleanUp();
856 }
857 
tgFreeProfileString(pszStr)858 char *tgFreeProfileString(pszStr)
859    char *pszStr;
860 {
861    UtilFree(pszStr);
862    return NULL;
863 }
864 
tgBeginFastProfile(pszFile)865 void tgBeginFastProfile(pszFile)
866    char *pszFile;
867 {
868    TGINIFILEINFO *ptifi=TgIniFindFileInfo(pszFile, TRUE);
869 
870    if (ptifi == NULL) return;
871 
872    TgIniBeginFastProfile(ptifi);
873 }
874 
tgEndFastProfile(pszFile)875 void tgEndFastProfile(pszFile)
876    char *pszFile;
877 {
878    TGINIFILEINFO *ptifi=TgIniFindFileInfo(pszFile, TRUE);
879 
880    if (ptifi == NULL) return;
881 
882    TgIniEndFastProfile(ptifi);
883 }
884 
tgSetProfileStripQuotes(pszFile,bStripQuotes)885 int tgSetProfileStripQuotes(pszFile, bStripQuotes)
886    char *pszFile;
887    int bStripQuotes;
888 {
889    TGINIFILEINFO *ptifi=TgIniFindFileInfo(pszFile, TRUE);
890 
891    if (ptifi == NULL) return FALSE;
892 
893    return TgIniSetProfileStripQuotes(ptifi, bStripQuotes);
894 }
895 
tgGetProfileString(pszSection,pszEntry,pszFile)896 char *tgGetProfileString(pszSection, pszEntry, pszFile)
897    char *pszSection, *pszEntry, *pszFile;
898 {
899    TGINIFILEINFO *ptifi=TgIniFindFileInfo(pszFile, TRUE);
900 
901    if (ptifi == NULL) return NULL;
902 
903    return TgIniGetProfileString(pszSection, pszEntry, ptifi);
904 }
905 
tgGetProfileInt(pszSection,pszEntry,nDefault,pszFile)906 int tgGetProfileInt(pszSection, pszEntry, nDefault, pszFile)
907    char *pszSection, *pszEntry, *pszFile;
908    int nDefault;
909 {
910    TGINIFILEINFO *ptifi=NULL;
911    char *c_ptr=NULL;
912    int rc=nDefault;
913 
914    if (pszSection == NULL) {
915       int rc=(TgIniDiscardFileInfo(pszFile) ? nDefault : (int)(nDefault==0));
916 
917       return rc;
918    }
919    ptifi = TgIniFindFileInfo(pszFile, TRUE);
920 
921    if (ptifi == NULL) return nDefault;
922 
923    c_ptr = TgIniGetProfileString(pszSection, pszEntry, ptifi);
924    rc = (c_ptr == NULL ? nDefault : (int)atoi(c_ptr));
925 
926    UtilFree(c_ptr);
927    return rc;
928 }
929 
tgWriteProfileString(pszSection,pszEntry,pszString,pszFile)930 int tgWriteProfileString(pszSection, pszEntry, pszString, pszFile)
931    char *pszSection, *pszEntry, *pszString, *pszFile;
932 {
933    TGINIFILEINFO *ptifi=TgIniFindFileInfo(pszFile, TRUE);
934 
935    if (pszSection == NULL) {
936       if (ptifi == NULL || !ptifi->bModified) return TRUE;
937       return WriteOutIniFile(ptifi, NULL);
938    }
939    return TgIniWriteProfileString(pszSection, pszEntry, pszString, ptifi);
940 }
941 
tgBeginDupKeySection(pszSection,pszFile)942 void tgBeginDupKeySection(pszSection, pszFile)
943    char *pszSection, *pszFile;
944 {
945    TGINIFILEINFO *ptifi=NULL;
946 
947    if (pszSection == NULL) return;
948 
949    ptifi = TgIniFindFileInfo(pszFile, TRUE);
950 
951    if (ptifi == NULL) return;
952 
953    TgIniBeginDupKeySection(pszSection, ptifi);
954 }
955 
tgEndDupKeySection(pszSection,pszFile)956 void tgEndDupKeySection(pszSection, pszFile)
957    char *pszSection, *pszFile;
958 {
959    TGINIFILEINFO *ptifi=NULL;
960 
961    if (pszSection == NULL) return;
962 
963    ptifi = TgIniFindFileInfo(pszFile, TRUE);
964 
965    if (ptifi == NULL) return;
966 
967    TgIniEndDupKeySection(pszSection, ptifi);
968 }
969 
tgBeginValuelessKeySection(pszSection,pszFile)970 int tgBeginValuelessKeySection(pszSection, pszFile)
971    char *pszSection, *pszFile;
972 {
973    TGINIFILEINFO *ptifi=NULL;
974 
975    if (pszSection == NULL) return FALSE;
976 
977    ptifi = TgIniFindFileInfo(pszFile, TRUE);
978 
979    if (ptifi == NULL) return FALSE;
980 
981    return TgIniBeginValuelessKeySection(pszSection, ptifi);
982 }
983 
tgEndValuelessKeySection(pszSection,pszFile,bValuelessKey)984 void tgEndValuelessKeySection(pszSection, pszFile, bValuelessKey)
985    char *pszSection, *pszFile;
986    int bValuelessKey;
987 {
988    TGINIFILEINFO *ptifi=NULL;
989 
990    if (pszSection == NULL) return;
991 
992    ptifi = TgIniFindFileInfo(pszFile, TRUE);
993 
994    if (ptifi == NULL) return;
995 
996    TgIniEndValuelessKeySection(pszSection, ptifi, bValuelessKey);
997 }
998 
tgIsValuelessKeySection(pszSection,pszFile)999 int tgIsValuelessKeySection(pszSection, pszFile)
1000    char *pszSection, *pszFile;
1001 {
1002    TGINIFILEINFO *ptifi=NULL;
1003 
1004    if (pszSection == NULL) return FALSE;
1005 
1006    ptifi = TgIniFindFileInfo(pszFile, TRUE);
1007 
1008    if (ptifi == NULL) return FALSE;
1009 
1010    return TgIniIsValuelessKeySection(pszSection, ptifi);
1011 }
1012 
tgDeleteDupKeyValue(pszSection,pszEntry,pszValue,pszFile)1013 int tgDeleteDupKeyValue(pszSection, pszEntry, pszValue, pszFile)
1014    char *pszSection, *pszEntry, *pszValue, *pszFile;
1015 {
1016    TGINIFILEINFO *ptifi=NULL;
1017 
1018    if (pszFile == NULL || pszSection == NULL || pszEntry == NULL ||
1019          pszValue == NULL) {
1020       return FALSE;
1021    }
1022    ptifi = TgIniFindFileInfo(pszFile, TRUE);
1023 
1024    if (ptifi == NULL) return TRUE;
1025 
1026    return TgIniDeleteDupKeyValue(pszSection, pszEntry, pszValue, ptifi);
1027 }
1028 
1029