PageRenderTime 68ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/fltk/fltk/Fl_Preferences.cxx

https://bitbucket.org/wieker/works
C++ | 1803 lines | 1125 code | 110 blank | 568 comment | 269 complexity | c8f64c94162172fa60b81eafc4028999 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. //
  2. // "$Id: Fl_Preferences.cxx 8291 2011-01-19 06:33:48Z manolo $"
  3. //
  4. // Preferences methods for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 2002-2010 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/Fl_Plugin.H>
  30. #include <FL/filename.H>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <stdarg.h>
  34. #include <FL/fl_utf8.h>
  35. #include "flstring.h"
  36. #include <sys/stat.h>
  37. #include <time.h>
  38. #if defined(WIN32) && !defined(__CYGWIN__)
  39. # include <windows.h>
  40. # include <direct.h>
  41. # include <io.h>
  42. // Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
  43. // on Windows, which is supposed to be POSIX compliant...
  44. # define access _access
  45. # define mkdir _mkdir
  46. #elif defined (__APPLE__)
  47. # include <ApplicationServices/ApplicationServices.h>
  48. # include <unistd.h>
  49. # include <dlfcn.h>
  50. #else
  51. # include <unistd.h>
  52. # include <dlfcn.h>
  53. #endif
  54. #ifdef WIN32
  55. # include <windows.h>
  56. # include <rpc.h>
  57. // function pointer for the UuidCreate Function
  58. // RPC_STATUS RPC_ENTRY UuidCreate(UUID __RPC_FAR *Uuid);
  59. typedef RPC_STATUS (WINAPI* uuid_func)(UUID __RPC_FAR *Uuid);
  60. #else
  61. # include <sys/time.h>
  62. #endif // WIN32
  63. #ifdef __CYGWIN__
  64. # include <wchar.h>
  65. #endif
  66. char Fl_Preferences::nameBuffer[128];
  67. char Fl_Preferences::uuidBuffer[40];
  68. Fl_Preferences *Fl_Preferences::runtimePrefs = 0;
  69. /**
  70. * Returns a UUID as generated by the system.
  71. *
  72. * A UUID is a "universally unique identifier" which is commonly used in
  73. * configuration files to create identities. A UUID in ASCII looks like this:
  74. * <tt>937C4900-51AA-4C11-8DD3-7AB59944F03E</tt>. It has always 36 bytes plus
  75. * a trailing zero.
  76. *
  77. * \return a pointer to a static buffer containing the new UUID in ASCII format.
  78. * The buffer is overwritten during every call to this function!
  79. */
  80. const char *Fl_Preferences::newUUID() {
  81. #ifdef __APPLE__
  82. CFUUIDRef theUUID = CFUUIDCreate(NULL);
  83. CFUUIDBytes b = CFUUIDGetUUIDBytes(theUUID);
  84. sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  85. b.byte0, b.byte1, b.byte2, b.byte3, b.byte4, b.byte5, b.byte6, b.byte7,
  86. b.byte8, b.byte9, b.byte10, b.byte11, b.byte12, b.byte13, b.byte14, b.byte15);
  87. CFRelease(theUUID);
  88. #elif defined (WIN32)
  89. // First try and use the win API function UuidCreate(), but if that is not
  90. // available, fall back to making something up from scratch.
  91. // We do not want to link against the Rpcrt4.dll, as we will rarely use it,
  92. // so we load the DLL dynamically, if it is available, and work from there.
  93. static HMODULE hMod = NULL;
  94. UUID ud;
  95. UUID *pu = &ud;
  96. int got_uuid = 0;
  97. if (!hMod) { // first time in?
  98. hMod = LoadLibrary("Rpcrt4.dll");
  99. }
  100. if (hMod) { // do we have a usable handle to Rpcrt4.dll?
  101. uuid_func uuid_crt = (uuid_func)GetProcAddress(hMod, "UuidCreate");
  102. if (uuid_crt != NULL) {
  103. RPC_STATUS rpc_res = uuid_crt(pu);
  104. if ( // is the return status OK for our needs?
  105. (rpc_res == RPC_S_OK) || // all is well
  106. (rpc_res == RPC_S_UUID_LOCAL_ONLY) || // only unique to this machine
  107. (rpc_res == RPC_S_UUID_NO_ADDRESS) // probably only locally unique
  108. ) {
  109. got_uuid = -1;
  110. sprintf(uuidBuffer, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  111. pu->Data1, pu->Data2, pu->Data3, pu->Data4[0], pu->Data4[1],
  112. pu->Data4[2], pu->Data4[3], pu->Data4[4],
  113. pu->Data4[5], pu->Data4[6], pu->Data4[7]);
  114. }
  115. }
  116. }
  117. if (got_uuid == 0) { // did not make a UUID - use fallback logic
  118. unsigned char b[16];
  119. time_t t = time(0); // first 4 byte
  120. b[0] = (unsigned char)t;
  121. b[1] = (unsigned char)(t>>8);
  122. b[2] = (unsigned char)(t>>16);
  123. b[3] = (unsigned char)(t>>24);
  124. int r = rand(); // four more bytes
  125. b[4] = (unsigned char)r;
  126. b[5] = (unsigned char)(r>>8);
  127. b[6] = (unsigned char)(r>>16);
  128. b[7] = (unsigned char)(r>>24);
  129. // Now we try to find 4 more "random" bytes. We extract the
  130. // lower 4 bytes from the address of t - it is created on the
  131. // stack so *might* be in a different place each time...
  132. // This is now done via a union to make it compile OK on 64-bit systems.
  133. union { void *pv; unsigned char a[sizeof(void*)]; } v;
  134. v.pv = (void *)(&t);
  135. // NOTE: This assume that all WinXX systems are little-endian
  136. b[8] = v.a[0];
  137. b[9] = v.a[1];
  138. b[10] = v.a[2];
  139. b[11] = v.a[3];
  140. TCHAR name[MAX_COMPUTERNAME_LENGTH + 1]; // only used to make last four bytes
  141. DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
  142. // GetComputerName() does not depend on any extra libs, and returns something
  143. // analogous to gethostname()
  144. GetComputerName(name, &nSize);
  145. // use the first 4 TCHAR's of the name to create the last 4 bytes of our UUID
  146. for (int ii = 0; ii < 4; ii++) {
  147. b[12 + ii] = (unsigned char)name[ii];
  148. }
  149. sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  150. b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
  151. b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
  152. }
  153. #else
  154. // warning Unix implementation of Fl_Preferences::newUUID() incomplete!
  155. // #include <uuid/uuid.h>
  156. // void uuid_generate(uuid_t out);
  157. unsigned char b[16];
  158. time_t t = time(0); // first 4 byte
  159. b[0] = (unsigned char)t;
  160. b[1] = (unsigned char)(t>>8);
  161. b[2] = (unsigned char)(t>>16);
  162. b[3] = (unsigned char)(t>>24);
  163. int r = rand(); // four more bytes
  164. b[4] = (unsigned char)r;
  165. b[5] = (unsigned char)(r>>8);
  166. b[6] = (unsigned char)(r>>16);
  167. b[7] = (unsigned char)(r>>24);
  168. unsigned long a = (unsigned long)&t; // four more bytes
  169. b[8] = (unsigned char)a;
  170. b[9] = (unsigned char)(a>>8);
  171. b[10] = (unsigned char)(a>>16);
  172. b[11] = (unsigned char)(a>>24);
  173. // Now we try to find 4 more "random" bytes. We extract the
  174. // lower 4 bytes from the address of t - it is created on the
  175. // stack so *might* be in a different place each time...
  176. // This is now done via a union to make it compile OK on 64-bit systems.
  177. union { void *pv; unsigned char a[sizeof(void*)]; } v;
  178. v.pv = (void *)(&t);
  179. // NOTE: May need to handle big- or little-endian systems here
  180. # if WORDS_BIGENDIAN
  181. b[8] = v.a[sizeof(void*) - 1];
  182. b[9] = v.a[sizeof(void*) - 2];
  183. b[10] = v.a[sizeof(void*) - 3];
  184. b[11] = v.a[sizeof(void*) - 4];
  185. # else /* data ordered for a little-endian system */
  186. b[8] = v.a[0];
  187. b[9] = v.a[1];
  188. b[10] = v.a[2];
  189. b[11] = v.a[3];
  190. # endif
  191. char name[80]; // last four bytes
  192. gethostname(name, 79);
  193. memcpy(b+12, name, 4);
  194. sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  195. b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
  196. b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
  197. #endif
  198. return uuidBuffer;
  199. }
  200. /**
  201. The constructor creates a group that manages name/value pairs and
  202. child groups. Groups are ready for reading and writing at any time.
  203. The root argument is either Fl_Preferences::USER
  204. or Fl_Preferences::SYSTEM.
  205. This constructor creates the <i>base</i> instance for all
  206. following entries and reads existing databases into memory. The
  207. vendor argument is a unique text string identifying the
  208. development team or vendor of an application. A domain name or
  209. an EMail address are great unique names, e.g.
  210. "researchATmatthiasm.com" or "fltk.org". The
  211. application argument can be the working title or final
  212. name of your application. Both vendor and
  213. application must be valid relative UNIX pathnames and
  214. may contain '/'s to create deeper file structures.
  215. A set of Preferences marked "run-time" exists exactly one per application and
  216. only as long as the application runs. It can be used as a database for
  217. volatile information. FLTK uses it to register plugins at run-time.
  218. \param[in] root can be \c USER or \c SYSTEM for user specific or system wide
  219. preferences
  220. \param[in] vendor unique text describing the company or author of this file
  221. \param[in] application unique text describing the application
  222. */
  223. Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application ) {
  224. node = new Node( "." );
  225. rootNode = new RootNode( this, root, vendor, application );
  226. node->setRoot(rootNode);
  227. }
  228. /**
  229. \brief Use this constructor to create or read a preferences file at an
  230. arbitrary position in the file system.
  231. The file name is generated in the form
  232. <tt><i>path</i>/<i>application</i>.prefs</tt>. If \p application
  233. is \c NULL, \p path must contain the full file name.
  234. \param[in] path path to the directory that contains the preferences file
  235. \param[in] vendor unique text describing the company or author of this file
  236. \param[in] application unique text describing the application
  237. */
  238. Fl_Preferences::Fl_Preferences( const char *path, const char *vendor, const char *application ) {
  239. node = new Node( "." );
  240. rootNode = new RootNode( this, path, vendor, application );
  241. node->setRoot(rootNode);
  242. }
  243. /**
  244. \brief Generate or read a new group of entries within another group.
  245. Use the \p group argument to name the group that you would like to access.
  246. \p Group can also contain a path to a group further down the hierarchy by
  247. separating group names with a forward slash '/'.
  248. \param[in] parent reference object for the new group
  249. \param[in] group name of the group to access (may contain '/'s)
  250. */
  251. Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *group ) {
  252. rootNode = parent.rootNode;
  253. node = parent.node->addChild( group );
  254. }
  255. /**
  256. \brief Create or access a group of preferences using a name.
  257. \param[in] parent the parameter parent is a pointer to the parent group.
  258. \p Parent may be \p NULL. It then refers to an application internal
  259. database which exists only once, and remains in RAM only until the
  260. application quits. This database is used to manage plugins and other
  261. data indexes by strings.
  262. \param[in] group a group name that is used as a key into the database
  263. \see Fl_Preferences( Fl_Preferences&, const char *group )
  264. */
  265. Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group ) {
  266. if (parent==0) {
  267. if (!runtimePrefs) {
  268. runtimePrefs = new Fl_Preferences();
  269. runtimePrefs->node = new Node( "." );
  270. runtimePrefs->rootNode = new RootNode( runtimePrefs );
  271. runtimePrefs->node->setRoot(rootNode);
  272. }
  273. parent = runtimePrefs;
  274. }
  275. rootNode = parent->rootNode;
  276. node = parent->node->addChild( group );
  277. }
  278. /**
  279. \brief Open a child group using a given index.
  280. Use the \p groupIndex argument to find the group that you would like to access.
  281. If the given index is invalid (negative or too high), a new group is created
  282. with a UUID as a name.
  283. The index needs to be fixed. It is currently backward. Index 0 points
  284. to the last member in the 'list' of preferences.
  285. \param[in] parent reference object for the new group
  286. \param[in] groupIndex zero based index into child groups
  287. */
  288. Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, int groupIndex ) {
  289. rootNode = parent.rootNode;
  290. if (groupIndex<0 || groupIndex>=parent.groups()) {
  291. node = parent.node->addChild( newUUID() );
  292. } else {
  293. node = parent.node->childNode( groupIndex );
  294. }
  295. }
  296. /**
  297. \see Fl_Preferences( Fl_Preferences&, int groupIndex )
  298. */
  299. Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, int groupIndex ) {
  300. rootNode = parent->rootNode;
  301. if (groupIndex<0 || groupIndex>=parent->groups()) {
  302. node = parent->node->addChild( newUUID() );
  303. } else {
  304. node = parent->node->childNode( groupIndex );
  305. }
  306. }
  307. /**
  308. Create a new dataset access point using a dataset ID.
  309. ID's are a great way to remember shortcuts to database entries that are deeply
  310. nested in a preferences database, as long as the database root is not deleted.
  311. An ID can be retrieved from any Fl_Preferences dataset, and can then be used
  312. to create multiple new references to the same dataset.
  313. ID's can be put very helpful when put into the <tt>user_data()</tt> field of
  314. widget callbacks.
  315. */
  316. Fl_Preferences::Fl_Preferences( Fl_Preferences::ID id ) {
  317. node = (Node*)id;
  318. rootNode = node->findRoot();
  319. }
  320. /**
  321. Create another reference to a Preferences group.
  322. */
  323. Fl_Preferences::Fl_Preferences(const Fl_Preferences &rhs)
  324. : node(rhs.node),
  325. rootNode(rhs.rootNode)
  326. { }
  327. /**
  328. Assign another reference to a Preference group.
  329. */
  330. Fl_Preferences &Fl_Preferences::operator=(const Fl_Preferences &rhs) {
  331. if (&rhs != this) {
  332. node = rhs.node;
  333. rootNode = rhs.rootNode;
  334. }
  335. return *this;
  336. }
  337. /**
  338. The destructor removes allocated resources. When used on the
  339. \em base preferences group, the destructor flushes all
  340. changes to the preferences file and deletes all internal
  341. databases.
  342. The destructor does not remove any data from the database. It merely
  343. deletes your reference to the database.
  344. */
  345. Fl_Preferences::~Fl_Preferences() {
  346. if (node && !node->parent()) delete rootNode;
  347. // DO NOT delete nodes! The root node will do that after writing the preferences
  348. // zero all pointer to avoid memory errors, even though
  349. // Valgrind does not complain (Cygwind does though)
  350. node = 0L;
  351. rootNode = 0L;
  352. }
  353. /**
  354. Returns the number of groups that are contained within a group.
  355. \return 0 for no groups at all
  356. */
  357. int Fl_Preferences::groups() {
  358. return node->nChildren();
  359. }
  360. /**
  361. Returns the name of the Nth (\p num_group) group.
  362. There is no guaranteed order of group names. The index must
  363. be within the range given by groups().
  364. \param[in] num_group number indexing the requested group
  365. \return 'C' string pointer to the group name
  366. */
  367. const char *Fl_Preferences::group( int num_group ) {
  368. return node->child( num_group );
  369. }
  370. /**
  371. Returns non-zero if a group with this name exists.
  372. Group names are relative to the Preferences node and can contain a path.
  373. "." describes the current node, "./" describes the topmost node.
  374. By preceding a groupname with a "./", its path becomes relative to the topmost node.
  375. \param[in] key name of group that is searched for
  376. \return 0 if no group by that name was found
  377. */
  378. char Fl_Preferences::groupExists( const char *key ) {
  379. return node->search( key ) ? 1 : 0 ;
  380. }
  381. /**
  382. Deletes a group.
  383. Removes a group and all keys and groups within that group
  384. from the database.
  385. \param[in] group name of the group to delete
  386. \return 0 if call failed
  387. */
  388. char Fl_Preferences::deleteGroup( const char *group ) {
  389. Node *nd = node->search( group );
  390. if ( nd ) return nd->remove();
  391. return 0;
  392. }
  393. /**
  394. Delete all groups.
  395. */
  396. char Fl_Preferences::deleteAllGroups() {
  397. node->deleteAllChildren();
  398. return 1;
  399. }
  400. /**
  401. Returns the number of entries (name/value pairs) in a group.
  402. \return number of entries
  403. */
  404. int Fl_Preferences::entries() {
  405. return node->nEntry();
  406. }
  407. /**
  408. Returns the name of an entry. There is no guaranteed order of
  409. entry names. The index must be within the range given by
  410. entries().
  411. \param[in] index number indexing the requested entry
  412. \return pointer to value cstring
  413. */
  414. const char *Fl_Preferences::entry( int index ) {
  415. return node->entry(index).name;
  416. }
  417. /**
  418. Returns non-zero if an entry with this name exists.
  419. \param[in] key name of entry that is searched for
  420. \return 0 if entry was not found
  421. */
  422. char Fl_Preferences::entryExists( const char *key ) {
  423. return node->getEntry( key )>=0 ? 1 : 0 ;
  424. }
  425. /**
  426. Deletes a single name/value pair.
  427. This function removes the entry \p key from the database.
  428. \param[in] key name of entry to delete
  429. \return 0 if deleting the entry failed
  430. */
  431. char Fl_Preferences::deleteEntry( const char *key ) {
  432. return node->deleteEntry( key );
  433. }
  434. /**
  435. Delete all entries.
  436. */
  437. char Fl_Preferences::deleteAllEntries() {
  438. node->deleteAllEntries();
  439. return 1;
  440. }
  441. /**
  442. Delete all groups and all entries.
  443. */
  444. char Fl_Preferences::clear() {
  445. char ret1 = deleteAllGroups();
  446. char ret2 = deleteAllEntries();
  447. return ret1 & ret2;
  448. }
  449. /**
  450. Reads an entry from the group. A default value must be
  451. supplied. The return value indicates if the value was available
  452. (non-zero) or the default was used (0).
  453. \param[in] key name of entry
  454. \param[out] value returned from preferences or default value if none was set
  455. \param[in] defaultValue default value to be used if no preference was set
  456. \return 0 if the default value was used
  457. */
  458. char Fl_Preferences::get( const char *key, int &value, int defaultValue ) {
  459. const char *v = node->get( key );
  460. value = v ? atoi( v ) : defaultValue;
  461. return ( v != 0 );
  462. }
  463. /**
  464. Sets an entry (name/value pair). The return value indicates if there
  465. was a problem storing the data in memory. However it does not
  466. reflect if the value was actually stored in the preferences
  467. file.
  468. \param[in] key name of entry
  469. \param[in] value set this entry to \p value
  470. \return 0 if setting the value failed
  471. */
  472. char Fl_Preferences::set( const char *key, int value ) {
  473. sprintf( nameBuffer, "%d", value );
  474. node->set( key, nameBuffer );
  475. return 1;
  476. }
  477. /**
  478. Reads an entry from the group. A default value must be
  479. supplied. The return value indicates if the value was available
  480. (non-zero) or the default was used (0).
  481. \param[in] key name of entry
  482. \param[out] value returned from preferences or default value if none was set
  483. \param[in] defaultValue default value to be used if no preference was set
  484. \return 0 if the default value was used
  485. */
  486. char Fl_Preferences::get( const char *key, float &value, float defaultValue ) {
  487. const char *v = node->get( key );
  488. value = v ? (float)atof( v ) : defaultValue;
  489. return ( v != 0 );
  490. }
  491. /**
  492. Sets an entry (name/value pair). The return value indicates if there
  493. was a problem storing the data in memory. However it does not
  494. reflect if the value was actually stored in the preferences
  495. file.
  496. \param[in] key name of entry
  497. \param[in] value set this entry to \p value
  498. \return 0 if setting the value failed
  499. */
  500. char Fl_Preferences::set( const char *key, float value ) {
  501. sprintf( nameBuffer, "%g", value );
  502. node->set( key, nameBuffer );
  503. return 1;
  504. }
  505. /**
  506. Sets an entry (name/value pair). The return value indicates if there
  507. was a problem storing the data in memory. However it does not
  508. reflect if the value was actually stored in the preferences
  509. file.
  510. \param[in] key name of entry
  511. \param[in] value set this entry to \p value
  512. \param[in] precision number of decimal digits to represent value
  513. \return 0 if setting the value failed
  514. */
  515. char Fl_Preferences::set( const char *key, float value, int precision ) {
  516. sprintf( nameBuffer, "%.*g", precision, value );
  517. node->set( key, nameBuffer );
  518. return 1;
  519. }
  520. /**
  521. Reads an entry from the group. A default value must be
  522. supplied. The return value indicates if the value was available
  523. (non-zero) or the default was used (0).
  524. \param[in] key name of entry
  525. \param[out] value returned from preferences or default value if none was set
  526. \param[in] defaultValue default value to be used if no preference was set
  527. \return 0 if the default value was used
  528. */
  529. char Fl_Preferences::get( const char *key, double &value, double defaultValue ) {
  530. const char *v = node->get( key );
  531. value = v ? atof( v ) : defaultValue;
  532. return ( v != 0 );
  533. }
  534. /**
  535. Sets an entry (name/value pair). The return value indicates if there
  536. was a problem storing the data in memory. However it does not
  537. reflect if the value was actually stored in the preferences
  538. file.
  539. \param[in] key name of entry
  540. \param[in] value set this entry to \p value
  541. \return 0 if setting the value failed
  542. */
  543. char Fl_Preferences::set( const char *key, double value ) {
  544. sprintf( nameBuffer, "%g", value );
  545. node->set( key, nameBuffer );
  546. return 1;
  547. }
  548. /**
  549. Sets an entry (name/value pair). The return value indicates if there
  550. was a problem storing the data in memory. However it does not
  551. reflect if the value was actually stored in the preferences
  552. file.
  553. \param[in] key name of entry
  554. \param[in] value set this entry to \p value
  555. \param[in] precision number of decimal digits to represent value
  556. \return 0 if setting the value failed
  557. */
  558. char Fl_Preferences::set( const char *key, double value, int precision ) {
  559. sprintf( nameBuffer, "%.*g", precision, value );
  560. node->set( key, nameBuffer );
  561. return 1;
  562. }
  563. // remove control sequences from a string
  564. static char *decodeText( const char *src ) {
  565. int len = 0;
  566. const char *s = src;
  567. for ( ; *s; s++, len++ ) {
  568. if ( *s == '\\' ) {
  569. if ( isdigit( s[1] ) ) {
  570. s+=3;
  571. } else {
  572. s+=1;
  573. }
  574. }
  575. }
  576. char *dst = (char*)malloc( len+1 ), *d = dst;
  577. for ( s = src; *s; s++ ) {
  578. char c = *s;
  579. if ( c == '\\' ) {
  580. if ( s[1] == '\\' ) { *d++ = c; s++; }
  581. else if ( s[1] == 'n' ) { *d++ = '\n'; s++; }
  582. else if ( s[1] == 'r' ) { *d++ = '\r'; s++; }
  583. else if ( isdigit( s[1] ) ) { *d++ = ((s[1]-'0')<<6) + ((s[2]-'0')<<3) + (s[3]-'0'); s+=3; }
  584. else s++; // error
  585. }
  586. else
  587. *d++ = c;
  588. }
  589. *d = 0;
  590. return dst;
  591. }
  592. /**
  593. Reads an entry from the group. A default value must be
  594. supplied. The return value indicates if the value was available
  595. (non-zero) or the default was used (0).
  596. 'maxSize' is the maximum length of text that will be read.
  597. The text buffer must allow for one additional byte for a trailling zero.
  598. \param[in] key name of entry
  599. \param[out] text returned from preferences or default value if none was set
  600. \param[in] defaultValue default value to be used if no preference was set
  601. \param[in] maxSize maximum length of value plus one byte for a trailing zero
  602. \return 0 if the default value was used
  603. */
  604. char Fl_Preferences::get( const char *key, char *text, const char *defaultValue, int maxSize ) {
  605. const char *v = node->get( key );
  606. if ( v && strchr( v, '\\' ) ) {
  607. char *w = decodeText( v );
  608. strlcpy(text, w, maxSize);
  609. free( w );
  610. return 1;
  611. }
  612. if ( !v ) v = defaultValue;
  613. if ( v ) strlcpy(text, v, maxSize);
  614. else text = 0;
  615. return ( v != defaultValue );
  616. }
  617. /**
  618. Reads an entry from the group. A default value must be
  619. supplied. The return value indicates if the value was available
  620. (non-zero) or the default was used (0). get() allocates memory of
  621. sufficient size to hold the value. The buffer must be free'd by
  622. the developer using 'free(value)'.
  623. \param[in] key name of entry
  624. \param[out] text returned from preferences or default value if none was set
  625. \param[in] defaultValue default value to be used if no preference was set
  626. \return 0 if the default value was used
  627. */
  628. char Fl_Preferences::get( const char *key, char *&text, const char *defaultValue ) {
  629. const char *v = node->get( key );
  630. if ( v && strchr( v, '\\' ) ) {
  631. text = decodeText( v );
  632. return 1;
  633. }
  634. if ( !v ) v = defaultValue;
  635. if ( v )
  636. text = strdup( v );
  637. else
  638. text = 0;
  639. return ( v != defaultValue );
  640. }
  641. /**
  642. Sets an entry (name/value pair). The return value indicates if there
  643. was a problem storing the data in memory. However it does not
  644. reflect if the value was actually stored in the preferences
  645. file.
  646. \param[in] key name of entry
  647. \param[in] text set this entry to \p value
  648. \return 0 if setting the value failed
  649. */
  650. char Fl_Preferences::set( const char *key, const char *text ) {
  651. const char *s = text ? text : "";
  652. int n=0, ns=0;
  653. for ( ; *s; s++ ) { n++; if ( *s<32 || *s=='\\' || *s==0x7f ) ns+=4; }
  654. if ( ns ) {
  655. char *buffer = (char*)malloc( n+ns+1 ), *d = buffer;
  656. for ( s=text; *s; ) {
  657. char c = *s;
  658. if ( c=='\\' ) { *d++ = '\\'; *d++ = '\\'; s++; }
  659. else if ( c=='\n' ) { *d++ = '\\'; *d++ = 'n'; s++; }
  660. else if ( c=='\r' ) { *d++ = '\\'; *d++ = 'r'; s++; }
  661. else if ( c<32 || c==0x7f )
  662. { *d++ = '\\'; *d++ = '0'+((c>>6)&3); *d++ = '0'+((c>>3)&7); *d++ = '0'+(c&7); s++; }
  663. else *d++ = *s++;
  664. }
  665. *d = 0;
  666. node->set( key, buffer );
  667. free( buffer );
  668. }
  669. else
  670. node->set( key, text );
  671. return 1;
  672. }
  673. // convert a hex string to binary data
  674. static void *decodeHex( const char *src, int &size ) {
  675. size = strlen( src )/2;
  676. unsigned char *data = (unsigned char*)malloc( size ), *d = data;
  677. const char *s = src;
  678. for ( int i=size; i>0; i-- ) {
  679. int v;
  680. char x = tolower(*s++);
  681. if ( x >= 'a' ) v = x-'a'+10; else v = x-'0';
  682. v = v<<4;
  683. x = tolower(*s++);
  684. if ( x >= 'a' ) v += x-'a'+10; else v += x-'0';
  685. *d++ = (uchar)v;
  686. }
  687. return (void*)data;
  688. }
  689. /**
  690. Reads an entry from the group. A default value must be
  691. supplied. The return value indicates if the value was available
  692. (non-zero) or the default was used (0).
  693. 'maxSize' is the maximum length of text that will be read.
  694. \param[in] key name of entry
  695. \param[out] data value returned from preferences or default value if none was set
  696. \param[in] defaultValue default value to be used if no preference was set
  697. \param[in] defaultSize size of default value array
  698. \param[in] maxSize maximum length of value
  699. \return 0 if the default value was used
  700. \todo maxSize should receive the number of bytes that were read.
  701. */
  702. char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int maxSize ) {
  703. const char *v = node->get( key );
  704. if ( v ) {
  705. int dsize;
  706. void *w = decodeHex( v, dsize );
  707. memmove( data, w, dsize>maxSize?maxSize:dsize );
  708. free( w );
  709. return 1;
  710. }
  711. if ( defaultValue )
  712. memmove( data, defaultValue, defaultSize>maxSize?maxSize:defaultSize );
  713. return 0;
  714. }
  715. /**
  716. Reads an entry from the group. A default value must be
  717. supplied. The return value indicates if the value was available
  718. (non-zero) or the default was used (0). get() allocates memory of
  719. sufficient size to hold the value. The buffer must be free'd by
  720. the developer using 'free(value)'.
  721. \param[in] key name of entry
  722. \param[out] data returned from preferences or default value if none was set
  723. \param[in] defaultValue default value to be used if no preference was set
  724. \param[in] defaultSize size of default value array
  725. \return 0 if the default value was used
  726. */
  727. char Fl_Preferences::get( const char *key, void *&data, const void *defaultValue, int defaultSize ) {
  728. const char *v = node->get( key );
  729. if ( v ) {
  730. int dsize;
  731. data = decodeHex( v, dsize );
  732. return 1;
  733. }
  734. if ( defaultValue ) {
  735. data = (void*)malloc( defaultSize );
  736. memmove( data, defaultValue, defaultSize );
  737. }
  738. else
  739. data = 0;
  740. return 0;
  741. }
  742. /**
  743. Sets an entry (name/value pair). The return value indicates if there
  744. was a problem storing the data in memory. However it does not
  745. reflect if the value was actually stored in the preferences
  746. file.
  747. \param[in] key name of entry
  748. \param[in] data set this entry to \p value
  749. \param[in] dsize size of data array
  750. \return 0 if setting the value failed
  751. */
  752. char Fl_Preferences::set( const char *key, const void *data, int dsize ) {
  753. char *buffer = (char*)malloc( dsize*2+1 ), *d = buffer;;
  754. unsigned char *s = (unsigned char*)data;
  755. for ( ; dsize>0; dsize-- ) {
  756. static char lu[] = "0123456789abcdef";
  757. unsigned char v = *s++;
  758. *d++ = lu[v>>4];
  759. *d++ = lu[v&0xf];
  760. }
  761. *d = 0;
  762. node->set( key, buffer );
  763. free( buffer );
  764. return 1;
  765. }
  766. /**
  767. Returns the size of the value part of an entry.
  768. \param[in] key name of entry
  769. \return size of value
  770. */
  771. int Fl_Preferences::size( const char *key ) {
  772. const char *v = node->get( key );
  773. return v ? strlen( v ) : 0 ;
  774. }
  775. /**
  776. \brief Creates a path that is related to the preferences file and
  777. that is usable for additional application data.
  778. This function creates a directory that is named after the preferences
  779. database without the \c .prefs extension and located in the same directory.
  780. It then fills the given buffer with the complete path name.
  781. Exmaple:
  782. \code
  783. Fl_Preferences prefs( USER, "matthiasm.com", "test" );
  784. char path[FL_PATH_MAX];
  785. prefs.getUserdataPath( path );
  786. \endcode
  787. creates the preferences database in (MS Windows):
  788. \code
  789. c:/Documents and Settings/matt/Application Data/matthiasm.com/test.prefs
  790. \endcode
  791. and returns the userdata path:
  792. \code
  793. c:/Documents and Settings/matt/Application Data/matthiasm.com/test/
  794. \endcode
  795. \param[out] path buffer for user data path
  796. \param[in] pathlen size of path buffer (should be at least \c FL_PATH_MAX)
  797. \return 0 if path was not created or pathname can't fit into buffer
  798. */
  799. char Fl_Preferences::getUserdataPath( char *path, int pathlen ) {
  800. if ( rootNode )
  801. return rootNode->getPath( path, pathlen );
  802. return 0;
  803. }
  804. /**
  805. Writes all preferences to disk. This function works only with
  806. the base preferences group. This function is rarely used as
  807. deleting the base preferences flushes automatically.
  808. */
  809. void Fl_Preferences::flush() {
  810. if ( rootNode && node->dirty() )
  811. rootNode->write();
  812. }
  813. //-----------------------------------------------------------------------------
  814. // helper class to create dynamic group and entry names on the fly
  815. //
  816. /**
  817. Creates a group name or entry name on the fly.
  818. This version creates a simple unsigned integer as an entry name.
  819. \code
  820. int n, i;
  821. Fl_Preferences prev( appPrefs, "PreviousFiles" );
  822. prev.get( "n", 0 );
  823. for ( i=0; i<n; i++ )
  824. prev.get( Fl_Preferences::Name(i), prevFile[i], "" );
  825. \endcode
  826. */
  827. Fl_Preferences::Name::Name( unsigned int n ) {
  828. data_ = (char*)malloc(20);
  829. sprintf(data_, "%u", n);
  830. }
  831. /**
  832. Creates a group name or entry name on the fly.
  833. This version creates entry names as in 'printf'.
  834. \code
  835. int n, i;
  836. Fl_Preferences prefs( USER, "matthiasm.com", "test" );
  837. prev.get( "nFiles", 0 );
  838. for ( i=0; i<n; i++ )
  839. prev.get( Fl_Preferences::Name( "File%d", i ), prevFile[i], "" );
  840. \endcode
  841. */
  842. Fl_Preferences::Name::Name( const char *format, ... ) {
  843. data_ = (char*)malloc(1024);
  844. va_list args;
  845. va_start(args, format);
  846. vsnprintf(data_, 1024, format, args);
  847. va_end(args);
  848. }
  849. // delete the name
  850. Fl_Preferences::Name::~Name() {
  851. if (data_) {
  852. free(data_);
  853. data_ = 0L;
  854. }
  855. }
  856. //-----------------------------------------------------------------------------
  857. // internal methods, do not modify or use as they will change without notice
  858. //
  859. int Fl_Preferences::Node::lastEntrySet = -1;
  860. // recursively create a path in the file system
  861. static char makePath( const char *path ) {
  862. if (access(path, 0)) {
  863. const char *s = strrchr( path, '/' );
  864. if ( !s ) return 0;
  865. int len = s-path;
  866. char *p = (char*)malloc( len+1 );
  867. memcpy( p, path, len );
  868. p[len] = 0;
  869. makePath( p );
  870. free( p );
  871. #if defined(WIN32) && !defined(__CYGWIN__)
  872. return ( mkdir( path ) == 0 );
  873. #else
  874. return ( mkdir( path, 0777 ) == 0 );
  875. #endif // WIN32 && !__CYGWIN__
  876. }
  877. return 1;
  878. }
  879. #if 0
  880. // strip the filename and create a path
  881. static void makePathForFile( const char *path ) {
  882. const char *s = strrchr( path, '/' );
  883. if ( !s ) return;
  884. int len = s-path;
  885. char *p = (char*)malloc( len+1 );
  886. memcpy( p, path, len );
  887. p[len] = 0;
  888. makePath( p );
  889. free( p );
  890. }
  891. #endif
  892. // create the root node
  893. // - construct the name of the file that will hold our preferences
  894. Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char *vendor, const char *application )
  895. : prefs_(prefs),
  896. filename_(0L),
  897. vendor_(0L),
  898. application_(0L) {
  899. char filename[ FL_PATH_MAX ]; filename[0] = 0;
  900. #ifdef WIN32
  901. # define FLPREFS_RESOURCE "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
  902. # define FLPREFS_RESOURCEW L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
  903. int appDataLen = strlen(vendor) + strlen(application) + 8;
  904. DWORD type, nn;
  905. LONG err;
  906. HKEY key;
  907. switch (root) {
  908. case SYSTEM:
  909. err = RegOpenKeyW( HKEY_LOCAL_MACHINE, FLPREFS_RESOURCEW, &key );
  910. if (err == ERROR_SUCCESS) {
  911. nn = FL_PATH_MAX - appDataLen;
  912. err = RegQueryValueExW( key, L"Common AppData", 0L, &type,
  913. (BYTE*)filename, &nn );
  914. if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) ) {
  915. filename[0] = 0;
  916. filename[1] = 0;
  917. }
  918. RegCloseKey(key);
  919. }
  920. break;
  921. case USER:
  922. err = RegOpenKeyW( HKEY_CURRENT_USER, FLPREFS_RESOURCEW, &key );
  923. if (err == ERROR_SUCCESS) {
  924. nn = FL_PATH_MAX - appDataLen;
  925. err = RegQueryValueExW( key, L"AppData", 0L, &type,
  926. (BYTE*)filename, &nn );
  927. if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) ) {
  928. filename[0] = 0;
  929. filename[1] = 0;
  930. }
  931. RegCloseKey(key);
  932. }
  933. break;
  934. }
  935. if (!filename[1] && !filename[0]) {
  936. strcpy(filename, "C:\\FLTK");
  937. } else {
  938. #if 0
  939. xchar *b = (xchar*)_wcsdup((xchar *)filename);
  940. #else
  941. // cygwin does not come with _wcsdup. Use malloc + wcscpy.
  942. // For implementation of wcsdup functionality See
  943. // - http://linenum.info/p/glibc/2.7/wcsmbs/wcsdup.c
  944. xchar *b = (xchar*) malloc((wcslen((xchar *) filename) + 1) * sizeof(xchar));
  945. wcscpy(b, (xchar *) filename);
  946. #endif
  947. // filename[fl_unicode2utf(b, wcslen((xchar*)b), filename)] = 0;
  948. unsigned len = fl_utf8fromwc(filename, (FL_PATH_MAX-1), b, wcslen(b));
  949. filename[len] = 0;
  950. free(b);
  951. }
  952. snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
  953. "/%s/%s.prefs", vendor, application);
  954. for (char *s = filename; *s; s++) if (*s == '\\') *s = '/';
  955. #elif defined ( __APPLE__ )
  956. // TODO: verify that this is the Apple sanctioned way of finding these folders
  957. // (On MSWindows, this frequently leads to issues with internationalized systems)
  958. // Carbon: err = FindFolder( kLocalDomain, kPreferencesFolderType, 1, &spec.vRefNum, &spec.parID );
  959. switch (root) {
  960. case SYSTEM:
  961. strcpy(filename, "/Library/Preferences");
  962. break;
  963. case USER:
  964. sprintf(filename, "%s/Library/Preferences", fl_getenv("HOME"));
  965. break;
  966. }
  967. snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
  968. "/%s/%s.prefs", vendor, application );
  969. #else
  970. const char *e;
  971. switch (root) {
  972. case USER:
  973. if ((e = fl_getenv("HOME")) != NULL) {
  974. strlcpy(filename, e, sizeof(filename));
  975. if (filename[strlen(filename)-1] != '/') {
  976. strlcat(filename, "/.fltk/", sizeof(filename));
  977. } else {
  978. strlcat(filename, ".fltk/", sizeof(filename));
  979. }
  980. break;
  981. }
  982. case SYSTEM:
  983. strcpy(filename, "/etc/fltk/");
  984. break;
  985. }
  986. snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
  987. "%s/%s.prefs", vendor, application);
  988. #endif
  989. filename_ = strdup(filename);
  990. vendor_ = strdup(vendor);
  991. application_ = strdup(application);
  992. read();
  993. }
  994. // create the root node
  995. // - construct the name of the file that will hold our preferences
  996. Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, const char *vendor, const char *application )
  997. : prefs_(prefs),
  998. filename_(0L),
  999. vendor_(0L),
  1000. application_(0L) {
  1001. if (!vendor)
  1002. vendor = "unknown";
  1003. if (!application) {
  1004. application = "unknown";
  1005. filename_ = strdup(path);
  1006. } else {
  1007. char filename[ FL_PATH_MAX ]; filename[0] = 0;
  1008. snprintf(filename, sizeof(filename), "%s/%s.prefs", path, application);
  1009. filename_ = strdup(filename);
  1010. }
  1011. vendor_ = strdup(vendor);
  1012. application_ = strdup(application);
  1013. read();
  1014. }
  1015. // create a root node that exists only on RAM and can not be read or written to
  1016. // a file
  1017. Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs )
  1018. : prefs_(prefs),
  1019. filename_(0L),
  1020. vendor_(0L),
  1021. application_(0L) {
  1022. }
  1023. // destroy the root node and all depending nodes
  1024. Fl_Preferences::RootNode::~RootNode() {
  1025. if ( prefs_->node->dirty() )
  1026. write();
  1027. if ( filename_ ) {
  1028. free( filename_ );
  1029. filename_ = 0L;
  1030. }
  1031. if ( vendor_ ) {
  1032. free( vendor_ );
  1033. vendor_ = 0L;
  1034. }
  1035. if ( application_ ) {
  1036. free( application_ );
  1037. application_ = 0L;
  1038. }
  1039. delete prefs_->node;
  1040. prefs_->node = 0L;
  1041. }
  1042. // read a preferences file and construct the group tree and with all entry leafs
  1043. int Fl_Preferences::RootNode::read() {
  1044. if (!filename_) // RUNTIME preferences
  1045. return -1;
  1046. char buf[1024];
  1047. FILE *f = fl_fopen( filename_, "rb" );
  1048. if ( !f )
  1049. return -1;
  1050. if (fgets( buf, 1024, f )==0) { /* ignore */ }
  1051. if (fgets( buf, 1024, f )==0) { /* ignore */ }
  1052. if (fgets( buf, 1024, f )==0) { /* ignore */ }
  1053. Node *nd = prefs_->node;
  1054. for (;;) {
  1055. if ( !fgets( buf, 1024, f ) ) break; // EOF or Error
  1056. if ( buf[0]=='[' ) { // read a new group
  1057. int end = strcspn( buf+1, "]\n\r" );
  1058. buf[ end+1 ] = 0;
  1059. nd = prefs_->node->find( buf+1 );
  1060. } else if ( buf[0]=='+' ) { // value of previous name/value pair spans multiple lines
  1061. int end = strcspn( buf+1, "\n\r" );
  1062. if ( end != 0 ) { // if entry is not empty
  1063. buf[ end+1 ] = 0;
  1064. nd->add( buf+1 );
  1065. }
  1066. } else { // read a name/value pair
  1067. int end = strcspn( buf, "\n\r" );
  1068. if ( end != 0 ) { // if entry is not empty
  1069. buf[ end ] = 0;
  1070. nd->set( buf );
  1071. }
  1072. }
  1073. }
  1074. fclose( f );
  1075. return 0;
  1076. }
  1077. // write the group tree and all entry leafs
  1078. int Fl_Preferences::RootNode::write() {
  1079. if (!filename_) // RUNTIME preferences
  1080. return -1;
  1081. fl_make_path_for_file(filename_);
  1082. FILE *f = fl_fopen( filename_, "wb" );
  1083. if ( !f )
  1084. return -1;
  1085. fprintf( f, "; FLTK preferences file format 1.0\n" );
  1086. fprintf( f, "; vendor: %s\n", vendor_ );
  1087. fprintf( f, "; application: %s\n", application_ );
  1088. prefs_->node->write( f );
  1089. fclose( f );
  1090. #if !(defined(__APPLE__) || defined(WIN32))
  1091. // unix: make sure that system prefs are user-readable
  1092. if (strncmp(filename_, "/etc/fltk/", 10) == 0) {
  1093. char *p;
  1094. p = filename_ + 9;
  1095. do { // for each directory to the pref file
  1096. *p = 0;
  1097. fl_chmod(filename_, 0755); // rwxr-xr-x
  1098. *p = '/';
  1099. p = strchr(p+1, '/');
  1100. } while (p);
  1101. fl_chmod(filename_, 0644); // rw-r--r--
  1102. }
  1103. #endif
  1104. return 0;
  1105. }
  1106. // get the path to the preferences directory
  1107. char Fl_Preferences::RootNode::getPath( char *path, int pathlen ) {
  1108. if (!filename_) // RUNTIME preferences
  1109. return -1;
  1110. strlcpy( path, filename_, pathlen);
  1111. char *s;
  1112. for ( s = path; *s; s++ ) if ( *s == '\\' ) *s = '/';
  1113. s = strrchr( path, '.' );
  1114. if ( !s ) return 0;
  1115. *s = 0;
  1116. char ret = fl_make_path( path );
  1117. #if !(defined(__APPLE__) || defined(WIN32))
  1118. // unix: make sure that system prefs dir. is user-readable
  1119. if (strncmp(path, "/etc/fltk/", 10) == 0) {
  1120. fl_chmod(path, 0755); // rwxr-xr-x
  1121. }
  1122. #endif
  1123. strcpy( s, "/" );
  1124. return ret;
  1125. }
  1126. // create a node that represents a group
  1127. // - path must be a single word, prferable alnum(), dot and underscore only. Space is ok.
  1128. Fl_Preferences::Node::Node( const char *path ) {
  1129. if ( path ) path_ = strdup( path ); else path_ = 0;
  1130. child_ = 0; next_ = 0; parent_ = 0;
  1131. entry_ = 0;
  1132. nEntry_ = NEntry_ = 0;
  1133. dirty_ = 0;
  1134. top_ = 0;
  1135. indexed_ = 0;
  1136. index_ = 0;
  1137. nIndex_ = NIndex_ = 0;
  1138. }
  1139. void Fl_Preferences::Node::deleteAllChildren() {
  1140. Node *nx;
  1141. for ( Node *nd = child_; nd; nd = nx ) {
  1142. nx = nd->next_;
  1143. delete nd;
  1144. }
  1145. child_ = 0L;
  1146. dirty_ = 1;
  1147. updateIndex();
  1148. }
  1149. void Fl_Preferences::Node::deleteAllEntries() {
  1150. if ( entry_ ) {
  1151. for ( int i = 0; i < nEntry_; i++ ) {
  1152. if ( entry_[i].name ) {
  1153. free( entry_[i].name );
  1154. entry_[i].name = 0L;
  1155. }
  1156. if ( entry_[i].value ) {
  1157. free( entry_[i].value );
  1158. entry_[i].value = 0L;
  1159. }
  1160. }
  1161. free( entry_ );
  1162. entry_ = 0L;
  1163. nEntry_ = 0;
  1164. NEntry_ = 0;
  1165. }
  1166. dirty_ = 1;
  1167. }
  1168. // delete this and all depending nodes
  1169. Fl_Preferences::Node::~Node() {
  1170. deleteAllChildren();
  1171. deleteAllEntries();
  1172. deleteIndex();
  1173. if ( path_ ) {
  1174. free( path_ );
  1175. path_ = 0L;
  1176. }
  1177. next_ = 0L;
  1178. parent_ = 0L;
  1179. }
  1180. // recursively check if any entry is dirty (was changed after loading a fresh prefs file)
  1181. char Fl_Preferences::Node::dirty() {
  1182. if ( dirty_ ) return 1;
  1183. if ( next_ && next_->dirty() ) return 1;
  1184. if ( child_ && child_->dirty() ) return 1;
  1185. return 0;
  1186. }
  1187. // write this node (recursively from the last neighbor back to this)
  1188. // write all entries
  1189. // write all children
  1190. int Fl_Preferences::Node::write( FILE *f ) {
  1191. if ( next_ ) next_->write( f );
  1192. fprintf( f, "\n[%s]\n\n", path_ );
  1193. for ( int i = 0; i < nEntry_; i++ ) {
  1194. char *src = entry_[i].value;
  1195. if ( src ) { // hack it into smaller pieces if needed
  1196. fprintf( f, "%s:", entry_[i].name );
  1197. int cnt, written;
  1198. for ( cnt = 0; cnt < 60; cnt++ )
  1199. if ( src[cnt]==0 ) break;
  1200. written = fwrite( src, cnt, 1, f );
  1201. fprintf( f, "\n" );
  1202. src += cnt;
  1203. for (;*src;) {
  1204. for ( cnt = 0; cnt < 80; cnt++ )
  1205. if ( src[cnt]==0 ) break;
  1206. fputc( '+', f );
  1207. written = fwrite( src, cnt, 1, f );
  1208. fputc( '\n', f );
  1209. src += cnt;
  1210. }
  1211. }
  1212. else
  1213. fprintf( f, "%s\n", entry_[i].name );
  1214. }
  1215. if ( child_ ) child_->write( f );
  1216. dirty_ = 0;
  1217. return 0;
  1218. }
  1219. // set the parent node and create the full path
  1220. void Fl_Preferences::Node::setParent( Node *pn ) {
  1221. parent_ = pn;
  1222. next_ = pn->child_;
  1223. pn->child_ = this;
  1224. sprintf( nameBuffer, "%s/%s", pn->path_, path_ );
  1225. free( path_ );
  1226. path_ = strdup( nameBuffer );
  1227. }
  1228. // find the corresponding root node
  1229. Fl_Preferences::RootNode *Fl_Preferences::Node::findRoot() {
  1230. Node *n = this;
  1231. do {
  1232. if (n->top_)
  1233. return n->root_;
  1234. n = n->parent();
  1235. } while (n);
  1236. return 0L;
  1237. }
  1238. // add a child to this node and set its path (try to find it first...)
  1239. Fl_Preferences::Node *Fl_Preferences::Node::addChild( const char *path ) {
  1240. sprintf( nameBuffer, "%s/%s", path_, path );
  1241. char *name = strdup( nameBuffer );
  1242. Node *nd = find( name );
  1243. free( name );
  1244. dirty_ = 1;
  1245. updateIndex();
  1246. return nd;
  1247. }
  1248. // create and set, or change an entry within this node
  1249. void Fl_Preferences::Node::set( const char *name, const char *value )
  1250. {
  1251. for ( int i=0; i<nEntry_; i++ ) {
  1252. if ( strcmp( name, entry_[i].name ) == 0 ) {
  1253. if ( !value ) return; // annotation
  1254. if ( strcmp( value, entry_[i].value ) != 0 ) {
  1255. if ( entry_[i].value )
  1256. free( entry_[i].value );
  1257. entry_[i].value = strdup( value );
  1258. dirty_ = 1;
  1259. }
  1260. lastEntrySet = i;
  1261. return;
  1262. }
  1263. }
  1264. if ( NEntry_==nEntry_ ) {
  1265. NEntry_ = NEntry_ ? NEntry_*2 : 10;
  1266. entry_ = (Entry*)realloc( entry_, NEntry_ * sizeof(Entry) );
  1267. }
  1268. entry_[ nEntry_ ].name = strdup( name );
  1269. entry_[ nEntry_ ].value = value?strdup( value ):0;
  1270. lastEntrySet = nEntry_;
  1271. nEntry_++;
  1272. dirty_ = 1;
  1273. }
  1274. // create or set a value (or annotation) from a single line in the file buffer
  1275. void Fl_Preferences::Node::set( const char *line ) {
  1276. // hmm. If we assume that we always read this file in the beginning,
  1277. // we can handle the dirty flag 'quick and dirty'
  1278. char dirt = dirty_;
  1279. if ( line[0]==';' || line[0]==0 || line[0]=='#' ) {
  1280. set( line, 0 );
  1281. } else {
  1282. const char *c = strchr( line, ':' );
  1283. if ( c ) {
  1284. unsigned int len = c-line+1;
  1285. if ( len >= sizeof( nameBuffer ) )
  1286. len = sizeof( nameBuffer );
  1287. strlcpy( nameBuffer, line, len );
  1288. set( nameBuffer, c+1 );
  1289. } else {
  1290. set( line, "" );
  1291. }
  1292. }
  1293. dirty_ = dirt;
  1294. }
  1295. // add more data to an existing entry
  1296. void Fl_Preferences::Node::add( const char *line ) {
  1297. if ( lastEntrySet<0 || lastEntrySet>=nEntry_ ) return;
  1298. char *&dst = entry_[ lastEntrySet ].value;
  1299. int a = strlen( dst );
  1300. int b = strlen( line );
  1301. dst = (char*)realloc( dst, a+b+1 );
  1302. memcpy( dst+a, line, b+1 );
  1303. dirty_ = 1;
  1304. }
  1305. // get the value for a name, returns 0 if no such name
  1306. const char *Fl_Preferences::Node::get( const char *name ) {
  1307. int i = getEntry( name );
  1308. return i>=0 ? entry_[i].value : 0 ;
  1309. }
  1310. // find the index of an entry, returns -1 if no such entry
  1311. int Fl_Preferences::Node::getEntry( const char *name ) {
  1312. for ( int i=0; i<nEntry_; i++ ) {
  1313. if ( strcmp( name, entry_[i].name ) == 0 ) {
  1314. return i;
  1315. }
  1316. }
  1317. return -1;
  1318. }
  1319. // remove one entry form this group
  1320. char Fl_Preferences::Node::deleteEntry( const char *name ) {
  1321. int ix = getEntry( name );
  1322. if ( ix == -1 ) return 0;
  1323. memmove( entry_+ix, entry_+ix+1, (nEntry_-ix-1) * sizeof(Entry) );
  1324. nEntry_--;
  1325. dirty_ = 1;
  1326. return 1;
  1327. }
  1328. // find a group somewhere in the tree starting here
  1329. // - this method will always return a valid node (except for memory allocation problems)
  1330. // - if the node was not found, 'find' will create the required branch
  1331. Fl_Preferences::Node *Fl_Preferences::Node::find( const char *path ) {
  1332. int len = strlen( path_ );
  1333. if ( strncmp( path, path_, len ) == 0 ) {
  1334. if ( path[ len ] == 0 )
  1335. return this;
  1336. if ( path[ len ] == '/' ) {
  1337. Node *nd;
  1338. for ( nd = child_; nd; nd = nd->next_ ) {
  1339. Node *nn = nd->find( path );
  1340. if ( nn ) return nn;
  1341. }
  1342. const char *s = path+len+1;
  1343. const char *e = strchr( s, '/' );
  1344. if (e) strlcpy( nameBuffer, s, e-s+1 );
  1345. else strlcpy( nameBuffer, s, sizeof(nameBuffer));
  1346. nd = new Node( nameBuffer );
  1347. nd->setParent( this );
  1348. return nd->find( path );
  1349. }
  1350. }
  1351. return 0;
  1352. }
  1353. // find a group somewhere in the tree starting here
  1354. // caller must not set 'offset' argument
  1355. // - if the node does not exist, 'search' returns NULL
  1356. // - if the pathname is "." (current node) return this node
  1357. // - if the pathname is "./" (root node) return the topmost node
  1358. // - if the pathname starts with "./", start the search at the root node instead
  1359. Fl_Preferences::Node *Fl_Preferences::Node::search( const char *path, int offset ) {
  1360. if ( offset == 0 ) {
  1361. if ( path[0] == '.' ) {
  1362. if ( path[1] == 0 ) {
  1363. return this; // user was searching for current node
  1364. } else if ( path[1] == '/' ) {
  1365. Node *nn = this;
  1366. while ( nn->parent() ) nn = nn->parent();
  1367. if ( path[2]==0 ) { // user is searching for root ( "./" )
  1368. return nn;
  1369. }
  1370. return nn->search( path+2, 2 ); // do a relative search on the root node
  1371. }
  1372. }
  1373. offset = strlen( path_ ) + 1;
  1374. }
  1375. int len = strlen( path_ );
  1376. if ( len < offset-1 ) return 0;
  1377. len -= offset;
  1378. if ( ( len <= 0 ) || ( strncmp( path, path_+offset, len ) == 0 ) ) {
  1379. if ( len > 0 && path[ len ] == 0 )
  1380. return this;
  1381. if ( len <= 0 || path[ len ] == '/' ) {
  1382. for ( Node *nd = child_; nd; nd = nd->next_ ) {
  1383. Node *nn = nd->search( path, offset );
  1384. if ( nn ) return nn;
  1385. }
  1386. return 0;
  1387. }
  1388. }
  1389. return 0;
  1390. }
  1391. // return the number of child nodes (groups)
  1392. int Fl_Preferences::Node::nChildren() {
  1393. if (indexed_) {
  1394. return nIndex_;
  1395. } else {
  1396. int cnt = 0;
  1397. for ( Node *nd = child_; nd; nd = nd->next_ )
  1398. cnt++;
  1399. return cnt;
  1400. }
  1401. }
  1402. // return the node name
  1403. const char *Fl_Preferences::Node::name() {
  1404. if ( path_ ) {
  1405. char *r = strrchr( path_, '/' );
  1406. return r ? r+1 : path_ ;
  1407. } else {
  1408. return 0L ;
  1409. }
  1410. }
  1411. // return the n'th child node's name
  1412. const char *Fl_Preferences::Node::child( int ix ) {
  1413. Node *nd = childNode( ix );
  1414. if ( nd )
  1415. return nd->name();
  1416. else
  1417. return 0L ;
  1418. }
  1419. // return the n'th child node
  1420. Fl_Preferences::Node *Fl_Preferences::Node::childNode( int ix ) {
  1421. createIndex();
  1422. if (indexed_) {
  1423. // usually faster access in correct order, but needing more memory
  1424. return index_[ix];
  1425. } else {
  1426. // slow access and reverse order
  1427. int n = nChildren();
  1428. ix = n - ix -1;
  1429. Node *nd;
  1430. for ( nd = child_; nd; nd = nd->next_ ) {
  1431. if ( !ix-- ) break;
  1432. if ( !nd ) break;
  1433. }
  1434. return nd;
  1435. }
  1436. }
  1437. // remove myself from the list and delete me (and all children)
  1438. char Fl_Preferences::Node::remove() {
  1439. Node *nd = 0, *np;
  1440. if ( parent() ) {
  1441. nd = parent()->child_; np = 0L;
  1442. for ( ; nd; np = nd, nd = nd->next_ ) {
  1443. if ( nd == this ) {
  1444. if ( np )
  1445. np->next_ = nd->next_;
  1446. else
  1447. parent()->child_ = nd->next_;
  1448. break;
  1449. }
  1450. }
  1451. parent()->dirty_ = 1;
  1452. parent()->updateIndex();
  1453. }
  1454. delete this;
  1455. return ( nd != 0 );
  1456. }
  1457. void Fl_Preferences::Node::createIndex() {
  1458. if (indexed_) return;
  1459. int n = nChildren();
  1460. if (n>NIndex_) {
  1461. NIndex_ = n + 16;
  1462. index_ = (Node**)realloc(index_, NIndex_*sizeof(Node**));
  1463. }
  1464. Node *nd;
  1465. int i = 0;
  1466. for (nd = child_; nd; nd = nd->next_, i++) {
  1467. index_[n-i-1] = nd;
  1468. }
  1469. nIndex_ = n;
  1470. indexed_ = 1;
  1471. }
  1472. void Fl_Preferences::Node::updateIndex() {
  1473. indexed_ = 0;
  1474. }
  1475. void Fl_Preferences::Node::deleteIndex() {
  1476. if (index_) free(index_);
  1477. NIndex_ = nIndex_ = 0;
  1478. index_ = 0;
  1479. indexed_ = 0;
  1480. }
  1481. /**
  1482. * \brief Create a plugin.
  1483. *
  1484. * \param[in] klass plugins are grouped in classes
  1485. * \param[in] name every plugin should have a unique name
  1486. */
  1487. Fl_Plugin::Fl_Plugin(const char *klass, const char *name)
  1488. : id(0) {
  1489. #ifdef FL_PLUGIN_VERBOSE
  1490. printf("Fl_Plugin: creating a plugin, class \"%s\", name \"%s\"\n",
  1491. klass, name);
  1492. #endif
  1493. Fl_Plugin_Manager pm(klass);
  1494. id = pm.addPlugin(name, this);
  1495. }
  1496. /**
  1497. * \brief Clear the plugin and remove it from the database.
  1498. */
  1499. Fl_Plugin::~Fl_Plugin() {
  1500. #ifdef FL_PLUGIN_VERBOSE
  1501. printf("Fl_Plugin: deleting a plugin\n");
  1502. #endif
  1503. if (id)
  1504. Fl_Plugin_Manager::remove(id);
  1505. }
  1506. /**
  1507. * \brief Manage all plugins belonging to one class.
  1508. */
  1509. Fl_Plugin_Manager::Fl_Plugin_Manager(const char *klass)
  1510. : Fl_Preferences(0, Fl_Preferences::Name("%s/%s", "plugins", klass)) {
  1511. #ifdef FL_PLUGIN_VERBOSE
  1512. printf("Fl_Plugin: creating a plugin manager for class \"%s\"\n", klass);
  1513. #endif
  1514. }
  1515. /**
  1516. * \brief Remove th…

Large files files are truncated, but you can click here to view the full file