PageRenderTime 664ms CodeModel.GetById 42ms RepoModel.GetById 8ms app.codeStats 1ms

/fltk/src/Fl_Preferences.cxx

http://luafltk.googlecode.com/
C++ | 1979 lines | 1250 code | 171 blank | 558 comment | 259 complexity | a04df1111267ac341352694dc6f6c72e MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-3.0, 0BSD

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

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

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