PageRenderTime 74ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/fltk/src/Fl_Preferences0.cxx

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