| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952 | /*  minIni - Multi-Platform INI file parser, suitable for embedded systems * *  These routines are in part based on the article "Multiplatform .INI Files" *  by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal. * *  Copyright (c) CompuPhase, 2008-2021 * *  Licensed under the Apache License, Version 2.0 (the "License"); you may not *  use this file except in compliance with the License. You may obtain a copy *  of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * *  Unless required by applicable law or agreed to in writing, software *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the *  License for the specific language governing permissions and limitations *  under the License. * *  Version: $Id: minIni.c 53 2015-01-18 13:35:11Z thiadmer.riemersma@gmail.com $ */#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY# if !defined UNICODE   /* for Windows */#   define UNICODE# endif# if !defined _UNICODE  /* for C library */#   define _UNICODE# endif#endif#define MININI_IMPLEMENTATION#include "minIni.h"#if defined NDEBUG  #define assert(e)#else  #include <assert.h>#endif#if !defined __T || defined INI_ANSIONLY  #include <ctype.h>  #include <string.h>  #include <stdlib.h>  #define TCHAR     char  #define __T(s)    s  #define _tcscat   strcat  #define _tcschr   strchr  #define _tcscmp   strcmp  #define _tcscpy   strcpy  #define _tcsicmp  stricmp  #define _tcslen   strlen  #define _tcsncmp  strncmp  #define _tcsnicmp strnicmp  #define _tcsrchr  strrchr  #define _tcstol   strtol  #define _tcstod   strtod  #define _totupper toupper  #define _stprintf sprintf  #define _tfgets   fgets  #define _tfputs   fputs  #define _tfopen   fopen  #define _tremove  remove  #define _trename  rename#endif#if defined __linux || defined __linux__  #define __LINUX__#elif defined FREEBSD && !defined __FreeBSD__  #define __FreeBSD__#elif defined(_MSC_VER)  #pragma warning(disable: 4996)	/* for Microsoft Visual C/C++ */#endif#if !defined strnicmp && !defined PORTABLE_STRNICMP  #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ || defined __NetBSD__ || defined __DragonFly__ || defined __GNUC__    #define strnicmp  strncasecmp  #endif#endif#if !defined _totupper  #define _totupper toupper#endif#if !defined INI_LINETERM  #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ || defined __NetBSD__ || defined __DragonFly__    #define INI_LINETERM    __T("\n")  #else    #define INI_LINETERM    __T("\r\n")  #endif#endif#if !defined INI_FILETYPE  #error Missing definition for INI_FILETYPE.#endif#if !defined sizearray  #define sizearray(a)    (sizeof(a) / sizeof((a)[0]))#endifenum quote_option {  QUOTE_NONE,  QUOTE_ENQUOTE,  QUOTE_DEQUOTE,};#if defined PORTABLE_STRNICMPint strnicmp(const TCHAR *s1, const TCHAR *s2, size_t n){  while (n-- != 0 && (*s1 || *s2)) {    register int c1, c2;    c1 = *s1++;    if ('a' <= c1 && c1 <= 'z')      c1 += ('A' - 'a');    c2 = *s2++;    if ('a' <= c2 && c2 <= 'z')      c2 += ('A' - 'a');    if (c1 != c2)      return c1 - c2;  }  return 0;}#endif /* PORTABLE_STRNICMP */static TCHAR *skipleading(const TCHAR *str){  assert(str != NULL);  while ('\0' < *str && *str <= ' ')    str++;  return (TCHAR *)str;}static TCHAR *skiptrailing(const TCHAR *str, const TCHAR *base){  assert(str != NULL);  assert(base != NULL);  while (str > base && '\0' < *(str-1) && *(str-1) <= ' ')    str--;  return (TCHAR *)str;}static TCHAR *striptrailing(TCHAR *str){  TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str);  assert(ptr != NULL);  *ptr = '\0';  return str;}static TCHAR *ini_strncpy(TCHAR *dest, const TCHAR *source, size_t maxlen, enum quote_option option){  size_t d, s;  assert(maxlen>0);  assert(source != NULL && dest != NULL);  assert((dest < source || (dest == source && option != QUOTE_ENQUOTE)) || dest > source + strlen(source));  if (option == QUOTE_ENQUOTE && maxlen < 3)    option = QUOTE_NONE;  /* cannot store two quotes and a terminating zero in less than 3 characters */  switch (option) {  case QUOTE_NONE:    for (d = 0; d < maxlen - 1 && source[d] != '\0'; d++)      dest[d] = source[d];    assert(d < maxlen);    dest[d] = '\0';    break;  case QUOTE_ENQUOTE:    d = 0;    dest[d++] = '"';    for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) {      if (source[s] == '"') {        if (d >= maxlen - 3)          break;  /* no space to store the escape character plus the one that follows it */        dest[d++] = '\\';      }      dest[d] = source[s];    }    dest[d++] = '"';    dest[d] = '\0';    break;  case QUOTE_DEQUOTE:    for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) {      if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"')        s++;      dest[d] = source[s];    }    dest[d] = '\0';    break;  default:    assert(0);  }  return dest;}static TCHAR *cleanstring(TCHAR *string, enum quote_option *quotes){  int isstring;  TCHAR *ep;  assert(string != NULL);  assert(quotes != NULL);  /* Remove a trailing comment */  isstring = 0;  for (ep = string; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) {    if (*ep == '"') {      if (*(ep + 1) == '"')        ep++;                 /* skip "" (both quotes) */      else        isstring = !isstring; /* single quote, toggle isstring */    } else if (*ep == '\\' && *(ep + 1) == '"') {      ep++;                   /* skip \" (both quotes */    }  }  assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#'));  *ep = '\0';                 /* terminate at a comment */  striptrailing(string);  /* Remove double quotes surrounding a value */  *quotes = QUOTE_NONE;  if (*string == '"' && (ep = _tcschr(string, '\0')) != NULL && *(ep - 1) == '"') {    string++;    *--ep = '\0';    *quotes = QUOTE_DEQUOTE;  /* this is a string, so remove escaped characters */  }  return string;}static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key,                        int idxSection, int idxKey, TCHAR *Buffer, int BufferSize,                        INI_FILEPOS *mark){  TCHAR *sp, *ep;  int len, idx;  enum quote_option quotes;  TCHAR LocalBuffer[INI_BUFFERSIZE];  assert(fp != NULL);  /* Move through file 1 line at a time until a section is matched or EOF. If   * parameter Section is NULL, only look at keys above the first section. If   * idxSection is positive, copy the relevant section name.   */  len = (Section != NULL) ? (int)_tcslen(Section) : 0;  if (len > 0 || idxSection >= 0) {    assert(idxSection >= 0 || Section != NULL);    idx = -1;    do {      do {        if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp))          return 0;        sp = skipleading(LocalBuffer);        ep = _tcsrchr(sp, ']');      } while (*sp != '[' || ep == NULL);      /* When arrived here, a section was found; now optionally skip leading and       * trailing whitespace.       */      assert(sp != NULL && *sp == '[');      sp = skipleading(sp + 1);      assert(ep != NULL && *ep == ']');      ep = skiptrailing(ep, sp);    } while ((((int)(ep-sp) != len || Section == NULL || _tcsnicmp(sp, Section, len) != 0) && ++idx != idxSection));    if (idxSection >= 0) {      if (idx == idxSection) {        assert(ep != NULL);        *ep = '\0'; /* the end of the section name was found earlier */        ini_strncpy(Buffer, sp, BufferSize, QUOTE_NONE);        return 1;      }      return 0; /* no more section found */    }  }  /* Now that the section has been found, find the entry.   * Stop searching upon leaving the section's area.   */  assert(Key != NULL || idxKey >= 0);  len = (Key != NULL) ? (int)_tcslen(Key) : 0;  idx = -1;  do {    if (mark != NULL)      ini_tell(fp, mark);   /* optionally keep the mark to the start of the line */    if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[')      return 0;    sp = skipleading(LocalBuffer);    ep = _tcschr(sp, '=');  /* Parse out the equal sign */    if (ep == NULL)      ep = _tcschr(sp, ':');  } while (*sp == ';' || *sp == '#' || ep == NULL           || ((len == 0 || (int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey));  if (idxKey >= 0) {    if (idx == idxKey) {      assert(ep != NULL);      assert(*ep == '=' || *ep == ':');      *ep = '\0';      striptrailing(sp);      ini_strncpy(Buffer, sp, BufferSize, QUOTE_NONE);      return 1;    }    return 0;   /* no more key found (in this section) */  }  /* Copy up to BufferSize chars to buffer */  assert(ep != NULL);  assert(*ep == '=' || *ep == ':');  sp = skipleading(ep + 1);  sp = cleanstring(sp, "es);  /* Remove a trailing comment */  ini_strncpy(Buffer, sp, BufferSize, quotes);  return 1;}/** ini_gets() * \param Section     the name of the section to search for * \param Key         the name of the entry to find the value of * \param DefValue    default string in the event of a failed read * \param Buffer      a pointer to the buffer to copy into * \param BufferSize  the maximum number of characters to copy * \param Filename    the name and full path of the .ini file to read from * * \return            the number of characters copied into the supplied buffer */int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue,             TCHAR *Buffer, int BufferSize, const TCHAR *Filename){  INI_FILETYPE fp;  int ok = 0;  if (Buffer == NULL || BufferSize <= 0 || Key == NULL)    return 0;  if (ini_openread(Filename, &fp)) {    ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize, NULL);    (void)ini_close(&fp);  }  if (!ok)    ini_strncpy(Buffer, (DefValue != NULL) ? DefValue : __T(""), BufferSize, QUOTE_NONE);  return (int)_tcslen(Buffer);}/** ini_getl() * \param Section     the name of the section to search for * \param Key         the name of the entry to find the value of * \param DefValue    the default value in the event of a failed read * \param Filename    the name of the .ini file to read from * * \return            the value located at Key */long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename){  TCHAR LocalBuffer[64];  int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename);  return (len == 0) ? DefValue                    : ((len >= 2 && _totupper((int)LocalBuffer[1]) == 'X') ? _tcstol(LocalBuffer, NULL, 16)                                                                           : _tcstol(LocalBuffer, NULL, 10));}#if defined INI_REAL/** ini_getf() * \param Section     the name of the section to search for * \param Key         the name of the entry to find the value of * \param DefValue    the default value in the event of a failed read * \param Filename    the name of the .ini file to read from * * \return            the value located at Key */INI_REAL ini_getf(const TCHAR *Section, const TCHAR *Key, INI_REAL DefValue, const TCHAR *Filename){  TCHAR LocalBuffer[64];  int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename);  return (len == 0) ? DefValue : ini_atof(LocalBuffer);}#endif/** ini_getbool() * \param Section     the name of the section to search for * \param Key         the name of the entry to find the value of * \param DefValue    default value in the event of a failed read; it should *                    zero (0) or one (1). * \param Filename    the name and full path of the .ini file to read from * * A true boolean is found if one of the following is matched: * - A string starting with 'y' or 'Y' * - A string starting with 't' or 'T' * - A string starting with '1' * * A false boolean is found if one of the following is matched: * - A string starting with 'n' or 'N' * - A string starting with 'f' or 'F' * - A string starting with '0' * * \return            the true/false flag as interpreted at Key */int ini_getbool(const TCHAR *Section, const TCHAR *Key, int DefValue, const TCHAR *Filename){  TCHAR LocalBuffer[2] = __T("");  int ret;  ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename);  LocalBuffer[0] = (TCHAR)_totupper((int)LocalBuffer[0]);  if (LocalBuffer[0] == 'Y' || LocalBuffer[0] == '1' || LocalBuffer[0] == 'T')    ret = 1;  else if (LocalBuffer[0] == 'N' || LocalBuffer[0] == '0' || LocalBuffer[0] == 'F')    ret = 0;  else    ret = DefValue;  return(ret);}/** ini_getsection() * \param idx         the zero-based sequence number of the section to return * \param Buffer      a pointer to the buffer to copy into * \param BufferSize  the maximum number of characters to copy * \param Filename    the name and full path of the .ini file to read from * * \return            the number of characters copied into the supplied buffer */int  ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename){  INI_FILETYPE fp;  int ok = 0;  if (Buffer == NULL || BufferSize <= 0 || idx < 0)    return 0;  if (ini_openread(Filename, &fp)) {    ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize, NULL);    (void)ini_close(&fp);  }  if (!ok)    *Buffer = '\0';  return (int)_tcslen(Buffer);}/** ini_getkey() * \param Section     the name of the section to browse through, or NULL to *                    browse through the keys outside any section * \param idx         the zero-based sequence number of the key to return * \param Buffer      a pointer to the buffer to copy into * \param BufferSize  the maximum number of characters to copy * \param Filename    the name and full path of the .ini file to read from * * \return            the number of characters copied into the supplied buffer */int  ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename){  INI_FILETYPE fp;  int ok = 0;  if (Buffer == NULL || BufferSize <= 0 || idx < 0)    return 0;  if (ini_openread(Filename, &fp)) {    ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize, NULL);    (void)ini_close(&fp);  }  if (!ok)    *Buffer = '\0';  return (int)_tcslen(Buffer);}/** ini_hassection() * \param Section     the name of the section to search for * \param Filename    the name of the .ini file to read from * * \return            1 if the section is found, 0 if not found */int ini_hassection(const mTCHAR *Section, const mTCHAR *Filename){  TCHAR LocalBuffer[32];  /* dummy buffer */  INI_FILETYPE fp;  int ok = 0;  if (ini_openread(Filename, &fp)) {    ok = getkeystring(&fp, Section, NULL, -1, 0, LocalBuffer, sizearray(LocalBuffer), NULL);    (void)ini_close(&fp);  }  return ok;}/** ini_haskey() * \param Section     the name of the section to search for * \param Key         the name of the entry to find the value of * \param Filename    the name of the .ini file to read from * * \return            1 if the key is found, 0 if not found */int ini_haskey(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Filename){  TCHAR LocalBuffer[32];  /* dummy buffer */  INI_FILETYPE fp;  int ok = 0;  if (ini_openread(Filename, &fp)) {    ok = getkeystring(&fp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), NULL);    (void)ini_close(&fp);  }  return ok;}#if !defined INI_NOBROWSE/** ini_browse() * \param Callback    a pointer to a function that will be called for every *                    setting in the INI file. * \param UserData    arbitrary data, which the function passes on the *                    \c Callback function * \param Filename    the name and full path of the .ini file to read from * * \return            1 on success, 0 on failure (INI file not found) * * \note              The \c Callback function must return 1 to continue *                    browsing through the INI file, or 0 to stop. Even when the *                    callback stops the browsing, this function will return 1 *                    (for success). */int ini_browse(INI_CALLBACK Callback, void *UserData, const TCHAR *Filename){  TCHAR LocalBuffer[INI_BUFFERSIZE];  int lenSec, lenKey;  enum quote_option quotes;  INI_FILETYPE fp;  if (Callback == NULL)    return 0;  if (!ini_openread(Filename, &fp))    return 0;  LocalBuffer[0] = '\0';   /* copy an empty section in the buffer */  lenSec = (int)_tcslen(LocalBuffer) + 1;  for ( ;; ) {    TCHAR *sp, *ep;    if (!ini_read(LocalBuffer + lenSec, INI_BUFFERSIZE - lenSec, &fp))      break;    sp = skipleading(LocalBuffer + lenSec);    /* ignore empty strings and comments */    if (*sp == '\0' || *sp == ';' || *sp == '#')      continue;    /* see whether we reached a new section */    ep = _tcsrchr(sp, ']');    if (*sp == '[' && ep != NULL) {      sp = skipleading(sp + 1);      ep = skiptrailing(ep, sp);      *ep = '\0';      ini_strncpy(LocalBuffer, sp, INI_BUFFERSIZE, QUOTE_NONE);      lenSec = (int)_tcslen(LocalBuffer) + 1;      continue;    }    /* not a new section, test for a key/value pair */    ep = _tcschr(sp, '=');    /* test for the equal sign or colon */    if (ep == NULL)      ep = _tcschr(sp, ':');    if (ep == NULL)      continue;               /* invalid line, ignore */    *ep++ = '\0';             /* split the key from the value */    striptrailing(sp);    ini_strncpy(LocalBuffer + lenSec, sp, INI_BUFFERSIZE - lenSec, QUOTE_NONE);    lenKey = (int)_tcslen(LocalBuffer + lenSec) + 1;    /* clean up the value */    sp = skipleading(ep);    sp = cleanstring(sp, "es);  /* Remove a trailing comment */    ini_strncpy(LocalBuffer + lenSec + lenKey, sp, INI_BUFFERSIZE - lenSec - lenKey, quotes);    /* call the callback */    if (!Callback(LocalBuffer, LocalBuffer + lenSec, LocalBuffer + lenSec + lenKey, UserData))      break;  }  (void)ini_close(&fp);  return 1;}#endif /* INI_NOBROWSE */#if ! defined INI_READONLYstatic void ini_tempname(TCHAR *dest, const TCHAR *source, int maxlength){  TCHAR *p;  ini_strncpy(dest, source, maxlength, QUOTE_NONE);  p = _tcschr(dest, '\0');  assert(p != NULL);  *(p - 1) = '~';}static enum quote_option check_enquote(const TCHAR *Value){  const TCHAR *p;  /* run through the value, if it has trailing spaces, or '"', ';' or '#'   * characters, enquote it   */  assert(Value != NULL);  for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++)    /* nothing */;  return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE;}static void writesection(TCHAR *LocalBuffer, const TCHAR *Section, INI_FILETYPE *fp){  if (Section != NULL && _tcslen(Section) > 0) {    TCHAR *p;    LocalBuffer[0] = '[';    ini_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE);  /* -1 for '[', -1 for ']', -2 for '\r\n' */    p = _tcschr(LocalBuffer, '\0');    assert(p != NULL);    *p++ = ']';    _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */    if (fp != NULL)      (void)ini_write(LocalBuffer, fp);  }}static void writekey(TCHAR *LocalBuffer, const TCHAR *Key, const TCHAR *Value, INI_FILETYPE *fp){  TCHAR *p;  enum quote_option option = check_enquote(Value);  ini_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE);  /* -1 for '=', -2 for '\r\n' */  p = _tcschr(LocalBuffer, '\0');  assert(p != NULL);  *p++ = '=';  ini_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */  p = _tcschr(LocalBuffer, '\0');  assert(p != NULL);  _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */  if (fp != NULL)    (void)ini_write(LocalBuffer, fp);}static int cache_accum(const TCHAR *string, int *size, int max){  int len = (int)_tcslen(string);  if (*size + len >= max)    return 0;  *size += len;  return 1;}static int cache_flush(TCHAR *buffer, int *size,                      INI_FILETYPE *rfp, INI_FILETYPE *wfp, INI_FILEPOS *mark){  int terminator_len = (int)_tcslen(INI_LINETERM);  int pos = 0, pos_prev = -1;  (void)ini_seek(rfp, mark);  assert(buffer != NULL);  buffer[0] = '\0';  assert(size != NULL);  assert(*size <= INI_BUFFERSIZE);  while (pos < *size && pos != pos_prev) {    pos_prev = pos;     /* to guard against zero bytes in the INI file */    (void)ini_read(buffer + pos, INI_BUFFERSIZE - pos, rfp);    while (pos < *size && buffer[pos] != '\0')      pos++;            /* cannot use _tcslen() because buffer may not be zero-terminated */  }  if (buffer[0] != '\0') {    assert(pos > 0 && pos <= INI_BUFFERSIZE);    if (pos == INI_BUFFERSIZE)      pos--;    buffer[pos] = '\0'; /* force zero-termination (may be left unterminated in the above while loop) */    (void)ini_write(buffer, wfp);  }  ini_tell(rfp, mark);  /* update mark */  *size = 0;  /* return whether the buffer ended with a line termination */  return (pos > terminator_len) && (_tcscmp(buffer + pos - terminator_len, INI_LINETERM) == 0);}static int close_rename(INI_FILETYPE *rfp, INI_FILETYPE *wfp, const TCHAR *filename, TCHAR *buffer){  (void)ini_close(rfp);  (void)ini_close(wfp);  (void)ini_tempname(buffer, filename, INI_BUFFERSIZE);  #if defined ini_remove || defined INI_REMOVE    (void)ini_remove(filename);  #endif  (void)ini_rename(buffer, filename);  return 1;}/** ini_puts() * \param Section     the name of the section to write the string in * \param Key         the name of the entry to write, or NULL to erase all keys in the section * \param Value       a pointer to the buffer the string, or NULL to erase the key * \param Filename    the name and full path of the .ini file to write to * * \return            1 if successful, otherwise 0 */int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename){  INI_FILETYPE rfp;  INI_FILETYPE wfp;  INI_FILEPOS mark;  INI_FILEPOS head, tail;  TCHAR *sp, *ep;  TCHAR LocalBuffer[INI_BUFFERSIZE];  int len, match, flag, cachelen;  assert(Filename != NULL);  if (!ini_openread(Filename, &rfp)) {    /* If the .ini file doesn't exist, make a new file */    if (Key != NULL && Value != NULL) {      if (!ini_openwrite(Filename, &wfp))        return 0;      writesection(LocalBuffer, Section, &wfp);      writekey(LocalBuffer, Key, Value, &wfp);      (void)ini_close(&wfp);    }    return 1;  }  /* If parameters Key and Value are valid (so this is not an "erase" request)   * and the setting already exists, there are two short-cuts to avoid rewriting   * the INI file.   */  if (Key != NULL && Value != NULL) {    match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), &head);    if (match) {      /* if the current setting is identical to the one to write, there is       * nothing to do.       */      if (_tcscmp(LocalBuffer,Value) == 0) {        (void)ini_close(&rfp);        return 1;      }      /* if the new setting has the same length as the current setting, and the       * glue file permits file read/write access, we can modify in place.       */      #if defined ini_openrewrite || defined INI_OPENREWRITE        /* we already have the start of the (raw) line, get the end too */        ini_tell(&rfp, &tail);        /* create new buffer (without writing it to file) */        writekey(LocalBuffer, Key, Value, NULL);        if (_tcslen(LocalBuffer) == (size_t)(tail - head)) {          /* length matches, close the file & re-open for read/write, then           * write at the correct position           */          (void)ini_close(&rfp);          if (!ini_openrewrite(Filename, &wfp))            return 0;          (void)ini_seek(&wfp, &head);          (void)ini_write(LocalBuffer, &wfp);          (void)ini_close(&wfp);          return 1;        }      #endif    }    /* key not found, or different value & length -> proceed */  } else if (Key != NULL && Value == NULL) {    /* Conversely, for a request to delete a setting; if that setting isn't       present, just return */    match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), NULL);    if (!match) {      (void)ini_close(&rfp);      return 1;    }    /* key found -> proceed to delete it */  }  /* Get a temporary file name to copy to. Use the existing name, but with   * the last character set to a '~'.   */  (void)ini_close(&rfp);  ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE);  if (!ini_openwrite(LocalBuffer, &wfp))    return 0;  /* In the case of (advisory) file locks, ini_openwrite() may have been blocked   * on the open, and after the block is lifted, the original file may have been   * renamed, which is why the original file was closed and is now reopened */  if (!ini_openread(Filename, &rfp)) {    /* If the .ini file doesn't exist any more, make a new file */    assert(Key != NULL && Value != NULL);    writesection(LocalBuffer, Section, &wfp);    writekey(LocalBuffer, Key, Value, &wfp);    (void)ini_close(&wfp);    return 1;  }  (void)ini_tell(&rfp, &mark);  cachelen = 0;  /* Move through the file one line at a time until a section is   * matched or until EOF. Copy to temp file as it is read.   */  len = (Section != NULL) ? (int)_tcslen(Section) : 0;  if (len > 0) {    do {      if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {        /* Failed to find section, so add one to the end */        flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);        if (Key!=NULL && Value!=NULL) {          if (!flag)            (void)ini_write(INI_LINETERM, &wfp);  /* force a new line behind the last line of the INI file */          writesection(LocalBuffer, Section, &wfp);          writekey(LocalBuffer, Key, Value, &wfp);        }        return close_rename(&rfp, &wfp, Filename, LocalBuffer);  /* clean up and rename */      }      /* Check whether this line is a section */      sp = skipleading(LocalBuffer);      ep = _tcsrchr(sp, ']');      match = (*sp == '[' && ep != NULL);      if (match) {        /* A section was found, skip leading and trailing whitespace */        assert(sp != NULL && *sp == '[');        sp = skipleading(sp + 1);        assert(ep != NULL && *ep == ']');        ep = skiptrailing(ep, sp);        match = ((int)(ep-sp) == len && _tcsnicmp(sp, Section, len) == 0);      }      /* Copy the line from source to dest, but not if this is the section that       * we are looking for and this section must be removed       */      if (!match || Key != NULL) {        if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) {          cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);          (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);          cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE);        }      }    } while (!match);  }  cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);  /* when deleting a section, the section head that was just found has not been   * copied to the output file, but because this line was not "accumulated" in   * the cache, the position in the input file was reset to the point just   * before the section; this must now be skipped (again)   */  if (Key == NULL) {    (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);    (void)ini_tell(&rfp, &mark);  }  /* Now that the section has been found, find the entry. Stop searching   * upon leaving the section's area. Copy the file as it is read   * and create an entry if one is not found.   */  len = (Key != NULL) ? (int)_tcslen(Key) : 0;  for( ;; ) {    if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {      /* EOF without an entry so make one */      flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);      if (Key!=NULL && Value!=NULL) {        if (!flag)          (void)ini_write(INI_LINETERM, &wfp);  /* force a new line behind the last line of the INI file */        writekey(LocalBuffer, Key, Value, &wfp);      }      return close_rename(&rfp, &wfp, Filename, LocalBuffer);  /* clean up and rename */    }    sp = skipleading(LocalBuffer);    ep = _tcschr(sp, '='); /* Parse out the equal sign */    if (ep == NULL)      ep = _tcschr(sp, ':');    match = (ep != NULL && len > 0 && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0);    if ((Key != NULL && match) || *sp == '[')      break;  /* found the key, or found a new section */    /* copy other keys in the section */    if (Key == NULL) {      (void)ini_tell(&rfp, &mark);  /* we are deleting the entire section, so update the read position */    } else {      if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) {        cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);        (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);        cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE);      }    }  }  /* the key was found, or we just dropped on the next section (meaning that it   * wasn't found); in both cases we need to write the key, but in the latter   * case, we also need to write the line starting the new section after writing   * the key   */  flag = (*sp == '[');  cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);  if (Key != NULL && Value != NULL)    writekey(LocalBuffer, Key, Value, &wfp);  /* cache_flush() reset the "read pointer" to the start of the line with the   * previous key or the new section; read it again (because writekey() destroyed   * the buffer)   */  (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);  if (flag) {    /* the new section heading needs to be copied to the output file */    cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE);  } else {    /* forget the old key line */    (void)ini_tell(&rfp, &mark);  }  /* Copy the rest of the INI file */  while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {    if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) {      cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);      (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);      cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE);    }  }  cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);  return close_rename(&rfp, &wfp, Filename, LocalBuffer);  /* clean up and rename */}/* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */#define ABS(v)  ((v) < 0 ? -(v) : (v))static void strreverse(TCHAR *str){  int i, j;  for (i = 0, j = (int)_tcslen(str) - 1; i < j; i++, j--) {    TCHAR t = str[i];    str[i] = str[j];    str[j] = t;  }}static void long2str(long value, TCHAR *str){  int i = 0;  long sign = value;  /* generate digits in reverse order */  do {    int n = (int)(value % 10);          /* get next lowest digit */    str[i++] = (TCHAR)(ABS(n) + '0');   /* handle case of negative digit */  } while (value /= 10);                /* delete the lowest digit */  if (sign < 0)    str[i++] = '-';  str[i] = '\0';  strreverse(str);}/** ini_putl() * \param Section     the name of the section to write the value in * \param Key         the name of the entry to write * \param Value       the value to write * \param Filename    the name and full path of the .ini file to write to * * \return            1 if successful, otherwise 0 */int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename){  TCHAR LocalBuffer[32];  long2str(Value, LocalBuffer);  return ini_puts(Section, Key, LocalBuffer, Filename);}#if defined INI_REAL/** ini_putf() * \param Section     the name of the section to write the value in * \param Key         the name of the entry to write * \param Value       the value to write * \param Filename    the name and full path of the .ini file to write to * * \return            1 if successful, otherwise 0 */int ini_putf(const TCHAR *Section, const TCHAR *Key, INI_REAL Value, const TCHAR *Filename){  TCHAR LocalBuffer[64];  ini_ftoa(LocalBuffer, Value);  return ini_puts(Section, Key, LocalBuffer, Filename);}#endif /* INI_REAL */#endif /* !INI_READONLY */
 |