#include <FL/Fl.H>
#include "Fl_System_Driver.H"
#include <FL/Fl_Preferences.H>
#include <FL/Fl_Plugin.H>
#include <FL/filename.H>
#include <FL/fl_utf8.h>
#include <FL/fl_string_functions.h>
#include "flstring.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#if (FLTK_USE_STD)
#include <string>
#endif
char Fl_Preferences::nameBuffer[128];
char Fl_Preferences::uuidBuffer[40];
Fl_Preferences *Fl_Preferences::runtimePrefs = 0;
unsigned int Fl_Preferences::fileAccess_ = Fl_Preferences::ALL;
static int clocale_snprintf(char *buffer, size_t buffer_size, const char *format, ...)
{
va_list args;
va_start(args, format);
int retval = Fl::system_driver()->clocale_vsnprintf(buffer, buffer_size, format, args);
va_end(args);
return retval;
}
static int clocale_sscanf(const char *input, const char *format, ...)
{
va_list args;
va_start(args, format);
int retval = Fl::system_driver()->clocale_vsscanf(input, format, args);
va_end(args);
return retval;
}
const char *Fl_Preferences::new_UUID() {
Fl::system_driver()->newUUID(uuidBuffer);
return uuidBuffer;
}
void Fl_Preferences::file_access(unsigned int flags)
{
fileAccess_ = flags;
}
unsigned int Fl_Preferences::file_access()
{
return fileAccess_;
}
Fl_Preferences::Root Fl_Preferences::filename( char *buffer, size_t buffer_size, Root root, const char *vendor, const char *application )
{
Root ret = UNKNOWN_ROOT_TYPE;
if (buffer && buffer_size>0) {
char *fn = Fl::system_driver()->preference_rootnode(NULL, root, vendor, application);
if (fn) {
fl_strlcpy(buffer, fn, buffer_size);
{ char *s; for ( s = buffer; *s; s++ ) if ( *s == '\\' ) *s = '/'; }
ret = root;
} else {
buffer[0] = 0;
}
}
return ret;
}
Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application ) {
node = new Node( "." );
rootNode = new RootNode( this, root, vendor, application );
node->setRoot(rootNode);
if (root & CLEAR)
clear();
}
Fl_Preferences::Fl_Preferences( const char *path, const char *vendor, const char *application ) {
node = new Node( "." );
rootNode = new RootNode( this, path, vendor, application, (Root)0 );
node->setRoot(rootNode);
}
Fl_Preferences::Fl_Preferences( const char *path, const char *vendor, const char *application, Root flags ) {
node = new Node( "." );
rootNode = new RootNode( this, path, vendor, application, flags );
node->setRoot(rootNode);
if (flags & CLEAR)
clear();
}
Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *group ) {
rootNode = parent.rootNode;
node = parent.node->addChild( group );
}
Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group ) {
if (parent==NULL) {
if (!runtimePrefs) {
runtimePrefs = new Fl_Preferences();
runtimePrefs->node = new Node( "." );
runtimePrefs->rootNode = new RootNode( runtimePrefs );
runtimePrefs->node->setRoot(runtimePrefs->rootNode);
}
parent = runtimePrefs;
}
rootNode = parent->rootNode;
node = parent->node->addChild( group );
}
Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, int groupIndex ) {
rootNode = parent.rootNode;
if (groupIndex<0 || groupIndex>=parent.groups()) {
node = parent.node->addChild( newUUID() );
} else {
node = parent.node->childNode( groupIndex );
}
}
Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, int groupIndex ) {
rootNode = parent->rootNode;
if (groupIndex<0 || groupIndex>=parent->groups()) {
node = parent->node->addChild( newUUID() );
} else {
node = parent->node->childNode( groupIndex );
}
}
Fl_Preferences::Fl_Preferences( Fl_Preferences::ID id ) {
node = (Node*)id;
rootNode = node->findRoot();
}
Fl_Preferences::Fl_Preferences(const Fl_Preferences &rhs)
: node(rhs.node),
rootNode(rhs.rootNode)
{ }
Fl_Preferences &Fl_Preferences::operator=(const Fl_Preferences &rhs) {
if (&rhs != this) {
node = rhs.node;
rootNode = rhs.rootNode;
}
return *this;
}
Fl_Preferences::~Fl_Preferences() {
if (node && !node->parent()) delete rootNode;
node = 0L;
rootNode = 0L;
}
Fl_Preferences::Root Fl_Preferences::filename( char *buffer, size_t buffer_size)
{
if (!buffer || buffer_size==0)
return UNKNOWN_ROOT_TYPE;
RootNode *rn = rootNode;
if (!rn)
return UNKNOWN_ROOT_TYPE;
if (rn->root()==MEMORY)
return MEMORY;
char *fn = rn->filename();
if (!fn)
return UNKNOWN_ROOT_TYPE;
fl_strlcpy(buffer, fn, buffer_size);
if (buffer[0]==0)
return UNKNOWN_ROOT_TYPE;
return (Root)(rn->root() & ROOT_MASK);
}
int Fl_Preferences::groups() {
return node->nChildren();
}
const char *Fl_Preferences::group( int num_group ) {
return node->child( num_group );
}
char Fl_Preferences::group_exists( const char *key ) {
return node->search( key ) ? 1 : 0 ;
}
char Fl_Preferences::delete_group( const char *group ) {
Node *nd = node->search( group );
if ( nd ) return nd->remove();
return 0;
}
char Fl_Preferences::delete_all_groups() {
node->deleteAllChildren();
return 1;
}
int Fl_Preferences::entries() {
return node->nEntry();
}
const char *Fl_Preferences::entry( int index ) {
return node->entry(index).name;
}
char Fl_Preferences::entry_exists( const char *key ) {
return node->getEntry( key )>=0 ? 1 : 0 ;
}
char Fl_Preferences::delete_entry( const char *key ) {
return node->deleteEntry( key );
}
char Fl_Preferences::delete_all_entries() {
node->deleteAllEntries();
return 1;
}
char Fl_Preferences::clear() {
char ret1 = deleteAllGroups();
char ret2 = deleteAllEntries();
return ret1 & ret2;
}
char Fl_Preferences::get( const char *key, int &value, int defaultValue ) {
const char *v = node->get( key );
value = v ? atoi( v ) : defaultValue;
return ( v != 0 );
}
char Fl_Preferences::set( const char *key, int value ) {
snprintf( nameBuffer, sizeof(nameBuffer), "%d", value );
node->set( key, nameBuffer );
return 1;
}
char Fl_Preferences::get( const char *key, float &value, float defaultValue ) {
const char *v = node->get( key );
if (v) {
if (rootNode->root() & C_LOCALE) {
clocale_sscanf(v, "%g", &value);
} else {
value = (float)atof(v);
}
} else {
value = defaultValue;
}
return ( v != NULL );
}
char Fl_Preferences::set( const char *key, float value ) {
if (rootNode->root() & C_LOCALE) {
clocale_snprintf( nameBuffer, sizeof(nameBuffer), "%g", value );
} else {
snprintf( nameBuffer, sizeof(nameBuffer), "%g", value );
}
node->set( key, nameBuffer );
return 1;
}
char Fl_Preferences::set( const char *key, float value, int precision ) {
if (rootNode->root() & C_LOCALE) {
clocale_snprintf( nameBuffer, sizeof(nameBuffer), "%.*g", precision, value );
} else {
snprintf( nameBuffer, sizeof(nameBuffer), "%.*g", precision, value );
}
node->set( key, nameBuffer );
return 1;
}
char Fl_Preferences::get( const char *key, double &value, double defaultValue ) {
const char *v = node->get( key );
if (v) {
if (rootNode->root() & C_LOCALE) {
clocale_sscanf(v, "%lg", &value);
} else {
value = atof(v);
}
} else {
value = defaultValue;
}
return ( v != NULL );
}
char Fl_Preferences::set( const char *key, double value ) {
if (rootNode->root() & C_LOCALE) {
clocale_snprintf( nameBuffer, sizeof(nameBuffer), "%lg", value );
} else {
snprintf( nameBuffer, sizeof(nameBuffer), "%lg", value );
}
node->set( key, nameBuffer );
return 1;
}
char Fl_Preferences::set( const char *key, double value, int precision ) {
if (rootNode->root() & C_LOCALE) {
clocale_snprintf( nameBuffer, sizeof(nameBuffer), "%.*lg", precision, value );
} else {
snprintf( nameBuffer, sizeof(nameBuffer), "%.*lg", precision, value );
}
node->set( key, nameBuffer );
return 1;
}
static char *decodeText( const char *src ) {
int len = 0;
const char *s = src;
for ( ; *s; s++, len++ ) {
if ( *s == '\\' ) {
if ( isdigit( s[1] ) ) {
s+=3;
} else {
s+=1;
}
}
}
char *dst = (char*)malloc( len+1 ), *d = dst;
for ( s = src; *s; s++ ) {
char c = *s;
if ( c == '\\' ) {
if ( s[1] == '\\' ) { *d++ = c; s++; }
else if ( s[1] == 'n' ) { *d++ = '\n'; s++; }
else if ( s[1] == 'r' ) { *d++ = '\r'; s++; }
else if ( isdigit( s[1] ) ) { *d++ = ((s[1]-'0')<<6) + ((s[2]-'0')<<3) + (s[3]-'0'); s+=3; }
else s++; }
else
*d++ = c;
}
*d = 0;
return dst;
}
char Fl_Preferences::get( const char *key, char *text, const char *defaultValue, int maxSize ) {
const char *v = node->get( key );
if ( v && strchr( v, '\\' ) ) {
char *w = decodeText( v );
strlcpy(text, w, maxSize);
free( w );
return 1;
}
if ( !v ) v = defaultValue;
if ( v ) strlcpy(text, v, maxSize);
else *text = 0;
return ( v != defaultValue );
}
char Fl_Preferences::get( const char *key, char *&text, const char *defaultValue ) {
const char *v = node->get( key );
if ( v && strchr( v, '\\' ) ) {
text = decodeText( v );
return 1;
}
if ( !v ) v = defaultValue;
if ( v )
text = fl_strdup( v );
else
text = 0;
return ( v != defaultValue );
}
#if (FLTK_USE_STD)
char Fl_Preferences::get( const char *key, std::string &value, const std::string &defaultValue ) {
const char *v = node->get( key );
if (v) {
if ( strchr( v, '\\' ) ) {
char *text = decodeText( v );
value = text;
::free(text);
} else {
value = v;
}
return 1;
} else {
value = defaultValue;
return 0;
}
}
#endif
char Fl_Preferences::set( const char *key, const char *text ) {
const char *s = text ? text : "";
int n=0, ns=0;
for ( ; *s; s++ ) { n++; if ( *s<32 || *s=='\\' || *s==0x7f ) ns+=4; }
if ( ns ) {
char *buffer = (char*)malloc( n+ns+1 ), *d = buffer;
for ( s=text; *s; ) {
char c = *s;
if ( c=='\\' ) { *d++ = '\\'; *d++ = '\\'; s++; }
else if ( c=='\n' ) { *d++ = '\\'; *d++ = 'n'; s++; }
else if ( c=='\r' ) { *d++ = '\\'; *d++ = 'r'; s++; }
else if ( c<32 || c==0x7f )
{ *d++ = '\\'; *d++ = '0'+((c>>6)&3); *d++ = '0'+((c>>3)&7); *d++ = '0'+(c&7); s++; }
else *d++ = *s++;
}
*d = 0;
node->set( key, buffer );
free( buffer );
}
else
node->set( key, text );
return 1;
}
static void *decodeHex( const char *src, int &size ) {
size = (int) strlen( src )/2;
unsigned char *data = (unsigned char*)malloc( size ), *d = data;
const char *s = src;
for ( int i=size; i>0; i-- ) {
int v;
char x = tolower(*s++);
if ( x >= 'a' ) v = x-'a'+10; else v = x-'0';
v = v<<4;
x = tolower(*s++);
if ( x >= 'a' ) v += x-'a'+10; else v += x-'0';
*d++ = (uchar)v;
}
return (void*)data;
}
char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int maxSize ) {
const char *v = node->get( key );
if ( v ) {
int dsize;
void *w = decodeHex( v, dsize );
memmove( data, w, dsize>maxSize?maxSize:dsize );
free( w );
return 1;
}
if ( defaultValue )
memmove( data, defaultValue, defaultSize>maxSize?maxSize:defaultSize );
return 0;
}
char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int *maxSize ) {
if (!maxSize || !data)
return -1;
int capacity = *maxSize;
const char *v = node->get( key );
if ( v ) {
int nFound;
void *w = decodeHex( v, nFound );
int nWrite = (nFound>capacity) ? capacity : nFound;
memmove( data, w, nWrite);
free( w );
*maxSize = nWrite;
return 1;
}
if ( defaultValue ) {
int nWrite = (defaultSize>capacity) ? capacity : defaultSize;
memmove( data, defaultValue, nWrite );
*maxSize = nWrite;
} else {
*maxSize = 0;
}
return 0;
}
char Fl_Preferences::get( const char *key, void *&data, const void *defaultValue, int defaultSize ) {
const char *v = node->get( key );
if ( v ) {
int dsize;
data = decodeHex( v, dsize );
return 1;
}
if ( defaultValue ) {
data = (void*)malloc( defaultSize );
memmove( data, defaultValue, defaultSize );
}
else
data = 0;
return 0;
}
char Fl_Preferences::set( const char *key, const void *data, int dsize ) {
char *buffer = (char*)malloc( dsize*2+1 ), *d = buffer;;
unsigned char *s = (unsigned char*)data;
for ( ; dsize>0; dsize-- ) {
static char lu[] = "0123456789abcdef";
unsigned char v = *s++;
*d++ = lu[v>>4];
*d++ = lu[v&0xf];
}
*d = 0;
node->set( key, buffer );
free( buffer );
return 1;
}
#if (FLTK_USE_STD)
char Fl_Preferences::set( const char *entry, const std::string &value ) {
return set(entry, value.c_str());
}
#endif
int Fl_Preferences::size( const char *key ) {
const char *v = node->get( key );
return (int) (v ? strlen( v ) : 0);
}
char Fl_Preferences::get_userdata_path( char *path, int pathlen ) {
if ( rootNode )
return rootNode->getPath( path, pathlen );
return 0;
}
int Fl_Preferences::flush() {
int ret = dirty();
if (ret != 1)
return ret;
return rootNode->write();
}
int Fl_Preferences::dirty() {
Node *n = node;
while (n && n->parent())
n = n->parent();
if (!n)
return -1;
return n->dirty();
}
Fl_Preferences::Name::Name( unsigned int n ) {
data_ = (char*)malloc(20);
snprintf(data_, 20, "%u", n);
}
Fl_Preferences::Name::Name( const char *format, ... ) {
data_ = (char*)malloc(1024);
va_list args;
va_start(args, format);
vsnprintf(data_, 1024, format, args);
va_end(args);
}
Fl_Preferences::Name::~Name() {
if (data_) {
free(data_);
data_ = 0L;
}
}
int Fl_Preferences::Node::lastEntrySet = -1;
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char *vendor, const char *application )
: prefs_(prefs),
filename_(0L),
vendor_(0L),
application_(0L),
root_type_((Root)(root & ~CLEAR))
{
char *filename = Fl::system_driver()->preference_rootnode(prefs, root, vendor, application);
filename_ = filename ? fl_strdup(filename) : 0L;
vendor_ = fl_strdup(vendor);
application_ = fl_strdup(application);
if ( (root & CLEAR) == 0 )
read();
}
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, const char *vendor, const char *application, Root flags )
: prefs_(prefs),
filename_(0L),
vendor_(0L),
application_(0L),
root_type_( (Root)(USER | (flags & C_LOCALE) ))
{
if (!vendor)
vendor = "unknown";
if (!application) {
application = "unknown";
filename_ = fl_strdup(path);
} else {
char filename[ FL_PATH_MAX ]; filename[0] = 0;
snprintf(filename, sizeof(filename), "%s/%s.prefs", path, application);
filename_ = fl_strdup(filename);
}
vendor_ = fl_strdup(vendor);
application_ = fl_strdup(application);
if ( (flags & CLEAR) == 0 )
read();
}
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs )
: prefs_(prefs),
filename_(0L),
vendor_(0L),
application_(0L),
root_type_(Fl_Preferences::MEMORY)
{
}
Fl_Preferences::RootNode::~RootNode() {
if ( prefs_->node->dirty() )
write();
if ( filename_ ) {
free( filename_ );
filename_ = 0L;
}
if ( vendor_ ) {
free( vendor_ );
vendor_ = 0L;
}
if ( application_ ) {
free( application_ );
application_ = 0L;
}
delete prefs_->node;
prefs_->node = 0L;
}
int Fl_Preferences::RootNode::read() {
if ( (root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::MEMORY ) {
prefs_->node->clearDirtyFlags();
return 0;
}
if (!filename_ || !filename_[0]) return -1;
if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_READ_OK) ) {
prefs_->node->clearDirtyFlags();
return -1;
}
if ( ((root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::USER) && !(fileAccess_ & Fl_Preferences::USER_READ_OK) ) {
prefs_->node->clearDirtyFlags();
return -1;
}
if ( ((root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::SYSTEM) && !(fileAccess_ & Fl_Preferences::SYSTEM_READ_OK) ) {
prefs_->node->clearDirtyFlags();
return -1;
}
char buf[1024];
FILE *f = fl_fopen( filename_, "rb" );
if ( !f )
return -1;
if (fgets( buf, 1024, f )==0) { }
if (fgets( buf, 1024, f )==0) { }
if (fgets( buf, 1024, f )==0) { }
Node *nd = prefs_->node;
for (;;) {
if ( !fgets( buf, 1024, f ) ) break; if ( buf[0]=='[' ) { size_t end = strcspn( buf+1, "]\n\r" );
buf[ end+1 ] = 0;
nd = prefs_->node->find( buf+1 );
} else if ( buf[0]=='+' ) { size_t end = strcspn( buf+1, "\n\r" );
if ( end != 0 ) { buf[ end+1 ] = 0;
if (nd) nd->add( buf+1 );
}
} else { size_t end = strcspn( buf, "\n\r" );
if ( end != 0 ) { buf[ end ] = 0;
if (nd) nd->set( buf );
}
}
}
fclose( f );
prefs_->node->clearDirtyFlags();
return 0;
}
int Fl_Preferences::RootNode::write() {
if ( (root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::MEMORY ) {
prefs_->node->clearDirtyFlags();
return 0;
}
if (!filename_ || !filename_[0]) return -1;
if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_WRITE_OK) )
return -1;
if ( ((root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::USER) && !(fileAccess_ & Fl_Preferences::USER_WRITE_OK) )
return -1;
if ( ((root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::SYSTEM) && !(fileAccess_ & Fl_Preferences::SYSTEM_WRITE_OK) )
return -1;
fl_make_path_for_file(filename_);
FILE *f = fl_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 );
if (Fl::system_driver()->preferences_need_protection_check()) {
if (strncmp(filename_, "/etc/fltk/", 10) == 0) {
char *p;
p = filename_ + 9;
do { *p = 0;
fl_chmod(filename_, 0755); *p = '/';
p = strchr(p+1, '/');
} while (p);
fl_chmod(filename_, 0644); }
}
return 0;
}
char Fl_Preferences::RootNode::getPath( char *path, int pathlen ) {
if (!filename_) return 1;
if (pathlen<=0)
return 1;
strlcpy( path, filename_, pathlen);
char *name = 0L, *ext = 0L;
{ char *s; for ( s = path; *s; s++ ) if ( *s == '\\' ) *s = '/'; }
name = strrchr( path, '/' );
if (name)
name++; else
name = path;
ext = strrchr( path, '.' );
if ( (ext==0L) || (ext<name) ) {
if (strlen(name)==0) {
strlcat( path, "data", pathlen );
} else {
strlcat( path, ".data", pathlen );
}
} else {
*ext = 0; }
char ret = fl_make_path( path );
if (Fl::system_driver()->preferences_need_protection_check() && strncmp(path, "/etc/fltk/", 10) == 0) {
fl_chmod(path, 0755); }
strlcat( path, "/", pathlen );
return ret;
}
Fl_Preferences::Node::Node( const char *path ) {
if ( path ) path_ = fl_strdup( path ); else path_ = 0;
first_child_ = 0; next_ = 0; parent_ = 0;
entry_ = 0;
nEntry_ = NEntry_ = 0;
dirty_ = 0;
top_ = 0;
indexed_ = 0;
index_ = 0;
nIndex_ = NIndex_ = 0;
}
void Fl_Preferences::Node::deleteAllChildren() {
Node *next_node = NULL;
for ( Node *current_node = first_child_; current_node; current_node = next_node ) {
next_node = current_node->next_;
delete current_node;
}
first_child_ = NULL;
dirty_ = 1;
updateIndex();
}
void Fl_Preferences::Node::deleteAllEntries() {
if ( entry_ ) {
for ( int i = 0; i < nEntry_; i++ ) {
if ( entry_[i].name ) {
::free( entry_[i].name );
entry_[i].name = NULL;
}
if ( entry_[i].value ) {
::free( entry_[i].value );
entry_[i].value = NULL;
}
}
free( entry_ );
entry_ = NULL;
nEntry_ = 0;
NEntry_ = 0;
}
dirty_ = 1;
}
Fl_Preferences::Node::~Node() {
next_ = NULL;
parent_ = NULL;
deleteAllChildren();
deleteAllEntries();
deleteIndex();
if ( path_ ) {
::free( path_ );
path_ = NULL;
}
}
char Fl_Preferences::Node::dirty() {
if ( dirty_ ) return 1;
if ( next_ && next_->dirty() ) return 1;
if ( first_child_ && first_child_->dirty() ) return 1;
return 0;
}
void Fl_Preferences::Node::clearDirtyFlags() {
Fl_Preferences::Node *nd = this;
while (nd) {
nd->dirty_ = 0;
if ( nd->first_child_ ) nd->first_child_->clearDirtyFlags();
nd = nd->next_;
}
}
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 ) { fprintf( f, "%s:", entry_[i].name );
size_t cnt, written = 0;
for ( cnt = 0; cnt < 60; cnt++ )
if ( src[cnt]==0 ) break;
written += 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 );
written += fwrite( src, cnt, 1, f );
fputc( '\n', f );
src += cnt;
}
(void)written; }
else
fprintf( f, "%s\n", entry_[i].name );
}
if ( first_child_ ) first_child_->write( f );
dirty_ = 0;
return 0;
}
void Fl_Preferences::Node::setParent( Node *pn ) {
parent_ = pn;
next_ = pn->first_child_;
pn->first_child_ = this;
snprintf( nameBuffer, sizeof(nameBuffer), "%s/%s", pn->path_, path_ );
free( path_ );
path_ = fl_strdup( nameBuffer );
}
Fl_Preferences::RootNode *Fl_Preferences::Node::findRoot() {
Node *n = this;
do {
if (n->top_)
return n->root_node_;
n = n->parent();
} while (n);
return 0L;
}
Fl_Preferences::Node *Fl_Preferences::Node::addChild( const char *path ) {
snprintf( nameBuffer, sizeof(nameBuffer), "%s/%s", path_, path );
char *name = fl_strdup( nameBuffer );
Node *nd = find( name );
free( name );
updateIndex();
return nd;
}
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; if ( strcmp( value, entry_[i].value ) != 0 ) {
if ( entry_[i].value )
free( entry_[i].value );
entry_[i].value = fl_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 = fl_strdup( name );
entry_[ nEntry_ ].value = value?fl_strdup(value):0;
lastEntrySet = nEntry_;
nEntry_++;
dirty_ = 1;
}
void Fl_Preferences::Node::set( const char *line ) {
char dirt = dirty_;
if ( line[0]==';' || line[0]==0 || line[0]=='#' ) {
set( line, 0 );
} else {
const char *c = strchr( line, ':' );
if ( c ) {
size_t len = c-line+1;
if ( len >= sizeof( nameBuffer ) )
len = sizeof( nameBuffer );
strlcpy( nameBuffer, line, len );
set( nameBuffer, c+1 );
} else {
set( line, "" );
}
}
dirty_ = dirt;
}
void Fl_Preferences::Node::add( const char *line ) {
if ( lastEntrySet<0 || lastEntrySet>=nEntry_ ) return;
char *&dst = entry_[ lastEntrySet ].value;
size_t a = strlen( dst );
size_t b = strlen( line );
dst = (char*)realloc( dst, a+b+1 );
memcpy( dst+a, line, b+1 );
}
const char *Fl_Preferences::Node::get( const char *name ) {
int i = getEntry( name );
return i>=0 ? entry_[i].value : 0 ;
}
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;
}
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;
}
Fl_Preferences::Node *Fl_Preferences::Node::find( const char *path ) {
int len = (int) strlen( path_ );
if ( strncmp( path, path_, len ) == 0 ) {
if ( path[ len ] == 0 )
return this;
if ( path[ len ] == '/' ) {
Node *nd;
for ( nd = first_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 );
dirty_ = 1;
return nd->find( path );
}
}
return 0;
}
Fl_Preferences::Node *Fl_Preferences::Node::search( const char *path, int offset ) {
if ( offset == 0 ) {
if ( path[0] == '.' ) {
if ( path[1] == 0 ) {
return this; } else if ( path[1] == '/' ) {
Node *nn = this;
while ( nn->parent() ) nn = nn->parent();
if ( path[2]==0 ) { return nn;
}
return nn->search( path+2, 2 ); }
}
offset = (int) strlen( path_ ) + 1;
}
int len = (int) 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 = first_child_; nd; nd = nd->next_ ) {
Node *nn = nd->search( path, offset );
if ( nn ) return nn;
}
return 0;
}
}
return 0;
}
int Fl_Preferences::Node::nChildren() {
if (indexed_) {
return nIndex_;
} else {
int cnt = 0;
for ( Node *nd = first_child_; nd; nd = nd->next_ )
cnt++;
return cnt;
}
}
const char *Fl_Preferences::Node::name() {
if ( path_ ) {
char *r = strrchr( path_, '/' );
return r ? r+1 : path_ ;
} else {
return 0L ;
}
}
const char *Fl_Preferences::Node::child( int ix ) {
Node *nd = childNode( ix );
if ( nd )
return nd->name();
else
return 0L ;
}
Fl_Preferences::Node *Fl_Preferences::Node::childNode( int ix ) {
createIndex();
if (indexed_) {
return index_[ix];
} else {
int n = nChildren();
ix = n - ix -1;
Node *nd;
for ( nd = first_child_; nd; nd = nd->next_ ) {
if ( !ix-- ) break;
if ( !nd ) break;
}
return nd;
}
}
char Fl_Preferences::Node::remove() {
Node *nd = NULL, *np = NULL;
Node *parent_node = parent();
if ( parent_node ) {
nd = parent_node->first_child_; np = NULL;
for ( ; nd; np = nd, nd = nd->next_ ) {
if ( nd == this ) {
if ( np )
np->next_ = next_;
else
parent_node->first_child_ = next_;
next_ = NULL;
break;
}
}
parent_node->dirty_ = 1;
parent_node->updateIndex();
}
delete this;
return ( nd != NULL );
}
void Fl_Preferences::Node::createIndex() {
if (indexed_) return;
int n = nChildren();
if (n>NIndex_) {
NIndex_ = n + 16;
index_ = (Node**)realloc(index_, NIndex_*sizeof(Node*));
}
Node *nd;
int i = 0;
for (nd = first_child_; nd; nd = nd->next_, i++) {
index_[n-i-1] = nd;
}
nIndex_ = n;
indexed_ = 1;
}
void Fl_Preferences::Node::updateIndex() {
indexed_ = 0;
}
void Fl_Preferences::Node::deleteIndex() {
if (index_)
::free(index_);
index_ = NULL;
NIndex_ = nIndex_ = 0;
indexed_ = 0;
}
Fl_Plugin::Fl_Plugin(const char *klass, const char *name)
: id(0) {
#ifdef FL_PLUGIN_VERBOSE
printf("Fl_Plugin: creating a plugin, class \"%s\", name \"%s\"\n",
klass, name);
#endif
Fl_Plugin_Manager pm(klass);
id = pm.addPlugin(name, this);
}
Fl_Plugin::~Fl_Plugin() {
#ifdef FL_PLUGIN_VERBOSE
printf("Fl_Plugin: deleting a plugin\n");
#endif
if (id)
Fl_Plugin_Manager::remove(id);
}
Fl_Plugin_Manager::Fl_Plugin_Manager(const char *klass)
: Fl_Preferences(0, Fl_Preferences::Name("%s/%s", "plugins", klass)) {
#ifdef FL_PLUGIN_VERBOSE
printf("Fl_Plugin: creating a plugin manager for class \"%s\"\n", klass);
#endif
}
Fl_Plugin_Manager::~Fl_Plugin_Manager() {
#ifdef FL_PLUGIN_VERBOSE
printf("Fl_Plugin: deleting a plugin manager\n");
#endif
}
static unsigned char x2i(char hi, char lo) {
return ((hi-'A')<<4) | (lo-'A');
}
static void i2x(unsigned char v, char *d) {
d[0] = ((v>>4)&0x0f)+'A'; d[1] = (v&0x0f)+'A';
}
static void *a2p(const char *s) {
union { void *ret; unsigned char d[sizeof(void*)]; } v;
v.ret = 0L;
int i=0, n=sizeof(void*);
for (i=0; i<n; i++) {
v.d[i] = x2i(s[2*i], s[2*i+1]);
}
return v.ret;
}
static void p2a(void *vp, char *d) {
union { void *vp; unsigned char s[sizeof(void*)]; } v;
v.vp = vp;
int i=0, n=sizeof(void*);
for (i=0; i<n; i++) {
i2x(v.s[i], d+i*2);
}
d[2*i] = 0;
}
Fl_Plugin *Fl_Plugin_Manager::plugin(int index) {
char buf[34];
Fl_Plugin *ret = 0;
Fl_Preferences pin(this, index);
pin.get("address", buf, "", 34);
if (buf[0]=='@') ret = (Fl_Plugin*)a2p(buf+1);
#ifdef FL_PLUGIN_VERBOSE
printf("Fl_Plugin: returning plugin at index %d: (%s) %p\n", index, buf, ret);
#endif
return ret;
}
Fl_Plugin *Fl_Plugin_Manager::plugin(const char *name) {
char buf[34];
Fl_Plugin *ret = 0;
if (groupExists(name)) {
Fl_Preferences pin(this, name);
pin.get("address", buf, "", 34);
if (buf[0]=='@') ret = (Fl_Plugin*)a2p(buf+1);
#ifdef FL_PLUGIN_VERBOSE
printf("Fl_Plugin: returning plugin named \"%s\": (%s) %p\n", name, buf, ret);
#endif
return ret;
} else {
#ifdef FL_PLUGIN_VERBOSE
printf("Fl_Plugin: no plugin found named \"%s\"\n", name);
#endif
return 0L;
}
}
Fl_Preferences::ID Fl_Plugin_Manager::addPlugin(const char *name, Fl_Plugin *plugin) {
char buf[34];
#ifdef FL_PLUGIN_VERBOSE
printf("Fl_Plugin: adding plugin named \"%s\" at 0x%p\n", name, plugin);
#endif
Fl_Preferences pin(this, name);
buf[0] = '@'; p2a(plugin, buf+1);
pin.set("address", buf);
return pin.id();
}
void Fl_Plugin_Manager::removePlugin(Fl_Preferences::ID id) {
Fl_Preferences::remove(id);
}
int Fl_Plugin_Manager::load(const char *filename) {
void *dl = Fl::system_driver()->load(filename);
return (dl != 0) ? 0 : -1;
}
int Fl_Plugin_Manager::loadAll(const char *dirpath, const char *pattern) {
struct dirent **dir;
int i, n = fl_filename_list(dirpath, &dir);
for (i=0; i<n; i++) {
struct dirent *e = dir[i];
if (pattern==0 || fl_filename_match(e->d_name, pattern)) {
load(Fl_Preferences::Name("%s%s", dirpath, e->d_name));
}
free(e);
}
free(dir);
return 0;
}