PageRenderTime 67ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/Utilities/FLTK/src/Fl_Preferences.cxx

https://bitbucket.org/olahlou/otb
C++ | 1182 lines | 825 code | 115 blank | 242 comment | 210 complexity | 5f5a9aad1d43232efc53ee83cc4cc82d MD5 | raw file
Possible License(s): LGPL-3.0, Apache-2.0, LGPL-2.0, CC-BY-SA-3.0, BSD-3-Clause, LGPL-2.1
  1. //
  2. // "$Id$"
  3. //
  4. // Preferences methods for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 2002-2005 by Matthias Melcher.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems on the following page:
  24. //
  25. // http://www.fltk.org/str.php
  26. //
  27. #include <FL/Fl.H>
  28. #include <FL/Fl_Preferences.H>
  29. #include <FL/filename.H>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <stdarg.h>
  33. #include "flstring.h"
  34. #include <sys/stat.h>
  35. #if defined(WIN32) && !defined(__CYGWIN__)
  36. # include <direct.h>
  37. # include <io.h>
  38. // Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
  39. // on Windows, which is supposed to be POSIX compliant...
  40. # define access _access
  41. # define mkdir _mkdir
  42. #elif defined (__APPLE__)
  43. # include <Carbon/Carbon.h>
  44. # include <unistd.h>
  45. #else
  46. # include <unistd.h>
  47. #endif
  48. char Fl_Preferences::nameBuffer[128];
  49. /**
  50. * create the initial preferences base
  51. * - root: machine or user preferences
  52. * - vendor: unique identification of author or vendor of application
  53. * Must be a valid directory name.
  54. * - application: vendor unique application name, i.e. "PreferencesTest"
  55. * multiple preferences files can be created per application.
  56. * Must be a valid file name.
  57. * example: Fl_Preferences base( Fl_Preferences::USER, "fltk.org", "test01");
  58. */
  59. Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application )
  60. {
  61. node = new Node( "." );
  62. rootNode = new RootNode( this, root, vendor, application );
  63. }
  64. /**
  65. * create the initial preferences base
  66. * - path: an application-supplied path
  67. * example: Fl_Preferences base( "/usr/foo" );
  68. */
  69. Fl_Preferences::Fl_Preferences( const char *path, const char *vendor, const char *application )
  70. {
  71. node = new Node( "." );
  72. rootNode = new RootNode( this, path, vendor, application );
  73. }
  74. /**
  75. * create a Preferences node in relation to a parent node for reading and writing
  76. * - parent: base name for group
  77. * - group: group name (can contain '/' seperated group names)
  78. * example: Fl_Preferences colors( base, "setup/colors" );
  79. */
  80. Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *key )
  81. {
  82. rootNode = parent.rootNode;
  83. node = parent.node->addChild( key );
  84. }
  85. /**
  86. * create a Preferences node in relation to a parent node for reading and writing
  87. * - parent: base name for group
  88. * - group: group name (can contain '/' seperated group names)
  89. * example: Fl_Preferences colors( base, "setup/colors" );
  90. */
  91. Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *key )
  92. {
  93. rootNode = parent->rootNode;
  94. node = parent->node->addChild( key );
  95. }
  96. /**
  97. * destroy individual keys
  98. * - destroying the base preferences will flush changes to the prefs file
  99. * - after destroying the base, none of the depending preferences must be read or written
  100. */
  101. Fl_Preferences::~Fl_Preferences()
  102. {
  103. if (node && !node->parent()) delete rootNode;
  104. // DO NOT delete nodes! The root node will do that after writing the preferences
  105. // zero all pointer to avoid memory errors, event though
  106. // Valgrind does not complain (Cygwind does though)
  107. node = 0L;
  108. rootNode = 0L;
  109. }
  110. /**
  111. * return the number of groups that are contained within a group
  112. * example: int n = base.groups();
  113. */
  114. int Fl_Preferences::groups()
  115. {
  116. return node->nChildren();
  117. }
  118. /**
  119. * return the group name of the n'th group
  120. * - there is no guaranteed order of group names
  121. * - the index must be within the range given by groups()
  122. * example: printf( "Group(%d)='%s'\n", ix, base.group(ix) );
  123. */
  124. const char *Fl_Preferences::group( int ix )
  125. {
  126. return node->child( ix );
  127. }
  128. /**
  129. * return 1, if a group with this name exists
  130. * example: if ( base.groupExists( "setup/colors" ) ) ...
  131. */
  132. char Fl_Preferences::groupExists( const char *key )
  133. {
  134. return node->search( key ) ? 1 : 0 ;
  135. }
  136. /**
  137. * delete a group
  138. * example: setup.deleteGroup( "colors/buttons" );
  139. */
  140. char Fl_Preferences::deleteGroup( const char *key )
  141. {
  142. Node *nd = node->search( key );
  143. if ( nd ) return nd->remove();
  144. return 0;
  145. }
  146. /**
  147. * return the number of entries (name/value) pairs for a group
  148. * example: int n = buttonColor.entries();
  149. */
  150. int Fl_Preferences::entries()
  151. {
  152. return node->nEntry;
  153. }
  154. /**
  155. * return the name of an entry
  156. * - there is no guaranteed order of entry names
  157. * - the index must be within the range given by entries()
  158. * example: printf( "Entry(%d)='%s'\n", ix, buttonColor.entry(ix) );
  159. */
  160. const char *Fl_Preferences::entry( int ix )
  161. {
  162. return node->entry[ix].name;
  163. }
  164. /**
  165. * return 1, if an entry with this name exists
  166. * example: if ( buttonColor.entryExists( "red" ) ) ...
  167. */
  168. char Fl_Preferences::entryExists( const char *key )
  169. {
  170. return node->getEntry( key )>=0 ? 1 : 0 ;
  171. }
  172. /**
  173. * remove a single entry (name/value pair)
  174. * example: buttonColor.deleteEntry( "red" );
  175. */
  176. char Fl_Preferences::deleteEntry( const char *key )
  177. {
  178. return node->deleteEntry( key );
  179. }
  180. /**
  181. * read an entry from the group
  182. */
  183. char Fl_Preferences::get( const char *key, int &value, int defaultValue )
  184. {
  185. const char *v = node->get( key );
  186. value = v ? atoi( v ) : defaultValue;
  187. return ( v != 0 );
  188. }
  189. /**
  190. * set an entry (name/value pair)
  191. */
  192. char Fl_Preferences::set( const char *key, int value )
  193. {
  194. sprintf( nameBuffer, "%d", value );
  195. node->set( key, nameBuffer );
  196. return 1;
  197. }
  198. /**
  199. * read an entry from the group
  200. */
  201. char Fl_Preferences::get( const char *key, float &value, float defaultValue )
  202. {
  203. const char *v = node->get( key );
  204. value = v ? (float)atof( v ) : defaultValue;
  205. return ( v != 0 );
  206. }
  207. /**
  208. * set an entry (name/value pair)
  209. */
  210. char Fl_Preferences::set( const char *key, float value )
  211. {
  212. sprintf( nameBuffer, "%g", value );
  213. node->set( key, nameBuffer );
  214. return 1;
  215. }
  216. /**
  217. * set an entry (name/value pair)
  218. */
  219. char Fl_Preferences::set( const char *key, float value, int precision )
  220. {
  221. sprintf( nameBuffer, "%.*g", precision, value );
  222. node->set( key, nameBuffer );
  223. return 1;
  224. }
  225. /**
  226. * read an entry from the group
  227. */
  228. char Fl_Preferences::get( const char *key, double &value, double defaultValue )
  229. {
  230. const char *v = node->get( key );
  231. value = v ? atof( v ) : defaultValue;
  232. return ( v != 0 );
  233. }
  234. /**
  235. * set an entry (name/value pair)
  236. */
  237. char Fl_Preferences::set( const char *key, double value )
  238. {
  239. sprintf( nameBuffer, "%g", value );
  240. node->set( key, nameBuffer );
  241. return 1;
  242. }
  243. /**
  244. * set an entry (name/value pair)
  245. */
  246. char Fl_Preferences::set( const char *key, double value, int precision )
  247. {
  248. sprintf( nameBuffer, "%.*g", precision, value );
  249. node->set( key, nameBuffer );
  250. return 1;
  251. }
  252. // remove control sequences from a string
  253. static char *decodeText( const char *src )
  254. {
  255. int len = 0;
  256. const char *s = src;
  257. for ( ; *s; s++, len++ )
  258. {
  259. if ( *s == '\\' )
  260. if ( isdigit( s[1] ) ) s+=3; else s+=1;
  261. }
  262. char *dst = (char*)malloc( len+1 ), *d = dst;
  263. for ( s = src; *s; s++ )
  264. {
  265. char c = *s;
  266. if ( c == '\\' )
  267. {
  268. if ( s[1] == '\\' ) { *d++ = c; s++; }
  269. else if ( s[1] == 'n' ) { *d++ = '\n'; s++; }
  270. else if ( s[1] == 'r' ) { *d++ = '\r'; s++; }
  271. else if ( isdigit( s[1] ) ) { *d++ = ((s[1]-'0')<<6) + ((s[2]-'0')<<3) + (s[3]-'0'); s+=3; }
  272. else s++; // error
  273. }
  274. else
  275. *d++ = c;
  276. }
  277. *d = 0;
  278. return dst;
  279. }
  280. /**
  281. * read a text entry from the group
  282. * the text will be moved into the given text buffer
  283. * text will be clipped to the buffer size
  284. */
  285. char Fl_Preferences::get( const char *key, char *text, const char *defaultValue, int maxSize )
  286. {
  287. const char *v = node->get( key );
  288. if ( v && strchr( v, '\\' ) ) {
  289. char *w = decodeText( v );
  290. strlcpy(text, w, maxSize);
  291. free( w );
  292. return 1;
  293. }
  294. if ( !v ) v = defaultValue;
  295. if ( v ) strlcpy(text, v, maxSize);
  296. else text = 0;
  297. return ( v != defaultValue );
  298. }
  299. /**
  300. * read a text entry from the group
  301. * 'text' will be changed to point to a new text buffer
  302. * the text buffer must be deleted with 'free(text)' by the user.
  303. */
  304. char Fl_Preferences::get( const char *key, char *&text, const char *defaultValue )
  305. {
  306. const char *v = node->get( key );
  307. if ( v && strchr( v, '\\' ) )
  308. {
  309. text = decodeText( v );
  310. return 1;
  311. }
  312. if ( !v ) v = defaultValue;
  313. if ( v )
  314. text = strdup( v );
  315. else
  316. text = 0;
  317. return ( v != defaultValue );
  318. }
  319. /**
  320. * set an entry (name/value pair)
  321. */
  322. char Fl_Preferences::set( const char *key, const char *text )
  323. {
  324. const char *s = text ? text : "";
  325. int n=0, ns=0;
  326. for ( ; *s; s++ ) { n++; if ( *s<32 || *s=='\\' || *s==0x7f ) ns+=4; }
  327. if ( ns )
  328. {
  329. char *buffer = (char*)malloc( n+ns+1 ), *d = buffer;
  330. for ( s=text; *s; )
  331. {
  332. char c = *s;
  333. if ( c=='\\' ) { *d++ = '\\'; *d++ = '\\'; s++; }
  334. else if ( c=='\n' ) { *d++ = '\\'; *d++ = 'n'; s++; }
  335. else if ( c=='\r' ) { *d++ = '\\'; *d++ = 'r'; s++; }
  336. else if ( c<32 || c==0x7f )
  337. { *d++ = '\\'; *d++ = '0'+((c>>6)&3); *d++ = '0'+((c>>3)&7); *d++ = '0'+(c&7); s++; }
  338. else *d++ = *s++;
  339. }
  340. *d = 0;
  341. node->set( key, buffer );
  342. free( buffer );
  343. }
  344. else
  345. node->set( key, text );
  346. return 1;
  347. }
  348. // convert a hex string to binary data
  349. static void *decodeHex( const char *src, int &size )
  350. {
  351. size = strlen( src )/2;
  352. unsigned char *data = (unsigned char*)malloc( size ), *d = data;
  353. const char *s = src;
  354. int i;
  355. for ( i=size; i>0; i-- )
  356. {
  357. int v;
  358. char x = tolower(*s++);
  359. if ( x >= 'a' ) v = x-'a'+10; else v = x-'0';
  360. v = v<<4;
  361. x = tolower(*s++);
  362. if ( x >= 'a' ) v += x-'a'+10; else v += x-'0';
  363. *d++ = (uchar)v;
  364. }
  365. return (void*)data;
  366. }
  367. /**
  368. * read a binary entry from the group
  369. * the data will be moved into the given destination buffer
  370. * data will be clipped to the buffer size
  371. */
  372. char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int maxSize )
  373. {
  374. const char *v = node->get( key );
  375. if ( v )
  376. {
  377. int dsize;
  378. void *w = decodeHex( v, dsize );
  379. memmove( data, w, dsize>maxSize?maxSize:dsize );
  380. free( w );
  381. return 1;
  382. }
  383. if ( defaultValue )
  384. memmove( data, defaultValue, defaultSize>maxSize?maxSize:defaultSize );
  385. return 0;
  386. }
  387. /**
  388. * read a binary entry from the group
  389. * 'data' will be changed to point to a new data buffer
  390. * the data buffer must be deleted with 'free(data)' by the user.
  391. */
  392. char Fl_Preferences::get( const char *key, void *&data, const void *defaultValue, int defaultSize )
  393. {
  394. const char *v = node->get( key );
  395. if ( v )
  396. {
  397. int dsize;
  398. data = decodeHex( v, dsize );
  399. return 1;
  400. }
  401. if ( defaultValue )
  402. {
  403. data = (void*)malloc( defaultSize );
  404. memmove( data, defaultValue, defaultSize );
  405. }
  406. else
  407. data = 0;
  408. return 0;
  409. }
  410. /**
  411. * set an entry (name/value pair)
  412. */
  413. char Fl_Preferences::set( const char *key, const void *data, int dsize )
  414. {
  415. char *buffer = (char*)malloc( dsize*2+1 ), *d = buffer;;
  416. unsigned char *s = (unsigned char*)data;
  417. for ( ; dsize>0; dsize-- )
  418. {
  419. static char lu[] = "0123456789abcdef";
  420. unsigned char v = *s++;
  421. *d++ = lu[v>>4];
  422. *d++ = lu[v&0xf];
  423. }
  424. *d = 0;
  425. node->set( key, buffer );
  426. free( buffer );
  427. return 1;
  428. }
  429. /**
  430. * return the size of the value part of an entry
  431. */
  432. int Fl_Preferences::size( const char *key )
  433. {
  434. const char *v = node->get( key );
  435. return v ? strlen( v ) : 0 ;
  436. }
  437. /**
  438. * creates a path that is related to the preferences file
  439. * and that is usable for application data beyond what is covered
  440. * by Fl_Preferences.
  441. * - 'getUserdataPath' actually creates the directory
  442. * - 'path' must be large enough to receive a complete file path
  443. * example:
  444. * Fl_Preferences prefs( USER, "matthiasm.com", "test" );
  445. * char path[FL_PATH_MAX];
  446. * prefs.getUserdataPath( path );
  447. * sample returns:
  448. * Win32: c:/Documents and Settings/matt/Application Data/matthiasm.com/test/
  449. * prefs: c:/Documents and Settings/matt/Application Data/matthiasm.com/test.prefs
  450. */
  451. char Fl_Preferences::getUserdataPath( char *path, int pathlen )
  452. {
  453. if ( rootNode )
  454. return rootNode->getPath( path, pathlen );
  455. return 0;
  456. }
  457. /**
  458. * write all preferences to disk
  459. * - this function works only with the base preference group
  460. * - this function is rarely used as deleting the base preferences flushes automatically
  461. */
  462. void Fl_Preferences::flush()
  463. {
  464. if ( rootNode && node->dirty() )
  465. rootNode->write();
  466. }
  467. //-----------------------------------------------------------------------------
  468. // helper class to create dynamic group and entry names on the fly
  469. //
  470. /**
  471. * create a group name or entry name on the fly
  472. * - this version creates a simple unsigned integer as an entry name
  473. * example:
  474. * int n, i;
  475. * Fl_Preferences prev( appPrefs, "PreviousFiles" );
  476. * prev.get( "n", 0 );
  477. * for ( i=0; i<n; i++ )
  478. * prev.get( Fl_Preferences::Name(i), prevFile[i], "" );
  479. */
  480. Fl_Preferences::Name::Name( unsigned int n )
  481. {
  482. data_ = (char*)malloc(20);
  483. sprintf(data_, "%u", n);
  484. }
  485. /**
  486. * create a group name or entry name on the fly
  487. * - this version creates entry names as in 'printf'
  488. * example:
  489. * int n, i;
  490. * Fl_Preferences prefs( USER, "matthiasm.com", "test" );
  491. * prev.get( "nFiles", 0 );
  492. * for ( i=0; i<n; i++ )
  493. * prev.get( Fl_Preferences::Name( "File%d", i ), prevFile[i], "" );
  494. */
  495. Fl_Preferences::Name::Name( const char *format, ... )
  496. {
  497. data_ = (char*)malloc(1024);
  498. va_list args;
  499. va_start(args, format);
  500. vsnprintf(data_, 1024, format, args);
  501. va_end(args);
  502. }
  503. // delete the name
  504. Fl_Preferences::Name::~Name()
  505. {
  506. if (data_) {
  507. free(data_);
  508. data_ = 0L;
  509. }
  510. }
  511. //-----------------------------------------------------------------------------
  512. // internal methods, do not modify or use as they will change without notice
  513. //
  514. int Fl_Preferences::Node::lastEntrySet = -1;
  515. // recursively create a path in the file system
  516. static char makePath( const char *path ) {
  517. if (access(path, 0)) {
  518. const char *s = strrchr( path, '/' );
  519. if ( !s ) return 0;
  520. int len = s-path;
  521. char *p = (char*)malloc( len+1 );
  522. memcpy( p, path, len );
  523. p[len] = 0;
  524. makePath( p );
  525. free( p );
  526. #if defined(WIN32) && !defined(__CYGWIN__)
  527. return ( mkdir( path ) == 0 );
  528. #else
  529. return ( mkdir( path, 0777 ) == 0 );
  530. #endif // WIN32 && !__CYGWIN__
  531. }
  532. return 1;
  533. }
  534. // strip the filename and create a path
  535. static void makePathForFile( const char *path )
  536. {
  537. const char *s = strrchr( path, '/' );
  538. if ( !s ) return;
  539. int len = s-path;
  540. char *p = (char*)malloc( len+1 );
  541. memcpy( p, path, len );
  542. p[len] = 0;
  543. makePath( p );
  544. free( p );
  545. }
  546. // create the root node
  547. // - construct the name of the file that will hold our preferences
  548. Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char *vendor, const char *application )
  549. {
  550. char filename[ FL_PATH_MAX ]; filename[0] = 0;
  551. #ifdef WIN32
  552. # define FLPREFS_RESOURCE "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
  553. int appDataLen = strlen(vendor) + strlen(application) + 8;
  554. DWORD type, nn;
  555. LONG err;
  556. HKEY key;
  557. switch (root) {
  558. case SYSTEM:
  559. err = RegOpenKey( HKEY_LOCAL_MACHINE, FLPREFS_RESOURCE, &key );
  560. if (err == ERROR_SUCCESS) {
  561. nn = FL_PATH_MAX - appDataLen;
  562. err = RegQueryValueEx( key, "Common AppData", 0L, &type, (BYTE*)filename, &nn );
  563. if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) )
  564. filename[0] = 0;
  565. RegCloseKey(key);
  566. }
  567. break;
  568. case USER:
  569. err = RegOpenKey( HKEY_CURRENT_USER, FLPREFS_RESOURCE, &key );
  570. if (err == ERROR_SUCCESS) {
  571. nn = FL_PATH_MAX - appDataLen;
  572. err = RegQueryValueEx( key, "AppData", 0L, &type, (BYTE*)filename, &nn );
  573. if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) )
  574. {
  575. err = RegQueryValueEx( key, "Personal", 0L, &type, (BYTE*)filename, &nn );
  576. if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) )
  577. filename[0] = 0;
  578. }
  579. RegCloseKey(key);
  580. }
  581. break;
  582. }
  583. if (!filename[0]) {
  584. strcpy(filename, "C:\\FLTK");
  585. }
  586. snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
  587. "/%s/%s.prefs", vendor, application);
  588. for (char *s = filename; *s; s++) if (*s == '\\') *s = '/';
  589. #elif defined ( __APPLE__ )
  590. FSSpec spec = { 0 };
  591. FSRef ref;
  592. OSErr err = fnfErr;
  593. switch (root) {
  594. case SYSTEM:
  595. err = FindFolder( kLocalDomain, kPreferencesFolderType,
  596. 1, &spec.vRefNum, &spec.parID );
  597. break;
  598. case USER:
  599. err = FindFolder( kUserDomain, kPreferencesFolderType,
  600. 1, &spec.vRefNum, &spec.parID );
  601. break;
  602. }
  603. FSpMakeFSRef( &spec, &ref );
  604. FSRefMakePath( &ref, (UInt8*)filename, FL_PATH_MAX );
  605. snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
  606. "/%s/%s.prefs", vendor, application );
  607. #else
  608. const char *e;
  609. switch (root) {
  610. case USER:
  611. if ((e = getenv("HOME")) != NULL) {
  612. strlcpy(filename, e, sizeof(filename));
  613. if (filename[strlen(filename)-1] != '/') {
  614. strlcat(filename, "/.fltk/", sizeof(filename));
  615. } else {
  616. strlcat(filename, ".fltk/", sizeof(filename));
  617. }
  618. break;
  619. }
  620. case SYSTEM:
  621. strcpy(filename, "/etc/fltk/");
  622. break;
  623. }
  624. snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
  625. "%s/%s.prefs", vendor, application);
  626. #endif
  627. prefs_ = prefs;
  628. filename_ = strdup(filename);
  629. vendor_ = strdup(vendor);
  630. application_ = strdup(application);
  631. read();
  632. }
  633. // create the root node
  634. // - construct the name of the file that will hold our preferences
  635. Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, const char *vendor, const char *application )
  636. {
  637. if (!vendor)
  638. vendor = "unknown";
  639. if (!application) {
  640. application = "unknown";
  641. filename_ = strdup(path);
  642. } else {
  643. char filename[ FL_PATH_MAX ]; filename[0] = 0;
  644. snprintf(filename, sizeof(filename), "%s/%s.prefs", path, application);
  645. filename_ = strdup(filename);
  646. }
  647. prefs_ = prefs;
  648. vendor_ = strdup(vendor);
  649. application_ = strdup(application);
  650. read();
  651. }
  652. // destroy the root node and all depending nodes
  653. Fl_Preferences::RootNode::~RootNode()
  654. {
  655. if ( prefs_->node->dirty() )
  656. write();
  657. if ( filename_ ) {
  658. free( filename_ );
  659. filename_ = 0L;
  660. }
  661. if ( vendor_ ) {
  662. free( vendor_ );
  663. vendor_ = 0L;
  664. }
  665. if ( application_ ) {
  666. free( application_ );
  667. application_ = 0L;
  668. }
  669. delete prefs_->node;
  670. prefs_->node = 0L;
  671. }
  672. // read a preferences file and construct the group tree and with all entry leafs
  673. int Fl_Preferences::RootNode::read()
  674. {
  675. char buf[1024];
  676. FILE *f = fopen( filename_, "rb" );
  677. if ( !f ) return 0;
  678. fgets( buf, 1024, f );
  679. fgets( buf, 1024, f );
  680. fgets( buf, 1024, f );
  681. Node *nd = prefs_->node;
  682. for (;;)
  683. {
  684. if ( !fgets( buf, 1024, f ) ) break; // EOF or Error
  685. if ( buf[0]=='[' ) // read a new group
  686. {
  687. int end = strcspn( buf+1, "]\n\r" );
  688. buf[ end+1 ] = 0;
  689. nd = prefs_->node->find( buf+1 );
  690. }
  691. else if ( buf[0]=='+' ) //
  692. { // value of previous name/value pair spans multiple lines
  693. int end = strcspn( buf+1, "\n\r" );
  694. if ( end != 0 ) // if entry is not empty
  695. {
  696. buf[ end+1 ] = 0;
  697. nd->add( buf+1 );
  698. }
  699. }
  700. else // read a name/value pair
  701. {
  702. int end = strcspn( buf, "\n\r" );
  703. if ( end != 0 ) // if entry is not empty
  704. {
  705. buf[ end ] = 0;
  706. nd->set( buf );
  707. }
  708. }
  709. }
  710. fclose( f );
  711. return 0;
  712. }
  713. // write the group tree and all entry leafs
  714. int Fl_Preferences::RootNode::write()
  715. {
  716. makePathForFile(filename_);
  717. FILE *f = fopen( filename_, "wb" );
  718. if ( !f ) return 1;
  719. fprintf( f, "; FLTK preferences file format 1.0\n" );
  720. fprintf( f, "; vendor: %s\n", vendor_ );
  721. fprintf( f, "; application: %s\n", application_ );
  722. prefs_->node->write( f );
  723. fclose( f );
  724. return 0;
  725. }
  726. // get the path to the preferences directory
  727. char Fl_Preferences::RootNode::getPath( char *path, int pathlen )
  728. {
  729. strlcpy( path, filename_, pathlen);
  730. char *s;
  731. for ( s = path; *s; s++ ) if ( *s == '\\' ) *s = '/';
  732. s = strrchr( path, '.' );
  733. if ( !s ) return 0;
  734. *s = 0;
  735. char ret = makePath( path );
  736. strcpy( s, "/" );
  737. return ret;
  738. }
  739. // create a node that represents a group
  740. // - path must be a single word, prferable alnum(), dot and underscore only. Space is ok.
  741. Fl_Preferences::Node::Node( const char *path )
  742. {
  743. if ( path ) path_ = strdup( path ); else path_ = 0;
  744. child_ = 0; next_ = 0; parent_ = 0;
  745. entry = 0;
  746. nEntry = NEntry = 0;
  747. dirty_ = 0;
  748. }
  749. // delete this and all depending nodes
  750. Fl_Preferences::Node::~Node()
  751. {
  752. Node *nx;
  753. for ( Node *nd = child_; nd; nd = nx )
  754. {
  755. nx = nd->next_;
  756. delete nd;
  757. }
  758. child_ = 0L;
  759. if ( entry )
  760. {
  761. for ( int i = 0; i < nEntry; i++ )
  762. {
  763. if ( entry[i].name ) {
  764. free( entry[i].name );
  765. entry[i].name = 0L;
  766. }
  767. if ( entry[i].value ) {
  768. free( entry[i].value );
  769. entry[i].value = 0L;
  770. }
  771. }
  772. free( entry );
  773. entry = 0L;
  774. nEntry = 0;
  775. }
  776. if ( path_ ) {
  777. free( path_ );
  778. path_ = 0L;
  779. }
  780. next_ = 0L;
  781. parent_ = 0L;
  782. }
  783. // recursively check if any entry is dirty (was changed after loading a fresh prefs file)
  784. char Fl_Preferences::Node::dirty()
  785. {
  786. if ( dirty_ ) return 1;
  787. if ( next_ && next_->dirty() ) return 1;
  788. if ( child_ && child_->dirty() ) return 1;
  789. return 0;
  790. }
  791. // write this node (recursively from the last neighbor back to this)
  792. // write all entries
  793. // write all children
  794. int Fl_Preferences::Node::write( FILE *f )
  795. {
  796. if ( next_ ) next_->write( f );
  797. fprintf( f, "\n[%s]\n\n", path_ );
  798. for ( int i = 0; i < nEntry; i++ )
  799. {
  800. char *src = entry[i].value;
  801. if ( src )
  802. { // hack it into smaller pieces if needed
  803. fprintf( f, "%s:", entry[i].name );
  804. int cnt;
  805. for ( cnt = 0; cnt < 60; cnt++ )
  806. if ( src[cnt]==0 ) break;
  807. fwrite( src, cnt, 1, f );
  808. fprintf( f, "\n" );
  809. src += cnt;
  810. for (;*src;)
  811. {
  812. for ( cnt = 0; cnt < 80; cnt++ )
  813. if ( src[cnt]==0 ) break;
  814. fputc( '+', f );
  815. fwrite( src, cnt, 1, f );
  816. fputc( '\n', f );
  817. src += cnt;
  818. }
  819. }
  820. else
  821. fprintf( f, "%s\n", entry[i].name );
  822. }
  823. if ( child_ ) child_->write( f );
  824. dirty_ = 0;
  825. return 0;
  826. }
  827. // set the parent node and create the full path
  828. void Fl_Preferences::Node::setParent( Node *pn )
  829. {
  830. parent_ = pn;
  831. next_ = pn->child_;
  832. pn->child_ = this;
  833. sprintf( nameBuffer, "%s/%s", pn->path_, path_ );
  834. free( path_ );
  835. path_ = strdup( nameBuffer );
  836. }
  837. // add a child to this node and set its path (try to find it first...)
  838. Fl_Preferences::Node *Fl_Preferences::Node::addChild( const char *path )
  839. {
  840. sprintf( nameBuffer, "%s/%s", path_, path );
  841. char *name = strdup( nameBuffer );
  842. Node *nd = find( name );
  843. free( name );
  844. dirty_ = 1;
  845. return nd;
  846. }
  847. // create and set, or change an entry within this node
  848. void Fl_Preferences::Node::set( const char *name, const char *value )
  849. {
  850. for ( int i=0; i<nEntry; i++ )
  851. {
  852. if ( strcmp( name, entry[i].name ) == 0 )
  853. {
  854. if ( !value ) return; // annotation
  855. if ( strcmp( value, entry[i].value ) != 0 )
  856. {
  857. if ( entry[i].value )
  858. free( entry[i].value );
  859. entry[i].value = strdup( value );
  860. dirty_ = 1;
  861. }
  862. lastEntrySet = i;
  863. return;
  864. }
  865. }
  866. if ( NEntry==nEntry )
  867. {
  868. NEntry = NEntry ? NEntry*2 : 10;
  869. entry = (Entry*)realloc( entry, NEntry * sizeof(Entry) );
  870. }
  871. entry[ nEntry ].name = strdup( name );
  872. entry[ nEntry ].value = value?strdup( value ):0;
  873. lastEntrySet = nEntry;
  874. nEntry++;
  875. dirty_ = 1;
  876. }
  877. // create or set a value (or annotation) from a single line in the file buffer
  878. void Fl_Preferences::Node::set( const char *line )
  879. {
  880. // hmm. If we assume that we always read this file in the beginning,
  881. // we can handle the dirty flag 'quick and dirty'
  882. char dirt = dirty_;
  883. if ( line[0]==';' || line[0]==0 || line[0]=='#' )
  884. {
  885. set( line, 0 );
  886. }
  887. else
  888. {
  889. const char *c = strchr( line, ':' );
  890. if ( c )
  891. {
  892. unsigned int len = c-line+1;
  893. if ( len >= sizeof( nameBuffer ) )
  894. len = sizeof( nameBuffer );
  895. strlcpy( nameBuffer, line, len );
  896. set( nameBuffer, c+1 );
  897. }
  898. else
  899. set( line, "" );
  900. }
  901. dirty_ = dirt;
  902. }
  903. // add more data to an existing entry
  904. void Fl_Preferences::Node::add( const char *line )
  905. {
  906. if ( lastEntrySet<0 || lastEntrySet>=nEntry ) return;
  907. char *&dst = entry[ lastEntrySet ].value;
  908. int a = strlen( dst );
  909. int b = strlen( line );
  910. dst = (char*)realloc( dst, a+b+1 );
  911. memcpy( dst+a, line, b+1 );
  912. dirty_ = 1;
  913. }
  914. // get the value for a name, returns 0 if no such name
  915. const char *Fl_Preferences::Node::get( const char *name )
  916. {
  917. int i = getEntry( name );
  918. return i>=0 ? entry[i].value : 0 ;
  919. }
  920. // find the index of an entry, returns -1 if no such entry
  921. int Fl_Preferences::Node::getEntry( const char *name )
  922. {
  923. for ( int i=0; i<nEntry; i++ )
  924. {
  925. if ( strcmp( name, entry[i].name ) == 0 )
  926. {
  927. return i;
  928. }
  929. }
  930. return -1;
  931. }
  932. // remove one entry form this group
  933. char Fl_Preferences::Node::deleteEntry( const char *name )
  934. {
  935. int ix = getEntry( name );
  936. if ( ix == -1 ) return 0;
  937. memmove( entry+ix, entry+ix+1, (nEntry-ix-1) * sizeof(Entry) );
  938. nEntry--;
  939. dirty_ = 1;
  940. return 1;
  941. }
  942. // find a group somewhere in the tree starting here
  943. // - this method will always return a valid node (except for memory allocation problems)
  944. // - if the node was not found, 'find' will create the required branch
  945. Fl_Preferences::Node *Fl_Preferences::Node::find( const char *path )
  946. {
  947. int len = strlen( path_ );
  948. if ( strncmp( path, path_, len ) == 0 )
  949. {
  950. if ( path[ len ] == 0 )
  951. return this;
  952. if ( path[ len ] == '/' )
  953. {
  954. Node *nd;
  955. for ( nd = child_; nd; nd = nd->next_ )
  956. {
  957. Node *nn = nd->find( path );
  958. if ( nn ) return nn;
  959. }
  960. const char *s = path+len+1;
  961. const char *e = strchr( s, '/' );
  962. if (e) strlcpy( nameBuffer, s, e-s+1 );
  963. else strlcpy( nameBuffer, s, sizeof(nameBuffer));
  964. nd = new Node( nameBuffer );
  965. nd->setParent( this );
  966. return nd->find( path );
  967. }
  968. }
  969. return 0;
  970. }
  971. // find a group somewhere in the tree starting here
  972. // caller must not set 'offset' argument
  973. // - if the node does not exist, 'search' returns NULL
  974. // - if the pathname is "." (current node) return this node
  975. // - if the pathname is "./" (root node) return the topmost node
  976. // - if the pathname starts with "./", start the search at the root node instead
  977. Fl_Preferences::Node *Fl_Preferences::Node::search( const char *path, int offset )
  978. {
  979. if ( offset == 0 )
  980. {
  981. if ( path[0] == '.' )
  982. {
  983. if ( path[1] == 0 )
  984. {
  985. return this; // user was searching for current node
  986. }
  987. else if ( path[1] == '/' )
  988. {
  989. Node *nn = this;
  990. while ( nn->parent_ ) nn = nn->parent_;
  991. if ( path[2]==0 )
  992. { // user is searching for root ( "./" )
  993. return nn;
  994. }
  995. return nn->search( path+2, 2 ); // do a relative search on the root node
  996. }
  997. }
  998. offset = strlen( path_ ) + 1;
  999. }
  1000. int len = strlen( path_ );
  1001. if ( len < offset-1 ) return 0;
  1002. len -= offset;
  1003. if ( ( len <= 0 ) || ( strncmp( path, path_+offset, len ) == 0 ) )
  1004. {
  1005. if ( len > 0 && path[ len ] == 0 )
  1006. return this;
  1007. if ( len <= 0 || path[ len ] == '/' )
  1008. {
  1009. for ( Node *nd = child_; nd; nd = nd->next_ )
  1010. {
  1011. Node *nn = nd->search( path, offset );
  1012. if ( nn ) return nn;
  1013. }
  1014. return 0;
  1015. }
  1016. }
  1017. return 0;
  1018. }
  1019. // return the number of child nodes (groups)
  1020. int Fl_Preferences::Node::nChildren()
  1021. {
  1022. int cnt = 0;
  1023. for ( Node *nd = child_; nd; nd = nd->next_ )
  1024. cnt++;
  1025. return cnt;
  1026. }
  1027. // return the n'th child node
  1028. const char *Fl_Preferences::Node::child( int ix )
  1029. {
  1030. Node *nd;
  1031. for ( nd = child_; nd; nd = nd->next_ )
  1032. {
  1033. if ( !ix-- ) break;
  1034. }
  1035. if ( nd && nd->path_ )
  1036. {
  1037. char *r = strrchr( nd->path_, '/' );
  1038. return r ? r+1 : nd->path_ ;
  1039. }
  1040. return 0L ;
  1041. }
  1042. // remove myself from the list and delete me (and all children)
  1043. char Fl_Preferences::Node::remove()
  1044. {
  1045. Node *nd = 0, *np;
  1046. if ( parent_ )
  1047. {
  1048. nd = parent_->child_; np = 0L;
  1049. for ( ; nd; np = nd, nd = nd->next_ )
  1050. {
  1051. if ( nd == this )
  1052. {
  1053. if ( np )
  1054. np->next_ = nd->next_;
  1055. else
  1056. parent_->child_ = nd->next_;
  1057. break;
  1058. }
  1059. }
  1060. parent_->dirty_ = 1;
  1061. }
  1062. delete this;
  1063. return ( nd != 0 );
  1064. }
  1065. //
  1066. // End of "$Id$".
  1067. //