fl_preferences.cxx

来自「SRI international 发布的OAA框架软件」· CXX 代码 · 共 1,121 行 · 第 1/2 页

CXX
1,121
字号
    int len = s-path;
    char *p = (char*)malloc( len+1 );
    memcpy( p, path, len );
    p[len] = 0;
    makePath( p );
    free( p );
#if defined(WIN32) && !defined(__CYGWIN__)
    return ( mkdir( path ) == 0 );
#else
    return ( mkdir( path, 0777 ) == 0 );
#endif // WIN32 && !__CYGWIN__
  }
  return 1;
}

// strip the filename and create a path
static void makePathForFile( const char *path )
{
  const char *s = strrchr( path, '/' );
  if ( !s ) return;
  int len = s-path;
  char *p = (char*)malloc( len+1 );
  memcpy( p, path, len );
  p[len] = 0;
  makePath( p );
  free( p );
}

// create the root node
// - construct the name of the file that will hold our preferences
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char *vendor, const char *application )
{
  char filename[ FL_PATH_MAX ]; filename[0] = 0;
#ifdef WIN32
#  define FLPREFS_RESOURCE	"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
  int appDataLen = strlen(vendor) + strlen(application) + 8;
  DWORD type, nn;
  LONG err;
  HKEY key;

  switch (root) {
    case SYSTEM:
      err = RegOpenKey( HKEY_LOCAL_MACHINE, FLPREFS_RESOURCE, &key );
      if (err == ERROR_SUCCESS) {
	nn = FL_PATH_MAX - appDataLen;
	err = RegQueryValueEx( key, "Common AppData", 0L, &type, (BYTE*)filename, &nn );
	if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) )
	  filename[0] = 0;
        RegCloseKey(key);
      }
      break;
    case USER:
      err = RegOpenKey( HKEY_CURRENT_USER, FLPREFS_RESOURCE, &key );
      if (err == ERROR_SUCCESS) {
	nn = FL_PATH_MAX - appDataLen;
	err = RegQueryValueEx( key, "AppData", 0L, &type, (BYTE*)filename, &nn );
	if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) )
	  filename[0] = 0;
        RegCloseKey(key);
      }
      break;
  }

  if (!filename[0]) {
    strcpy(filename, "C:\\FLTK");
  }

  snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
           "/%s/%s.prefs", vendor, application);
  for (char *s = filename; *s; s++) if (*s == '\\') *s = '/';
#elif defined ( __APPLE__ )
  FSSpec spec = { 0 };
  FSRef ref;
  OSErr err = fnfErr;
  switch (root) {
    case SYSTEM:
      err = FindFolder( kLocalDomain, kPreferencesFolderType,
			1, &spec.vRefNum, &spec.parID );
      break;
    case USER:
      err = FindFolder( kUserDomain, kPreferencesFolderType, 
			1, &spec.vRefNum, &spec.parID );
      break;
  }
  FSpMakeFSRef( &spec, &ref );
  FSRefMakePath( &ref, (UInt8*)filename, FL_PATH_MAX );
  snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
           "/%s/%s.prefs", vendor, application );
#else
  const char *e;
  switch (root) {
    case USER:
      if ((e = getenv("HOME")) != NULL) {
	strlcpy(filename, e, sizeof(filename));

	if (filename[strlen(filename)-1] != '/') {
	  strlcat(filename, "/.fltk/", sizeof(filename));
	} else {
	  strlcat(filename, ".fltk/", sizeof(filename));
	}
	break;
      }

    case SYSTEM:
      strcpy(filename, "/etc/fltk/");
      break;
  }

  snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
           "%s/%s.prefs", vendor, application);
#endif

  makePathForFile(filename);

  prefs_       = prefs;
  filename_    = strdup(filename);
  vendor_      = strdup(vendor);
  application_ = strdup(application);

  read();
}

// create the root node
// - construct the name of the file that will hold our preferences
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, const char *vendor, const char *application )
{
  char filename[ FL_PATH_MAX ]; filename[0] = 0;

  snprintf(filename, sizeof(filename), "%s/%s.prefs", path, application);

  makePathForFile(filename);

  prefs_       = prefs;
  filename_    = strdup(filename);
  vendor_      = strdup(vendor);
  application_ = strdup(application);

  read();
}

// destroy the root node and all depending nodes
Fl_Preferences::RootNode::~RootNode()
{
  if ( prefs_->node->dirty() )
    write();
  if ( filename_ ) 
    free( filename_ );
  if ( vendor_ )
    free( vendor_ );
  if ( application_ )
    free( application_ );
  delete prefs_->node;
}

// read a preferences file and construct the group tree and with all entry leafs
int Fl_Preferences::RootNode::read()
{
  char buf[1024];
  FILE *f = fopen( filename_, "rb" );
  if ( !f ) return 0;
  fgets( buf, 1024, f );
  fgets( buf, 1024, f );
  fgets( buf, 1024, f );
  Node *nd = prefs_->node;
  for (;;)
  {
    if ( !fgets( buf, 1024, f ) ) break;	// EOF or Error
    if ( buf[0]=='[' ) // read a new group
    {
      int end = strcspn( buf+1, "]\n\r" );
      buf[ end+1 ] = 0;
      nd = prefs_->node->find( buf+1 );
    }
    else if ( buf[0]=='+' ) // 
    { // value of previous name/value pair spans multiple lines
      int end = strcspn( buf+1, "\n\r" );
      if ( end != 0 ) // if entry is not empty
      {
	buf[ end+1 ] = 0;
	nd->add( buf+1 );
      }
    }
    else // read a name/value pair
    {
      int end = strcspn( buf, "\n\r" );
      if ( end != 0 ) // if entry is not empty
      {
	buf[ end ] = 0;
	nd->set( buf );
      }
    }
  }
  fclose( f );
  return 0;
}

// write the group tree and all entry leafs
int Fl_Preferences::RootNode::write()
{
  FILE *f = fopen( filename_, "wb" );
  if ( !f ) return 1;
  fprintf( f, "; FLTK preferences file format 1.0\n" );
  fprintf( f, "; vendor: %s\n", vendor_ );
  fprintf( f, "; application: %s\n", application_ );
  prefs_->node->write( f );
  fclose( f );
  return 0;
}

// get the path to the preferences directory
char Fl_Preferences::RootNode::getPath( char *path, int pathlen )
{
  strlcpy( path, filename_, pathlen);

  char *s;
  for ( s = path; *s; s++ ) if ( *s == '\\' ) *s = '/';
  s = strrchr( path, '.' );
  if ( !s ) return 0;
  *s = 0;
  char ret = makePath( path );
  strcpy( s, "/" );
  return ret;
}

// create a node that represents a group
// - path must be a single word, prferable alnum(), dot and underscore only. Space is ok.
Fl_Preferences::Node::Node( const char *path )
{
  if ( path ) path_ = strdup( path ); else path_ = 0;
  child_ = 0; next_ = 0; parent_ = 0;
  entry = 0;
  nEntry = NEntry = 0;
  dirty_ = 0;
}

// delete this and all depending nodes
Fl_Preferences::Node::~Node()
{
  Node *nx;
  for ( Node *nd = child_; nd; nd = nx )
  {
    nx = nd->next_;
    delete nd;
  }
  if ( entry )
  {
    for ( int i = 0; i < nEntry; i++ )
    {
      if ( entry[i].name ) 
	free( entry[i].name );
      if ( entry[i].value )
	free( entry[i].value );
    }
    free( entry );
  }
  if ( path_ ) 
    free( path_ );
}

// recursively check if any entry is dirty (was changed after loading a fresh prefs file)
char Fl_Preferences::Node::dirty()
{
  if ( dirty_ ) return 1;
  if ( next_ && next_->dirty() ) return 1;
  if ( child_ && child_->dirty() ) return 1;
  return 0;
}

// write this node (recursively from the last neighbor back to this)
// write all entries
// write all children
int Fl_Preferences::Node::write( FILE *f )
{
  if ( next_ ) next_->write( f );
  fprintf( f, "\n[%s]\n\n", path_ );
  for ( int i = 0; i < nEntry; i++ )
  {
    char *src = entry[i].value;
    if ( src )
    { // hack it into smaller pieces if needed
      fprintf( f, "%s:", entry[i].name );
      int cnt;
      for ( cnt = 0; cnt < 60; cnt++ )
	if ( src[cnt]==0 ) break;
      fwrite( src, cnt, 1, f );
      fprintf( f, "\n" );
      src += cnt;
      for (;*src;)
      {
	for ( cnt = 0; cnt < 80; cnt++ )
	  if ( src[cnt]==0 ) break;
        fputc( '+', f );
	fwrite( src, cnt, 1, f );
        fputc( '\n', f );
	src += cnt;
      }
    }
    else
      fprintf( f, "%s\n", entry[i].name );
  }
  if ( child_ ) child_->write( f );
  dirty_ = 0;
  return 0;
}

// set the parent node and create the full path
void Fl_Preferences::Node::setParent( Node *pn )
{
  parent_ = pn;
  next_ = pn->child_;
  pn->child_ = this;
  sprintf( nameBuffer, "%s/%s", pn->path_, path_ );
  free( path_ );
  path_ = strdup( nameBuffer );
}

// add a child to this node and set its path (try to find it first...)
Fl_Preferences::Node *Fl_Preferences::Node::addChild( const char *path )
{
  sprintf( nameBuffer, "%s/%s", path_, path );
  char *name = strdup( nameBuffer );
  Node *nd = find( nameBuffer );
  free( name );
  dirty_ = 1;
  return nd;
}

// create and set, or change an entry within this node
void Fl_Preferences::Node::set( const char *name, const char *value )
{
  for ( int i=0; i<nEntry; i++ )
  {
    if ( strcmp( name, entry[i].name ) == 0 )
    {
      if ( !value ) return; // annotation
      if ( strcmp( value, entry[i].value ) != 0 )
      {
	if ( entry[i].value )
	  free( entry[i].value );
	entry[i].value = strdup( value );
	dirty_ = 1;
      }
      lastEntrySet = i;
      return;
    }
  }
  if ( NEntry==nEntry )
  {
    NEntry = NEntry ? NEntry*2 : 10;
    entry = (Entry*)realloc( entry, NEntry * sizeof(Entry) );
  }
  entry[ nEntry ].name = strdup( name );
  entry[ nEntry ].value = value?strdup( value ):0;
  lastEntrySet = nEntry;
  nEntry++;
  dirty_ = 1;
}

// create or set a value (or annotation) from a single line in the file buffer
void Fl_Preferences::Node::set( const char *line )
{
  // hmm. If we assume that we always read this file in the beginning,
  // we can handle the dirty flag 'quick and dirty'
  char dirt = dirty_;
  if ( line[0]==';' || line[0]==0 || line[0]=='#' )
  {
    set( line, 0 );
  }
  else
  {
    const char *c = strchr( line, ':' );
    if ( c )
    {
      strlcpy( nameBuffer, line, c-line+1);
      set( nameBuffer, c+1 );
    }
    else
      set( line, "" );
  }
  dirty_ = dirt;
}

// add more data to an existing entry
void Fl_Preferences::Node::add( const char *line )
{
  if ( lastEntrySet<0 || lastEntrySet>=nEntry ) return;
  char *&dst = entry[ lastEntrySet ].value;
  int a = strlen( dst );
  int b = strlen( line );
  dst = (char*)realloc( dst, a+b+1 );
  memcpy( dst+a, line, b+1 );
  dirty_ = 1;
}

// get the value for a name, returns 0 if no such name
const char *Fl_Preferences::Node::get( const char *name )
{
  int i = getEntry( name );
  return i>=0 ? entry[i].value : 0 ;
}

// find the index of an entry, returns -1 if no such entry
int Fl_Preferences::Node::getEntry( const char *name )
{
  for ( int i=0; i<nEntry; i++ )
  {
    if ( strcmp( name, entry[i].name ) == 0 )
    {
      return i;
    }
  }
  return -1;
}

// remove one entry form this group
char Fl_Preferences::Node::deleteEntry( const char *name )
{
  int ix = getEntry( name );
  if ( ix == -1 ) return 0;
  memmove( entry+ix, entry+ix+1, (nEntry-ix-1) * sizeof(Entry) );
  nEntry--;
  dirty_ = 1;
  return 1;
}

// find a group somewhere in the tree starting here
// - this method will always return a valid node (except for memory allocation problems)
// - if the node was not found, 'find' will create the required branch
Fl_Preferences::Node *Fl_Preferences::Node::find( const char *path )
{
  int len = strlen( path_ );
  if ( strncmp( path, path_, len ) == 0 )
  {
    if ( path[ len ] == 0 ) 
      return this;
    if ( path[ len ] == '/' )
    {
      Node *nd;
      for ( nd = child_; nd; nd = nd->next_ )
      {
	Node *nn = nd->find( path );
	if ( nn ) return nn;
      }
      const char *s = path+len+1;
      const char *e = strchr( s, '/' );
      if (e) strlcpy( nameBuffer, s, e-s+1 );
      else strlcpy( nameBuffer, s, sizeof(nameBuffer));
      nd = new Node( nameBuffer );
      nd->setParent( this );
      return nd->find( path );
    }
  }
  return 0;
}

// find a group somewhere in the tree starting here
// caller must not set 'offset' argument
// - if the node does not exist, 'search' returns NULL
// - if the pathname is "." (current node) return this node
// - if the pathname is "./" (root node) return the topmost node
// - if the pathname starts with "./", start the search at the root node instead
Fl_Preferences::Node *Fl_Preferences::Node::search( const char *path, int offset )
{

  if ( offset == 0 )
  {
    if ( path[0] == '.' )
    {
      if ( path[1] == 0 )
      {
	return this; // user was searching for current node
      }
      else if ( path[1] == '/' )
      {
	Node *nn = this;
	while ( nn->parent_ ) nn = nn->parent_;
	if ( path[2]==0 )
	{ // user is searching for root ( "./" )
	  return nn;
	}
	return nn->search( path+2, 2 ); // do a relative search on the root node
      }
    }
    offset = strlen( path_ ) + 1;
  }
  
  int len = strlen( path_ );
  if ( len < offset-1 ) return 0;
  len -= offset;
  if ( ( len <= 0 ) || ( strncmp( path, path_+offset, len ) == 0 ) )
  {
    if ( len > 0 && path[ len ] == 0 ) 
      return this;
    if ( len <= 0 || path[ len ] == '/' )
    {
      for ( Node *nd = child_; nd; nd = nd->next_ )
      {
	Node *nn = nd->search( path, offset );
	if ( nn ) return nn;
      }
      return 0;
    }
  }
  return 0;
}

// return the number of child nodes (groups)
int Fl_Preferences::Node::nChildren()
{
  int cnt = 0;
  for ( Node *nd = child_; nd; nd = nd->next_ )
    cnt++;
  return cnt;
}

// return the n'th child node
const char *Fl_Preferences::Node::child( int ix )
{
  Node *nd;
  for ( nd = child_; nd; nd = nd->next_ )
  {
    if ( !ix-- ) break;
  }
  if ( nd && nd->path_ )
  {
    char *r = strrchr( nd->path_, '/' );
    return r ? r+1 : nd->path_ ;
  }
  return 0L ;
}

// remove myself from the list and delete me (and all children)
char Fl_Preferences::Node::remove()
{
  Node *nd = 0, *np;
  if ( parent_ )
  {
    nd = parent_->child_; np = 0L;
    for ( ; nd; nd = nd->next_ )
    {
      if ( nd == this )
      {
	if ( np ) 
	  np->next_ = nd->next_; 
	else 
	  parent_->child_ = nd->next_;
	break;
      }
    }
  }
  delete this;
  dirty_ = 1;
  return ( nd != 0 );
}


//
// End of "$Id: Fl_Preferences.cxx,v 1.1.1.1 2003/06/03 22:25:43 agno Exp $".
//

⌨️ 快捷键说明

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