⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 appconf.cpp

📁 这是一款2d游戏引擎
💻 CPP
📖 第 1 页 / 共 4 页
字号:
}

// other
// -----

// get absolute name
char *FileConfig::ConfigGroup::FullName() const
{
  char *szFullName;
  if ( m_pParent == NULL ) {
    // root group has no name
    return NULL;
  }
  else {
    char *pParentFullName = m_pParent->FullName();
    if ( pParentFullName == NULL ) {
      szFullName = new char[Strlen(m_szName) + 1];
      strcpy(szFullName, m_szName);
    }
    else {
      size_t len = Strlen(pParentFullName);
      szFullName = new char[len + Strlen(m_szName) + 2];
      strcpy(szFullName, pParentFullName);
      szFullName[len] = APPCONF_PATH_SEPARATOR;   // unfortunately strcat doesn't
      szFullName[len + 1] = '\0';                 // take 'char' argument
      strcat(szFullName, m_szName);
      delete [] pParentFullName;
    }
  }

  return szFullName;
}

// set dirty flag and propagate it as needed
// NB: when we set dirty, propagate to parent; when clear - to children
void FileConfig::ConfigGroup::SetDirty(Bool bDirty)
{
  m_bDirty = bDirty;
  if ( bDirty ) {
    if ( m_pParent != NULL )
      m_pParent->SetDirty();
  }
  else {
    ConfigEntry *pEntry;
    for ( pEntry = Entries(); pEntry != NULL; pEntry = pEntry->Next() )
      pEntry->SetDirty(FALSE);

    ConfigGroup *pGroup;
    for ( pGroup = Subgroup(); pGroup != NULL; pGroup = pGroup->Next() )
      pGroup->SetDirty(FALSE);
  }
}

// write changed data
Bool FileConfig::ConfigGroup::flush(std::ostream *ostr)
{
  // write all entries that were changed in this group
  Bool bFirstDirty = TRUE;
  ConfigEntry *pEntry;
  for ( pEntry = Entries(); pEntry != NULL; pEntry = pEntry->Next() ) {
    if ( pEntry->IsDirty() && pEntry->Value()) {
      if ( bFirstDirty ) {
        // output preceding comment for this group if any
        if ( Comment() != NULL ) {
          *ostr << Comment();
        }
        
        // output group header
        char *pName = FullName();
        if ( pName != NULL ) {
          *ostr << '[' << pName << ']';
          if ( pEntry->Comment() == NULL )
            *ostr << std::endl;
          delete [] pName;
        }
        //else: it's the top (default) group

        bFirstDirty = FALSE;  // only first time
      }

      // output preceding comment for this entry if any
      if ( pEntry->Comment() != NULL ) {
        *ostr << pEntry->Comment();
      }
      
      char *szFilteredValue = filterOut(pEntry->Value());
      *ostr << pEntry->Name() << " = " << szFilteredValue << std::endl;
      delete [] szFilteredValue;

      pEntry->SetDirty(FALSE);
    }
  }

  // flush all subgroups
  ConfigGroup *pGroup;
  Bool bOk = TRUE;
  for ( pGroup = Subgroup(); pGroup != NULL; pGroup = pGroup->Next() ) {
    if ( pGroup->IsDirty() && !pGroup->flush(ostr) ) {
      bOk = FALSE;
    }
  }

  return bOk;
}


// associate a comment with this group (comments are all we ignore,
// including real comments and whitespace)
void FileConfig::ConfigGroup::SetComment(char *szComment)
{
  assert( m_szComment == NULL );  // should be done only once
  
  m_szComment = szComment;
}

// ----------------------------------------------------------------------------
// ctor and dtor
// ----------------------------------------------------------------------------

// common part of both constructors
void FileConfig::Init()
{
  // top group always exists and must be created before reading from files
  m_pRootGroup = new ConfigGroup(NULL, NULL, "");

  m_szComment = NULL;
  m_bOk = FALSE;                // not (yet) initialized ok

  m_bExpandVariables = FALSE;   // don't do it automatically by default
}

// ctor reads data from files
FileConfig::FileConfig(const char *szFileName, Bool bLocalOnly, Bool bUseSubDir)
{
  Init();
  
  m_bUseSubDir = bUseSubDir;
  
  m_szFileName = new char[Strlen(szFileName) + 1];
  strcpy(m_szFileName, szFileName);
  
  // read global (system wide) file
  // ------------------------------
  std::ifstream inpStream;
  if ( !bLocalOnly ) {
    m_szFullFileName = GlobalConfigFile();
    inpStream.open(m_szFullFileName, std::ios::in);
    if ( inpStream ) {
      m_bParsingLocal = FALSE;
      m_bOk = readStream(&inpStream);
    }

    inpStream.close();
    inpStream.clear();
  }

  // read local (user's) file
  // ------------------------
  m_szFullFileName = LocalConfigFile();
  if ( m_szFullFileName != NULL ) {
    inpStream.open(m_szFullFileName, std::ios::in);
    if ( inpStream ) {
      m_bParsingLocal = TRUE;
      if ( readStream(&inpStream) ) {
        m_bOk = TRUE;
      }
      //else: depends on global file - if it was read ok, it's ok
    }
  }

  m_pCurGroup = m_pRootGroup;
  BaseConfig::setCurrentPath("");
}

// ctor reads data from files
FileConfig::FileConfig(std::istream *iStream)
{
  Init();
  
  m_szFileName = NULL;

  if ( iStream == NULL )
     return;

  m_bParsingLocal = TRUE;
  m_bOk = readStream(iStream);
  
  m_pCurGroup = m_pRootGroup;
  BaseConfig::setCurrentPath("");
}

FileConfig::FileConfig(void)
{
   Init();
   m_szFileName = NULL;
}

void
FileConfig::readFile(const char *szFileName)
{
  std::ifstream inpStream;

  Init();

   m_szFileName = new char[Strlen(szFileName) + 1];
   strcpy(m_szFileName, szFileName);

   m_szFullFileName = m_szFileName;
   inpStream.open(m_szFullFileName, std::ios::in);
   if ( inpStream ) {
      m_bParsingLocal = TRUE;
      if ( readStream(&inpStream) ) {
	 m_bOk = TRUE;
      }
   }

   m_pCurGroup = m_pRootGroup;
   BaseConfig::setCurrentPath("");
}

// dtor writes changes
FileConfig::~FileConfig()
{
  if( m_szFileName != NULL )	// if empty, was intialised from stream
    flush();
  
  if ( m_szComment != NULL )
    delete [] m_szComment;
    
  delete m_pRootGroup;
  
  if ( m_szFileName != NULL )
     delete [] m_szFileName;
}

// ----------------------------------------------------------------------------
// config files standard locations
// ----------------------------------------------------------------------------

// ### buffer overflows in sight...
const char *FileConfig::GlobalConfigFile() const
{
  static char s_szBuf[MAX_PATH];

  // check if file has extension
  Bool bNoExt = strchr(m_szFileName, '.') == NULL;

#ifdef  __unix__
    strcpy(s_szBuf, "/etc/");
    strncat(s_szBuf, m_szFileName, MAX_PATH - 11); // "/etc/", ".conf", \0
    s_szBuf[MAX_PATH - 6] = 0;
    if ( bNoExt )
      strcat(s_szBuf, ".conf");
#else   // Windows
    char szWinDir[MAX_PATH];
    ::GetWindowsDirectory(szWinDir, MAX_PATH);
    strcpy(s_szBuf, szWinDir);
    strcat(s_szBuf, "\\");
    strcat(s_szBuf, m_szFileName);
    if ( bNoExt )
      strcat(s_szBuf, ".INI");
#endif  // UNIX/Win

  return s_szBuf;
}

// ### buffer overflows in sight...
const char *FileConfig::LocalConfigFile() const
{
  static char s_szBuf[MAX_PATH];
  int maxlen;

  maxlen = MAX_PATH - 2 - strlen (m_szFileName) - 7; // "/.", m_szFileName, "/config"
#ifdef  __unix__
    const char *szHome = getenv("HOME");
    if ( szHome == NULL ) {
      // we're homeless...
      LogInfo(_("can't find user's HOME, looking for config file in current directory."));
      szHome = ".";
    }
    strncpy(s_szBuf, szHome, maxlen - 1);
    s_szBuf[maxlen - 1] = 0;
    strcat(s_szBuf, "/.");
    strcat(s_szBuf, m_szFileName);
    if(m_bUseSubDir) // look for ~/.appname/config instead of ~/.appname
    {
       mkdir(s_szBuf, 0755);	// try to make it, just in case it
				// didn't exist yet
       strcat(s_szBuf, "/config");
    }
#else   // Windows
#ifdef  __WIN32__
     maxlen -= 4; // ".INI"
     int wmaxlen;
      const char *szHome = getenv("HOMEDRIVE");
      if ( szHome == NULL )
        szHome = "";
      strncpy(s_szBuf, szHome, maxlen - 1);
      s_szBuf[maxlen - 1] = 0;
      szHome = getenv("HOMEPATH");
      wmaxlen = maxlen - 1 - strlen (s_szBuf);
      if ( szHome == NULL )
        strncpy(s_szBuf, ".", wmaxlen);
      else
        strncat(s_szBuf, szHome, wmaxlen);
      strcat(s_szBuf, m_szFileName);
      if ( strchr(m_szFileName, '.') == NULL )
        strcat(s_szBuf, ".INI");
#else   // Win16
      // Win 3.1 has no idea about HOME, so we use only the system wide file
      if ( !bLocalOnly ) {
        // we've already read it
        s_szBuf = NULL;
      }
      else {
        s_szBuf = GlobalConfigFile();
      }
    }
#endif  // WIN16/32
#endif  // UNIX/Win

  return s_szBuf;
}

// ----------------------------------------------------------------------------
// read/write
// ----------------------------------------------------------------------------

// parse the stream and create all groups and entries found in it
Bool FileConfig::readStream(std::istream *istr, ConfigGroup *pRootGroup)
{
  char szBuf[APPCONF_STRBUFLEN];

  m_pCurGroup = pRootGroup == NULL ? m_pRootGroup : pRootGroup;

  m_uiLine = 1;

  for (;;) {
    istr->getline(szBuf, APPCONF_STRBUFLEN, '\n');
    if ( istr->eof() ) {
    // last line may contain text also
    // (if it's not terminated with '\n' EOF is returned)
      return parseLine(szBuf);
    }

    if ( !istr->good() || !parseLine(szBuf) )
      return FALSE;      

    m_uiLine++;
  }
}

// parses one line of config file, returns FALSE on error
Bool FileConfig::parseLine(const char *psz)
{
  size_t len; // temp var

  const char *pStart = psz;
  
  // eat whitespace
  while ( isspace(*psz) ) psz++;

  // ignore empty lines or comments
  if ( *psz == '#' || *psz == ';' || *psz == '\0' ) {
    if ( *pStart != '\0' )
      AppendCommentLine(pStart);
    //else
    //  AppendCommentLine();
    return TRUE;
  }

  if ( *psz == '[' ) {          // a new group
    const char *pEnd = ++psz;

    while ( *pEnd != ']' ) {
      if ( IsValid(*pEnd) ) {
        // continue reading the group name
        pEnd++;
      }
      else {
        if ( *pEnd != APPCONF_PATH_SEPARATOR ) {
          LogError(_("file '%s': unexpected character at line %d (missing ']'?)"),
		           m_szFullFileName, m_uiLine);
          return FALSE;
        }
      }
    }

    len = pEnd - psz;
    char *szGroup = new char[len + 2];
    szGroup[0] = APPCONF_PATH_SEPARATOR;   // always considered as abs path
    szGroup[1] = '\0';
    strncat(szGroup, psz, len);
    
    // will create it if doesn't yet exist
    setCurrentPath(szGroup);

    // was there a comment before it?
    if ( m_szComment != NULL ) {
      m_pCurGroup->SetComment(m_szComment);
      m_szComment = NULL;
    }
    
    delete [] szGroup;
    
    // are there any comments after it?
    Bool bComment = FALSE;
    for ( pStart = ++pEnd ; *pEnd != '\0'; pEnd++ ) {
      switch ( *pEnd ) {
        case '#':
        case ';':
          bComment = TRUE;
          break;
        
        case ' ':
        case '\t':
          // ignore whitespace ('\n' impossible here)
          break;
          
        default:
          if ( !bComment ) {
            LogWarning(_("file '%s', line %d: '%s' ignored after group header."), 
                       m_szFullFileName, m_uiLine, pEnd);
            return TRUE;
          }
      }
    }
      
    if ( bComment ) {
      AppendCommentLine(pStart);
    }
  }
  else {                        // a key
    const char *pEnd = psz;
    while ( IsValid(*pEnd) )
      pEnd++;

    len = pEnd - psz;
    char *szKey = new char[len + 1];
    strncpy(szKey, psz, len + 1);
    szKey[len] = '\0';

    while ( isspace(*pEnd) ) pEnd++;  // eat whitespace

    if ( *pEnd++ != '=' ) {
      LogError(_("file '%s': expected '=' at line %d."), 
               m_szFullFileName, m_uiLine);
      return FALSE;
    }

    while ( isspace(*pEnd) ) pEnd++;  // eat whitespace

    ConfigEntry *pEntry = m_pCurGroup->FindEntry(szKey);

    if ( pEntry == NULL ) {
      pEntry = m_pCurGroup->AddEntry(szKey);
    }
    /* gives erroneous warnings when the same key appears in
       both global and local config files. Would be nice to
       have something happen when the same key appears twice
       (or more) in a section though.
  
      else {
        LogWarning(_("key '%s' has more than one value."), szKey);
      }

    */

    if ( m_szComment != NULL ) {
      pEntry->SetComment(m_szComment);
      m_szComment = NULL;
    }
    
    char *szUnfilteredValue = filterIn(pEnd);
    pEntry->SetValue(szUnfilteredValue, m_bParsingLocal, TRUE);

    delete [] szUnfilteredValue;
    delete [] szKey;
  }

  return TRUE;
}

// ----------------------------------------------------------------------------
// read data (which are fully loaded in memory)
// ----------------------------------------------------------------------------

const char *FileConfig::readEntry(const char *szKey, 
                                  const char *szDefault) const
{
  ConfigEntry *pEntry = m_pCurGroup->FindEntry(szKey);

  if ( pEntry != NULL ) {
    if ( m_bExpandVariables )
      return pEntry->ExpandedValue();
    else
      return pEntry->Value();
  }

  if(m_bRecordDefaults)
     ((FileConfig *)this)->writeEntry(szKey,szDefault);

  // entry wasn't found
  return szDefault;
}

// ----------------------------------------------------------------------------
// functions which update data in memory
// ----------------------------------------------------------------------------

// create new group if this one doesn't exist yet
void FileConfig::changeCurrentPath(const char *szPath)
{
  // normalize path
  BaseConfig::changeCurrentPath(szPath);
  szPath = getCurrentPath();

  m_pCurGroup = m_pRootGroup;

  // special case of empty path
  if ( *szPath == '\0' )
    return;
    
  char   *szGroupName = NULL;
  size_t  len = 0;

  const char *pBegin = szPath;
  const char *pEnd   = pBegin + 1;

  do{//while ( *pEnd != '\0' ) {
    while ( *pEnd != '\0' && *pEnd != APPCONF_PATH_SEPARATOR )
      pEnd++;

    if ( (unsigned)(pEnd - pBegin) + 1 > len ) {
      // not enough space, realloc buffer
      len = pEnd - pBegin + 1;

      // also delete old one
      if ( szGroupName != NULL )
        delete [] szGroupName;

      szGroupName = new char[len];
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -