?? appconf.cpp
字號:
#include "Core/precomp.h"
/*****************************************************************************\
* Project: CppLib: C++ library for Windows/UNIX platfroms *
* File: config.cpp - implementation of Config class *
*---------------------------------------------------------------------------*
* Language: C++ *
* Platfrom: any (tested under Windows NT) *
*---------------------------------------------------------------------------*
* (c) Karsten Ball黡er & Vadim Zeitlin *
* Ballueder@usa.net Vadim.zeitlin@dptmaths.ens-cachan.fr *
*---------------------------------------------------------------------------*
* Classes: *
* Config - manages configuration files or registry database *
*---------------------------------------------------------------------------*
* History: *
* 25.10.97 adapted from wxConfig by Karsten Ball黡er *
* 09.11.97 corrected bug in RegistryConfig::enumSubgroups *
* --- for further changes see appconf.h or the CVS log --- *
\*****************************************************************************/
/**********************************************************************\
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; if not, write to the Free *
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
* *
\**********************************************************************/
//static const char
//*cvs_id = "$Id: appconf.cpp,v 1.8 2003/09/05 20:33:06 mbn Exp $";
// MacOSX mostly behaves like unix
#ifdef __APPLE__
# define __unix__
#endif
// ============================================================================
// headers, constants, private declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// standard headers
#ifdef __WIN32__
# ifdef _MSC_VER
// nonstandard extension used : nameless struct/union
# pragma warning(disable: 4201)
# endif // VC++
# include <windows.h>
#endif // WIN32
#ifdef __unix__
# include <sys/param.h>
# include <sys/stat.h>
# include <unistd.h>
# define MAX_PATH MAXPATHLEN
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <cassert>
// our headers
#include "appconf.h"
// ----------------------------------------------------------------------------
// some debug/error reporting functions
// ----------------------------------------------------------------------------
#if APPCONF_USE_GETTEXT
# include <libintl.h>
# define _(x) dgettext(APPCONF_DOMAIN,x)
#else
# define _(x) (x)
#endif
//using namespace std;
#ifndef __WXWIN__
// in general, these messages could be treated all differently
// define a standard log function, to be called like printf()
#ifndef LogInfo
# define LogInfo LogError
#endif
// define a standard error log function, to be called like printf()
#ifndef LogWarning
# define LogWarning LogError
#endif
// logs an error message (with a name like that it's really strange)
// message length is limited to 1Kb
void LogError(const char *pszFormat, ...)
{
char szBuf[1025];
va_list argptr;
va_start(argptr, pszFormat);
vsnprintf(szBuf, 1023, pszFormat, argptr);
szBuf[1023] = 0;
va_end(argptr);
strncat(szBuf, "\n", 1);
fputs(szBuf, stderr);
}
#endif
#ifdef __WIN32__
const char *SysError()
{
static char s_szBuf[1024];
// get error message from system
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
// copy it to our buffer and free memory
strncpy(s_szBuf, (const char *)lpMsgBuf, sizeof(s_szBuf)/sizeof(char));
LocalFree(lpMsgBuf);
// returned string is capitalized and ended with '\n' - no good
s_szBuf[0] = (char)tolower(s_szBuf[0]);
size_t len = strlen(s_szBuf);
if ( (len > 0) && (s_szBuf[len - 1] == '\n') )
s_szBuf[len - 1] = '\0';
return s_szBuf;
}
#endif
// ----------------------------------------------------------------------------
// global functions
// ----------------------------------------------------------------------------
inline size_t Strlen(const char *pc) { return pc == NULL ? 0 : strlen(pc); }
inline Bool IsValid(char c) { return isalnum(c) || strchr("_/-!.*%", c); }
inline Bool IsCSym (char c) { return isalnum(c) || ( c == '_'); }
inline size_t Min(size_t n1, size_t n2) { return n1 < n2 ? n1 : n2; }
#define SIZE(array) (sizeof(array)/sizeof(array[0]))
#if APPCONF_CASE_SENSITIVE
# define StrCmp(s1,s2) strcmp((s1),(s2))
#else
# ifdef __unix__
//strcasecmp is in string.h
# define StrCmp(s1,s2) strcasecmp((s1),(s2))
# else
# ifdef _MSC_VER
# define StrCmp(s1,s2) _stricmp((s1),(s2))
# else
# error "Please define 'stricmp' function for your compiler."
# endif // compilers
# endif // strcasecmp/strcimp
#endif
// perform environment variable substitution
char *ExpandEnvVars(const char *psz)
{
char *szNewValue = new char[strlen(psz)+1];
strcpy(szNewValue, psz);
return szNewValue;
// produces internal comp error on rh 5.2 -- mbn
/*
// don't change the values the enum elements: they must be equal
// to the matching [closing] delimiter.
enum Bracket
{
Bracket_None,
Bracket_Normal = ')',
Bracket_Curly = '}'
};
// max size for an environment variable name is fixed
char szVarName[256];
// first calculate the length of the resulting string
size_t nNewLen = 0;
const char *pcIn;
for ( pcIn = psz; *pcIn != '\0'; pcIn++ ) {
switch ( *pcIn ) {
case '$':
{
Bracket bracket;
switch ( *++pcIn ) {
case '(':
bracket = Bracket_Normal;
pcIn++; // skip the bracket
break;
case '{':
bracket = Bracket_Curly;
pcIn++; // skip the bracket
break;
default:
bracket = Bracket_None;
}
const char *pcStart = pcIn;
while ( IsCSym(*pcIn) ) pcIn++;
size_t nCopy = Min(pcIn - pcStart, SIZE(szVarName));
strncpy(szVarName, pcStart, nCopy);
szVarName[nCopy] = '\0';
if ( bracket != Bracket_None ) {
if ( *pcIn != (char)bracket ) {
// # what to do? we decide to give warning and ignore
// the opening bracket
LogWarning(_("'%c' expected in '%s' after '${%s'"),
(char)bracket, psz, szVarName);
pcIn--;
}
}
else {
// everything is ok but we took one extra character
pcIn--;
}
// Strlen() acceps NULL as well
nNewLen += Strlen(getenv(szVarName));
}
break;
case '\\':
pcIn++;
// fall through
default:
nNewLen++;
}
}
// # we always realloc buffer (could reuse the old one if nNewLen < nOldLen)
char *szNewValue = new char[nNewLen + 1];
char *pcOut = szNewValue;
// now copy it to the new location replacing the variables with their values
for ( pcIn = psz; *pcIn != '\0'; pcIn++ ) {
switch ( *pcIn ) {
case '$':
{
Bracket bracket;
switch ( *++pcIn ) {
case '(':
bracket = Bracket_Normal;
pcIn++; // skip the bracket
break;
case '{':
bracket = Bracket_Curly;
pcIn++; // skip the bracket
break;
default:
bracket = Bracket_None;
}
const char *pcStart = pcIn;
while ( IsCSym(*pcIn) ) pcIn++;
size_t nCopy = Min(pcIn - pcStart, SIZE(szVarName));
strncpy(szVarName, pcStart, nCopy);
szVarName[nCopy] = '\0';
if ( bracket != Bracket_None ) {
if ( *pcIn != (char)bracket ) {
// warning message already given, just ignore opening bracket
pcIn--;
}
}
else {
// everything is ok but we took one extra character
pcIn--;
}
const char *pszValue = getenv(szVarName);
if ( pszValue != NULL ) {
strcpy(pcOut, pszValue);
pcOut += strlen(pszValue);
}
}
break;
case '\\':
pcIn++;
// fall through
default:
*pcOut++ = *pcIn;
}
}
*pcOut = '\0';
return szNewValue;
*/
}
// ============================================================================
// implementation of the class BaseConfig
// ============================================================================
// ----------------------------------------------------------------------------
// ctor and dtor
// ----------------------------------------------------------------------------
BaseConfig::BaseConfig()
{
m_szCurrentPath = NULL;
m_bRecordDefaults = FALSE;
}
BaseConfig::~BaseConfig()
{
if ( m_szCurrentPath != NULL )
delete [] m_szCurrentPath;
}
void
BaseConfig::recordDefaults(Bool enable)
{
m_bRecordDefaults = enable;
}
// ----------------------------------------------------------------------------
// handle long int and double values
// ----------------------------------------------------------------------------
Bool
BaseConfig::writeEntry(const char *szKey, long int Value)
{
char buffer[APPCONF_STRBUFLEN]; // ugly
sprintf(buffer, "%ld", Value);
return writeEntry(szKey,buffer);
}
Bool
BaseConfig::writeEntry(const char *szKey, double Value)
{
char buffer[APPCONF_STRBUFLEN]; // ugly
sprintf(buffer,"%g", Value);
return writeEntry(szKey,buffer);
}
long int
BaseConfig::readEntry(const char *szKey, long int Default) const
{
const char *cptr = readEntry(szKey,(const char *)NULL);
if(cptr)
return atol(cptr);
else
{
if(m_bRecordDefaults)
((BaseConfig *)this)->writeEntry(szKey,Default);
return Default;
}
}
double
BaseConfig::readEntry(const char *szKey, double Default) const
{
const char *cptr = readEntry(szKey,(const char *)NULL);
if(cptr)
return atof(cptr);
else
{
if(m_bRecordDefaults)
((BaseConfig *)this)->writeEntry(szKey,Default);
return Default;
}
}
// ----------------------------------------------------------------------------
// set/get current path
// ----------------------------------------------------------------------------
// this function resolves all ".." (but not '/'!) in the path
// returns pointer to dynamically allocated buffer, free with "delete []"
// ## code here is inefficient and difficult to understand, to rewrite
char *BaseConfig::normalizePath(const char *szStartPath, const char *szPath)
{
char *szNormPath;
// array grows in chunks of this size
#define COMPONENTS_INITIAL (10)
char **aszPathComponents; // component is something between 2 '/'
size_t nComponents = 0,
nMaxComponents;
aszPathComponents = new char *[nMaxComponents = COMPONENTS_INITIAL];
const char *pcStart; // start of last component
const char *pcIn;
// concatenate the two adding APPCONF_PATH_SEPARATOR to the end if not there
size_t len = Strlen(szStartPath);
size_t nOldLen = len + Strlen(szPath) + 1;
szNormPath = new char[nOldLen + 1];
strcpy(szNormPath, szStartPath);
szNormPath[len++] = APPCONF_PATH_SEPARATOR;
szNormPath[len] = '\0';
strcat(szNormPath, szPath);
// break combined path in components
Bool bEnd = FALSE;
for ( pcStart = pcIn = szNormPath; !bEnd; pcIn++ ) {
if ( *pcIn == APPCONF_PATH_SEPARATOR || *pcIn == '\0' ) {
if ( *pcIn == '\0' )
bEnd = TRUE;
// another component - is it "." or ".."?
if ( *pcStart == '.' ) {
if ( pcIn == pcStart + 1 ) {
// "./" - ignore
pcStart = pcIn + 1;
continue;
}
else if ( (pcIn == pcStart + 2) && (*(pcStart + 1) == '.') ) {
// "../" found - delete last component
if ( nComponents > 0 ) {
delete [] aszPathComponents[--nComponents];
}
else {
LogWarning(_("extra '..' in the path '%s'."), szPath);
}
pcStart = pcIn + 1;
continue;
}
}
else if ( pcIn == pcStart ) {
pcStart = pcIn + 1;
continue;
}
// normal component, add to the list
// grow array?
if ( nComponents == nMaxComponents ) {
// realloc array
char **aszOld = aszPathComponents;
nMaxComponents += COMPONENTS_INITIAL;
aszPathComponents = new char *[nMaxComponents];
// move data
memmove(aszPathComponents, aszOld,
sizeof(aszPathComponents[0]) * nComponents);
// free old
delete [] aszOld;
}
// do add
aszPathComponents[nComponents] = new char[pcIn - pcStart + 1];
strncpy(aszPathComponents[nComponents], pcStart, pcIn - pcStart);
aszPathComponents[nComponents][pcIn - pcStart] = '\0';
nComponents++;
pcStart = pcIn + 1;
}
}
if ( nComponents == 0 ) {
// special case
szNormPath[0] = '\0';
}
else {
// put all components together
len = 0;
for ( size_t n = 0; n < nComponents; n++ ) {
// add '/' before each new component except the first one
if ( len != 0 ) {
szNormPath[len++] = APPCONF_PATH_SEPARATOR;
}
szNormPath[len] = '\0';
// concatenate
strcat(szNormPath, aszPathComponents[n]);
// update length
len += strlen(aszPathComponents[n]);
// and free memory
delete [] aszPathComponents[n];
}
}
delete [] aszPathComponents;
return szNormPath;
}
void BaseConfig::changeCurrentPath(const char *szPath)
{
// special case (default value)
if ( Strlen(szPath) == 0 ) {
if ( m_szCurrentPath != NULL ) {
delete [] m_szCurrentPath;
m_szCurrentPath = NULL;
}
}
else {
char *szNormPath;
// if absolute path, start from top, otherwise from current
if ( *szPath == APPCONF_PATH_SEPARATOR )
szNormPath = normalizePath("", szPath + 1);
else
szNormPath = normalizePath(m_szCurrentPath ? m_szCurrentPath : "", szPath);
size_t len = Strlen(szNormPath);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -