#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined MININI_ANSI | |
# if !defined UNICODE /* for Windows */ | |
# define UNICODE | |
# endif | |
# if !defined _UNICODE /* for C library */ | |
# define _UNICODE | |
# endif | |
#endif | |
#include "ini.h" | |
#include <cwmp/log.h> | |
#include "errno.h" | |
#if defined NDEBUG | |
#define assert(e) | |
#else | |
//#include <assert.h> | |
#define assert(e) | |
#endif | |
#if !defined __T | |
#include <string.h> | |
#include <stdlib.h> | |
/* definition of tchar_t already in minIni.h */ | |
#define __T(s) s | |
#define _tcscmp strcmp | |
#define _tcsicmp stricmp | |
#define _tcsnicmp strncmp | |
#define _tcscat strcat | |
#define _tcscpy strcpy | |
#define _tcsncpy strncpy | |
#define _tcschr strchr | |
#define _tcsrchr strrchr | |
#define _tcslen strlen | |
#define _tcstol strtol | |
#define _tremove remove | |
#define _trename rename | |
#define _tfopen fopen | |
#define _tfgets fgets | |
#define _tfputs fputs | |
#endif | |
#if !defined INI_LINETERM | |
#define INI_LINETERM __T("\n") | |
#endif | |
#if !defined INI_FILETYPE | |
#define INI_FILETYPE FILE* | |
#endif | |
#if defined __linux || defined __linux__ | |
#define __LINUX__ | |
#endif | |
#if defined FREEBSD && !defined __FreeBSD__ | |
#define __FreeBSD__ | |
#endif | |
#if !defined strnicmp | |
#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ | |
#define strnicmp strncasecmp | |
#endif | |
#endif | |
#if !defined sizearray | |
#define sizearray(a) (sizeof(a) / sizeof((a)[0])) | |
#endif | |
static void ini_string_reverse(tchar_t *str); | |
#define INI_BUFFERSIZE 128 | |
static tchar_t *ini_skip_trailing(const tchar_t *str, const tchar_t *base) | |
{ | |
assert(str != NULL); | |
assert(base != NULL); | |
while (str > base && *(str-1) <= ' ') | |
str--; | |
return (tchar_t *)str; | |
} | |
static tchar_t *ini_skip_leading(const tchar_t *str) | |
{ | |
assert(str != NULL); | |
while (*str != '\0' && *str <= ' ') | |
str++; | |
return (tchar_t *)str; | |
} | |
static tchar_t *ini_save_strncpy(tchar_t *dest, const tchar_t *source, size_t maxlen) | |
{ | |
if(NULL == dest || NULL == source || maxlen <= 0) | |
{ | |
return dest; | |
} | |
assert(maxlen>0); | |
_tcsncpy(dest,source,maxlen); | |
dest[maxlen-1]='\0'; | |
return dest; | |
} | |
static tchar_t *ini_strip_trailing(tchar_t *str) | |
{ | |
tchar_t *ptr = ini_skip_trailing(_tcschr(str, '\0'), str); | |
assert(ptr != NULL); | |
*ptr='\0'; | |
return str; | |
} | |
static int ini_get_keystring(INI_FILETYPE *fp, const tchar_t *section, const tchar_t *key, | |
int section_index, int idxKey, tchar_t *buffer, int buffer_size) | |
{ | |
tchar_t *sp, *ep; | |
int len, idx; | |
tchar_t local_buffer[INI_BUFFERSIZE]; | |
assert(fp != NULL); | |
len = (section != NULL) ? _tcslen(section) : 0; | |
if (len > 0 || section_index >= 0) | |
{ | |
idx = -1; | |
do | |
{ | |
if (!ini_read(local_buffer, INI_BUFFERSIZE, fp)) | |
{ | |
return 0; | |
} | |
// cwmp_log_info("idx:%d, section_index:%d, local:%s", idx, section_index, local_buffer); | |
sp = ini_skip_leading(local_buffer); | |
ep = _tcschr(sp, ']'); | |
}while (*sp != '[' || ep == NULL || (((int)(ep-sp-1) != len || (section != NULL && _tcsnicmp(sp+1,section,len) != 0)) && ++idx != section_index)); | |
if (section_index >= 0) | |
{ | |
if (idx == section_index) | |
{ | |
assert(ep != NULL); | |
assert(*ep == ']'); | |
*ep = '\0'; | |
ini_save_strncpy(buffer, sp + 1, buffer_size); | |
cwmp_log_info("section_index:%d, buffer:%s", section_index, buffer); | |
return 1; | |
} /* if */ | |
return 0; /* no more section found */ | |
} /* if */ | |
} /* if */ | |
assert(key != NULL || idxKey >= 0); | |
len = (key != NULL) ? (int)_tcslen(key) : 0; | |
idx = -1; | |
do | |
{ | |
if (!ini_read(local_buffer,INI_BUFFERSIZE,fp) || *(sp = ini_skip_leading(local_buffer)) == '[') | |
return 0; | |
sp = ini_skip_leading(local_buffer); | |
ep = _tcschr(sp, '='); /* Parse out the equal sign */ | |
if (ep == NULL) | |
ep = _tcschr(sp, ':'); | |
}while (*sp == ';' || *sp == '#' || ep == NULL || (((int)(ini_skip_trailing(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'; | |
ini_strip_trailing(sp); | |
ini_save_strncpy(buffer, sp, buffer_size); | |
cwmp_log_info("buffer:%s", buffer); | |
return 1; | |
} /* if */ | |
return 0; /* no more key found (in this section) */ | |
} /* if */ | |
/* Copy up to buffer_size chars to buffer */ | |
// assert(ep != NULL); | |
// assert(*ep == '=' || *ep == ':'); | |
sp = ini_skip_leading(ep + 1); | |
ini_strip_trailing(sp); | |
/* Remove double quotes surrounding a value */ | |
ep = _tcschr(sp, '\0'); | |
if (*sp == '"' /*&& ep != NULL*/ && (ep - 1) != NULL && *(ep - 1) == '"') | |
{ | |
sp++; | |
*--ep = '\0'; | |
} /* if */ | |
ini_save_strncpy(buffer, sp, buffer_size); | |
return 1; | |
} | |
long ini_getl(const tchar_t *section, const tchar_t *key, long default_value, const tchar_t *filename) | |
{ | |
tchar_t buff[64]; | |
int len = ini_gets(section, key, __T(""), buff, sizearray(buff), filename); | |
if(len == 0) | |
{ | |
return default_value; | |
} | |
else | |
{ | |
errno = 0; | |
long ret = _tcstol(buff,NULL,10); | |
if (errno == ERANGE)// kw ERRNO.NOT_CHECKED | |
{ | |
printf("strtol errno %d: %s\n", errno, strerror(errno)); | |
} | |
return ret; | |
} | |
//return (len == 0) ? default_value : _tcstol(buff,NULL,10); | |
} | |
int ini_gets(const tchar_t *section, const tchar_t *key, const tchar_t *default_value, | |
tchar_t *buffer, int buffer_size, const tchar_t *filename) | |
{ | |
INI_FILETYPE fp; | |
int ok = 0; | |
if (buffer == NULL || buffer_size <= 0 || key == NULL) | |
{ | |
return 0; | |
} | |
if (ini_openread(filename, &fp)) | |
{ | |
ok = ini_get_keystring(&fp, section, key, -1, -1, buffer, buffer_size); | |
ini_close(&fp); | |
} /* if */ | |
if (!ok) | |
{ | |
if(default_value == NULL) | |
{ | |
buffer[0] = 0; | |
} | |
else | |
{ | |
ini_save_strncpy(buffer, default_value, buffer_size); | |
} | |
} | |
return _tcslen(buffer); | |
} | |
int ini_get_key(const tchar_t *section, int idx, tchar_t *buffer, int buffer_size, const tchar_t *filename) | |
{ | |
INI_FILETYPE fp; | |
int ok = 0; | |
if (buffer == NULL || buffer_size <= 0 || idx < 0) | |
{ | |
return 0; | |
} | |
if (ini_openread(filename, &fp)) | |
{ | |
ok = ini_get_keystring(&fp, section, NULL, -1, idx, buffer, buffer_size); | |
ini_close(&fp); | |
} /* if */ | |
if (!ok) | |
*buffer = '\0'; | |
return _tcslen(buffer); | |
} | |
int ini_get_section(int idx, tchar_t *buffer, int buffer_size, const tchar_t *filename) | |
{ | |
INI_FILETYPE fp; | |
int ok = 0; | |
if (buffer == NULL || buffer_size <= 0 || idx < 0) | |
return 0; | |
if (ini_openread(filename, &fp)) | |
{ | |
ok = ini_get_keystring(&fp, NULL, NULL, idx, -1, buffer, buffer_size); | |
ini_close(&fp); | |
} /* if */ | |
if (!ok) | |
*buffer = '\0'; | |
return _tcslen(buffer); | |
} | |
#if ! defined INI_READONLY | |
static void ini_tempname(tchar_t *dest, const tchar_t *source, int maxlength) | |
{ | |
tchar_t *p; | |
ini_save_strncpy(dest, source, maxlength); | |
#if 0 | |
p = _tcsrchr(dest, '\0'); | |
#else | |
p = dest + _tcslen(dest); | |
#endif | |
assert(p != NULL); | |
*(p - 1) = '~'; | |
} | |
static void ini_write_section(tchar_t *local_buffer, const tchar_t *section, INI_FILETYPE *fp) | |
{ | |
tchar_t *p; | |
if (section != NULL && _tcslen(section) > 0) | |
{ | |
local_buffer[0] = '['; | |
ini_save_strncpy(local_buffer + 1, section, INI_BUFFERSIZE - 4); /* -1 for '[', -1 for ']', -2 for '\r\n' */ | |
#if 0 | |
p = _tcsrchr(local_buffer, '\0'); | |
#else | |
p = local_buffer + _tcslen(local_buffer); | |
#endif | |
assert(p != NULL); | |
*p++ = ']'; | |
_tcsncpy(p, INI_LINETERM, INI_BUFFERSIZE - _tcslen(local_buffer) - 1); /* copy line terminator (typically "\n") */ | |
ini_write(local_buffer, fp); | |
} /* if */ | |
} | |
static void ini_write_key(tchar_t *local_buffer, const tchar_t *key, const tchar_t *value, INI_FILETYPE *fp) | |
{ | |
tchar_t *p; | |
ini_save_strncpy(local_buffer, key, INI_BUFFERSIZE - 3); /* -1 for '=', -2 for '\r\n' */ | |
#if 0 | |
p = _tcsrchr(local_buffer, '\0'); | |
#else | |
p = local_buffer + _tcslen(local_buffer); | |
#endif | |
assert(p != NULL); | |
*p++ = '='; | |
ini_save_strncpy(p, value, INI_BUFFERSIZE - (p - local_buffer) - 2); /* -2 for '\r\n' */ | |
#if 0 | |
p = _tcsrchr(local_buffer, '\0'); | |
#else | |
p = local_buffer + _tcslen(local_buffer); | |
#endif | |
assert(p != NULL); | |
_tcsncpy(p, INI_LINETERM, INI_BUFFERSIZE - _tcslen(local_buffer) - 1); /* copy line terminator (typically "\n") */ | |
ini_write(local_buffer, fp); | |
} | |
/** 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_t *section, const tchar_t *key, const tchar_t *value, const tchar_t *filename) | |
{ | |
INI_FILETYPE rfp; | |
INI_FILETYPE wfp; | |
tchar_t *sp, *ep; | |
tchar_t local_buffer[INI_BUFFERSIZE+1] = {0}; | |
int len, match, count; | |
tchar_t temp_name[INI_BUFFERSIZE+1] = {0}; | |
if(NULL == section || NULL == filename || NULL == key ) | |
{ | |
return 0; | |
} | |
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; | |
} | |
ini_write_section(local_buffer, section, &wfp); | |
ini_write_key(local_buffer, key, value, &wfp); | |
ini_sysc(&wfp); | |
ini_close(&wfp); | |
} /* if */ | |
return 1; | |
} /* if */ | |
/* If parameters key and value are valid (so this is not an "erase" request) | |
* and the setting already exists and it already has the correct value, do | |
* nothing. This early bail-out avoids rewriting the INI file for no reason. | |
*/ | |
if (/*key!=NULL && */value!=NULL) | |
{ | |
match = ini_get_keystring(&rfp, section, key, -1, -1, local_buffer, sizearray(local_buffer)); | |
if (match && _tcscmp(local_buffer,value)==0) | |
{ | |
ini_close(&rfp); | |
return 1; | |
} /* if */ | |
/* key not found, or different value -> proceed (but rewind the input file first) */ | |
ini_rewind(&rfp); | |
} /* if */ | |
/* Get a temporary file name to copy to. Use the existing name, but with | |
* the last character set to a '~'. | |
*/ | |
ini_tempname(temp_name, filename, INI_BUFFERSIZE); | |
if (!ini_openwrite(temp_name, &wfp)) | |
{ | |
ini_close(&rfp); | |
return 0; | |
} /* if */ | |
/* 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. | |
*/ | |
count = 0; | |
//len = (section != NULL) ? _tcslen(section) : 0; | |
len = _tcslen(section); | |
if (len > 0) | |
{ | |
do | |
{ | |
if (!ini_read(local_buffer, INI_BUFFERSIZE, &rfp)) | |
{ | |
/* Failed to find section, so add one to the end */ | |
// if (key!=NULL && value!=NULL) | |
{ | |
ini_write(INI_LINETERM, &wfp); /* force a new line (there may not have been one) behind the last line of the INI file */ | |
ini_write_section(local_buffer, section, &wfp); | |
ini_write_key(local_buffer, key, value, &wfp); | |
} /* if */ | |
/* Clean up and rename */ | |
ini_close(&rfp); | |
ini_sysc(&wfp); | |
ini_close(&wfp); | |
if(ini_remove(filename) <0) | |
{ | |
} | |
ini_tempname(temp_name, filename, INI_BUFFERSIZE); | |
if(ini_rename(temp_name, filename) < 0) | |
{ | |
} | |
return 1; | |
} /* if */ | |
/* 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 | |
*/ | |
sp = ini_skip_leading(local_buffer); | |
ep = _tcschr(sp, ']'); | |
match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,section,len) == 0); | |
// if (!match || key!=NULL) | |
{ | |
/* Remove blank lines, but insert a blank line (possibly one that was | |
* removed on the previous iteration) before a new section. This creates | |
* "neat" INI files. | |
*/ | |
if (_tcslen(sp) > 0) | |
{ | |
if (*sp == '[' && count > 0) | |
ini_write(INI_LINETERM, &wfp); | |
ini_write(sp, &wfp); | |
count++; | |
} /* if */ | |
} /* if */ | |
} | |
while (!match); | |
} /* if */ | |
/* 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) ? _tcslen(key) : 0; | |
len = _tcslen(key); | |
for ( ;; ) | |
{ | |
if (!ini_read(local_buffer, INI_BUFFERSIZE, &rfp)) | |
{ | |
/* EOF without an entry so make one */ | |
// if (key!=NULL && value!=NULL) | |
{ | |
ini_write(INI_LINETERM, &wfp); /* force a new line (there may not have been one) behind the last line of the INI file */ | |
ini_write_key(local_buffer, key, value, &wfp); | |
} /* if */ | |
/* Clean up and rename */ | |
ini_close(&rfp); | |
ini_sysc(&wfp); | |
ini_close(&wfp); | |
if(ini_remove(filename) < 0) | |
{ | |
} | |
ini_tempname(temp_name, filename, INI_BUFFERSIZE); | |
if(ini_rename(temp_name, filename) < 0) | |
{ | |
} | |
return 1; | |
} /* if */ | |
sp = ini_skip_leading(local_buffer); | |
ep = _tcschr(sp, '='); /* Parse out the equal sign */ | |
if (ep == NULL) | |
ep = _tcschr(sp, ':'); | |
match = (ep != NULL && (int)(ep-sp) == len && _tcsnicmp(sp,key,len) == 0); | |
if ((/*key!=NULL && */match) || *sp == '[') | |
break; /* found the key, or found a new section */ | |
/* in the section that we re-write, do not copy empty lines */ | |
if (/*key!=NULL && */_tcslen(sp) > 0) | |
ini_write(sp, &wfp); | |
} /* for */ | |
if (*sp == '[') | |
{ | |
/* found start of new section, the key was not in the specified | |
* section, so we add it just before the new section | |
*/ | |
// if (key!=NULL && value!=NULL) | |
{ | |
/* We cannot use "ini_write_key()" here, because we need to preserve the | |
* contents of local_buffer. | |
*/ | |
ini_write(key, &wfp); | |
ini_write("=", &wfp); | |
if(value!=NULL) | |
{ | |
ini_write(value, &wfp); | |
} | |
ini_write(INI_LINETERM INI_LINETERM, &wfp); /* put a blank line between the current and the next section */ | |
} /* if */ | |
/* write the new section header that we read previously */ | |
ini_write(sp, &wfp); | |
} | |
else | |
{ | |
/* We found the key; ignore the line just read (with the key and | |
* the current value) and write the key with the new value. | |
*/ | |
// if (key!=NULL && value!=NULL) | |
ini_write_key(local_buffer, key, value, &wfp); | |
} /* if */ | |
/* Copy the rest of the INI file (removing empty lines, except before a section) */ | |
while (ini_read(local_buffer, INI_BUFFERSIZE, &rfp)) | |
{ | |
sp = ini_skip_leading(local_buffer); | |
if (_tcslen(sp) > 0) | |
{ | |
if (*sp == '[') | |
ini_write(INI_LINETERM, &wfp); | |
ini_write(sp, &wfp); | |
} /* if */ | |
} /* while */ | |
/* Clean up and rename */ | |
ini_close(&rfp); | |
ini_sysc(&wfp); | |
ini_close(&wfp); | |
if(ini_remove(filename) < 0) | |
{ | |
} | |
ini_tempname(temp_name, filename, INI_BUFFERSIZE); | |
if(ini_rename(temp_name, filename) < 0) | |
{ | |
} | |
return 1; | |
} | |
#define ABS(v) ((v) < 0 ? -(v) : (v)) | |
static void ini_long2str(long value, tchar_t *str) | |
{ | |
int i = 0; | |
long sign = value; | |
int n; | |
/* generate digits in reverse order */ | |
do | |
{ | |
n = (int)(value % 10); /* get next lowest digit */ | |
str[i++] = (tchar_t)(ABS(n) + '0'); /* handle case of negative digit */ | |
} | |
while (value /= 10); /* delete the lowest digit */ | |
if (sign < 0) | |
str[i++] = '-'; | |
str[i] = '\0'; | |
ini_string_reverse(str); | |
} | |
static void ini_string_reverse(tchar_t *str) | |
{ | |
tchar_t t; | |
int i, j; | |
for (i = 0, j = _tcslen(str) - 1; i < j; i++, j--) | |
{ | |
t = str[i]; | |
str[i] = str[j]; | |
str[j] = t; | |
} /* for */ | |
} | |
int ini_putl(const tchar_t *section, const tchar_t *key, long value, const tchar_t *filename) | |
{ | |
tchar_t str[32]; | |
ini_long2str(value, str); | |
return ini_puts(section, key, str, filename); | |
} | |
#endif /* !INI_READONLY */ | |
#if defined PORTABLE_STRNICMP | |
int strnicmp(const tchar_t *s1, const tchar_t *s2, size_t n) | |
{ | |
register unsigned tchar_t c1, c2; | |
while (n-- != 0 && (*s1 || *s2)) | |
{ | |
c1 = *(const unsigned tchar_t *)s1++; | |
if ('a' <= c1 && c1 <= 'z') | |
c1 += ('A' - 'a'); | |
c2 = *(const unsigned tchar_t *)s2++; | |
if ('a' <= c2 && c2 <= 'z') | |
c2 += ('A' - 'a'); | |
if (c1 != c2) | |
return c1 - c2; | |
} /* while */ | |
return 0; | |
} | |
#endif /* PORTABLE_STRNICMP */ | |