1 /*************************************************************************** 2 * Copyright (C) 2010~2010 by CSSlayer * 3 * wengxt@gmail.com * 4 * * 5 * This program is free software; you can redistribute it and/or modify * 6 * it under the terms of the GNU General Public License as published by * 7 * the Free Software Foundation; either version 2 of the License, or * 8 * (at your option) any later version. * 9 * * 10 * This program is distributed in the hope that it will be useful, * 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 * GNU General Public License for more details. * 14 * * 15 * You should have received a copy of the GNU General Public License * 16 * along with this program; if not, write to the * 17 * Free Software Foundation, Inc., * 18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * 19 ***************************************************************************/ 20 21 /** 22 * @defgroup FcitxConfig FcitxConfig 23 * 24 * FcitxConfig includes a lot of configuration related macro and function. 25 * Macro can be easily used to bind a struct with configuration 26 * 27 * Fcitx configuration file can be easily mapped to corresponding user interface, 28 * and you don't need to write any user interface at all. 29 * 30 * FcitxConfig can be also used to implement native user interface. 31 * 32 * Here is a common example for use macro binding with a struct 33 * 34 * @code 35 * typedef struct _FcitxProfile { 36 * FcitxGenericConfig gconfig; 37 * boolean bUseRemind; 38 * char* imName; 39 * boolean bUseWidePunc; 40 * boolean bUseFullWidthChar; 41 * boolean bUsePreedit; 42 * char* imList; 43 * } FcitxProfile; 44 * @endcode 45 * 46 * A config struct need to put FcitxGenericConfig as first field. 47 * Following code will define a function 48 * 49 * @code 50 * CONFIG_BINDING_BEGIN_WITH_ARG(FcitxProfile, FcitxInstance* instance) 51 * CONFIG_BINDING_REGISTER("Profile", "FullWidth", bUseFullWidthChar) 52 * CONFIG_BINDING_REGISTER("Profile", "UseRemind", bUseRemind) 53 * CONFIG_BINDING_REGISTER_WITH_FILTER_ARG("Profile", "IMName", imName, FilterIMName, instance) 54 * CONFIG_BINDING_REGISTER("Profile", "WidePunc", bUseWidePunc) 55 * CONFIG_BINDING_REGISTER("Profile", "PreeditStringInClientWindow", bUsePreedit) 56 * CONFIG_BINDING_REGISTER_WITH_FILTER_ARG("Profile", "EnabledIMList", imList, FilterIMList, instance) 57 * CONFIG_BINDING_END() 58 * @endcode 59 * 60 * Then you will get following function: 61 * 62 * @code 63 * void FcitxProfileConfigBind( FcitxProfile* config, FcitxConfigFile* cfile, FcitxConfigFileDesc* cfdesc, FcitxInstance* instance ) 64 * @endcode 65 * 66 * If you need forward declaration, you can used 67 * @code 68 * CONFIG_BINDING_DECLARE_WITH_ARG(FcitxProfile, FcitxInstance* instance) 69 * @endcode 70 * 71 * The FcitxConfigFileDesc pointer is coresponding to the .desc file, which need to be placed 72 * under share/fcitx/configdesc/ 73 * 74 * You can use following macro to define a define to load FcitxConfigFileDesc* pointer, 75 * second argument is the .desc file name. 76 * 77 * The FcitxConfigFileDesc pointer returned by this macro is a static variable, so it should not be 78 * free'd, and will only load once. 79 * 80 * @code 81 * CONFIG_DESC_DEFINE(GetProfileDesc, "profile.desc") 82 * @endcode 83 */ 84 85 /** 86 * @addtogroup FcitxConfig 87 * @{ 88 */ 89 90 /** 91 * @file fcitx-config.h 92 * @author CSSlayer wengxt@gmail.com 93 * @date 2010-04-30 94 * 95 * Fcitx configure file read-write 96 */ 97 98 #ifndef _FCITX_FCITX_CONFIG_H_ 99 #define _FCITX_FCITX_CONFIG_H_ 100 101 #include <stdint.h> 102 #include <stdio.h> 103 #include <errno.h> 104 #include <fcitx-utils/uthash.h> 105 #include <fcitx-utils/utils.h> 106 #include <fcitx-utils/log.h> 107 #include <fcitx-config/xdg.h> 108 #include <fcitx-config/hotkey.h> 109 110 #ifdef __cplusplus 111 extern "C" 112 { 113 #endif 114 115 /** 116 * The Color type in config file 117 **/ 118 typedef struct _FcitxConfigColor { 119 double r; /**< red */ 120 double g; /**< green */ 121 double b; /**< blue */ 122 } FcitxConfigColor; 123 124 /** 125 * config value type 126 **/ 127 typedef enum _FcitxConfigType { 128 T_Integer, 129 T_Color, 130 T_String, 131 T_Char, 132 T_Boolean, 133 T_Enum, 134 T_File, 135 T_Hotkey, 136 T_Font, 137 T_I18NString 138 } FcitxConfigType; 139 140 /** 141 * The sync direction 142 **/ 143 typedef enum _FcitxConfigSync { 144 Raw2Value, 145 Value2Raw, 146 ValueFree 147 } FcitxConfigSync; 148 149 /** 150 * Sync result 151 **/ 152 typedef enum _FcitxConfigSyncResult { 153 SyncSuccess, 154 SyncNoBinding, 155 SyncInvalid 156 } FcitxConfigSyncResult; 157 158 /** 159 * The value of config 160 **/ 161 typedef union _FcitxConfigValueType { 162 void *untype; /**< simple pointer */ 163 int *integer; /**< pointer to integer */ 164 boolean *boolvalue; /**< pointer to boolean */ 165 166 struct _FcitxHotkey *hotkey; /**< pointer to two hotkeys */ 167 FcitxConfigColor *color; /**< pointer to color */ 168 int *enumerate; /**< pointer to enum */ 169 char **string; /**< pointer to string */ 170 char *chr; /**< pointer to char */ 171 } FcitxConfigValueType; 172 173 typedef struct _FcitxConfigGroup FcitxConfigGroup; /**< FcitxConfigGroup */ 174 175 typedef struct _FcitxConfigOption FcitxConfigOption; /**< FcitxConfigOption */ 176 177 typedef struct _FcitxConfigFileDesc FcitxConfigFileDesc; /**< FcitxConfigFileDesc */ 178 179 typedef struct _FcitxConfigGroupDesc FcitxConfigGroupDesc; /**< FcitxConfigGroupDesc */ 180 181 typedef struct _FcitxConfigOptionDesc FcitxConfigOptionDesc; /**< FcitxConfigOptionDesc */ 182 183 typedef struct _FcitxConfigOptionDesc2 FcitxConfigOptionDesc2; /**< FcitxConfigOptionDesc2 */ 184 185 typedef struct _FcitxGenericConfig FcitxGenericConfig; /**< FcitxGenericConfig */ 186 187 typedef struct _FcitxConfigOptionSubkey FcitxConfigOptionSubkey; /**< FcitxConfigOptionSubkey */ 188 189 typedef union _FcitxConfigConstrain FcitxConfigConstrain /** < FcitxConfigConstrain */; 190 191 /** 192 * sync filter function 193 **/ 194 typedef void (*FcitxSyncFilter)(FcitxGenericConfig* config, FcitxConfigGroup *group, FcitxConfigOption *option, void* value, FcitxConfigSync sync, void* arg); 195 196 /** 197 * Enum value type description 198 **/ 199 typedef struct _FcitxConfigEnum { 200 char **enumDesc; /**< enum string description, a user visble string */ 201 int enumCount; /**< length of enumDesc */ 202 } FcitxConfigEnum; 203 204 /** 205 * Config file contains multiple config groups, and the opposite config file description 206 **/ 207 typedef struct _FcitxConfigFile { 208 FcitxConfigFileDesc *fileDesc; /**< configuration file description */ 209 FcitxConfigGroup* groups; /**< contained group */ 210 } FcitxConfigFile; 211 212 213 /** 214 * A generic config struct, config struct can derive from it. 215 * @code 216 * struct TestConfig { 217 * FcitxGenericConfig gconfig; 218 * int own_value; 219 * }; 220 * @endcode 221 **/ 222 struct _FcitxGenericConfig { 223 /** 224 * config file pointer 225 **/ 226 FcitxConfigFile *configFile; 227 }; 228 229 /** 230 * Config Option Description, it describe a Key=Value entry in config file. 231 **/ 232 struct _FcitxConfigOptionDesc { 233 char *optionName; /**< option name */ 234 char *desc; /**< optiont description string, user visible */ 235 FcitxConfigType type; /**< value type */ 236 char *rawDefaultValue; /**< raw string default value */ 237 FcitxConfigEnum configEnum; /**< if type is enum, the enum item info */ 238 239 UT_hash_handle hh; /**< hash handle */ 240 }; 241 242 union _FcitxConfigConstrain { 243 struct { 244 int min; 245 int max; 246 } integerConstrain; 247 248 struct { 249 size_t maxLength; 250 } stringConstrain; 251 252 struct { 253 boolean disallowNoModifer; 254 boolean allowModifierOnly; 255 } hotkeyConstrain; 256 257 void* padding[10]; 258 }; 259 260 /** 261 * Config option description v2 262 */ 263 struct _FcitxConfigOptionDesc2 { 264 struct _FcitxConfigOptionDesc optionDesc; 265 boolean advance; 266 FcitxConfigConstrain constrain; 267 char* longDesc; 268 void* padding[16]; 269 }; 270 271 /** 272 * Config Group Description, it describe a [Gruop] in config file 273 **/ 274 275 struct _FcitxConfigGroupDesc { 276 char *groupName; /**< Group Name */ 277 FcitxConfigOptionDesc *optionsDesc; /**< Hash table for option description */ 278 UT_hash_handle hh; /**< hash handle */ 279 }; 280 281 /** 282 * Description of a config file 283 **/ 284 struct _FcitxConfigFileDesc { 285 FcitxConfigGroupDesc *groupsDesc; /**< group description */ 286 char* domain; /**< domain for translation */ 287 }; 288 289 /** 290 * Config Option in config file, Key=Value entry 291 **/ 292 struct _FcitxConfigOption { 293 char *optionName; /**< option name */ 294 char *rawValue; /**< raw string value */ 295 FcitxConfigValueType value; /**< value type */ 296 FcitxSyncFilter filter; /**< filter function */ 297 void *filterArg; /**< argument for filter function */ 298 union { 299 FcitxConfigOptionDesc *optionDesc; /**< option description pointer */ 300 FcitxConfigOptionDesc2 *optionDesc2; /**< option description pointer */ 301 }; 302 FcitxConfigOptionSubkey *subkey; /**< subkey which only used with I18NString */ 303 UT_hash_handle hh; /**< hash handle */ 304 } ; 305 306 /** 307 * Config Option subkey in config file, Key[Subkey]=Value entry 308 **/ 309 struct _FcitxConfigOptionSubkey { 310 char *subkeyName; /**< subkey name */ 311 char *rawValue; /**< subkey raw value */ 312 UT_hash_handle hh; /**< hash handle */ 313 }; 314 315 /** 316 * Config group in config file, [Group] Entry 317 **/ 318 struct _FcitxConfigGroup { 319 /** 320 * Group Name, unique in FcitxConfigFile 321 **/ 322 char *groupName; 323 /** 324 * Group Description 325 **/ 326 FcitxConfigGroupDesc *groupDesc; 327 /** 328 * Option store with a hash table 329 **/ 330 FcitxConfigOption* options; 331 /** 332 * UTHash handler 333 **/ 334 UT_hash_handle hh; 335 }; 336 337 /** 338 * declare the binding function 339 **/ 340 #define CONFIG_BINDING_DECLARE(config_type) \ 341 void config_type##ConfigBind(config_type* config, FcitxConfigFile* cfile, FcitxConfigFileDesc* cfdesc); 342 343 /** 344 * declare the binding function, with extra argument 345 **/ 346 #define CONFIG_BINDING_DECLARE_WITH_ARG(config_type, arg...) \ 347 void config_type##ConfigBind(config_type* config, FcitxConfigFile* cfile, FcitxConfigFileDesc* cfdesc, arg); 348 349 /** 350 * define the binding function 351 * each binding group for a config file will define a new function 352 * the structure is like: <br> 353 * CONFIG_BINDING_BEGIN <br> 354 * CONFIG_BINDING_REGISTER <br> 355 * .... <br> 356 * CONFIG_BINDING_REGISTER <br> 357 * CONFIG_BINDING_END 358 **/ 359 #define CONFIG_BINDING_BEGIN(config_type) \ 360 void config_type##ConfigBind(config_type* config, FcitxConfigFile* cfile, FcitxConfigFileDesc* cfdesc) { \ 361 (void) cfdesc; \ 362 FcitxGenericConfig *gconfig = (FcitxGenericConfig*) config; \ 363 if (gconfig->configFile) { \ 364 FcitxConfigFreeConfigFile(gconfig->configFile); \ 365 } \ 366 gconfig->configFile = cfile; 367 368 /** register binding and call it with extra argument */ 369 #define CONFIG_BINDING_BEGIN_WITH_ARG(config_type, arg...) \ 370 void config_type##ConfigBind(config_type* config, FcitxConfigFile* cfile, FcitxConfigFileDesc* cfdesc, arg) { \ 371 (void) cfdesc; \ 372 FcitxGenericConfig *gconfig = (FcitxGenericConfig*) config; \ 373 if (gconfig->configFile) { \ 374 FcitxConfigFreeConfigFile(gconfig->configFile); \ 375 } \ 376 gconfig->configFile = cfile; 377 /** 378 * register a binding 379 **/ 380 #define CONFIG_BINDING_REGISTER(g, o, var) \ 381 do { \ 382 FcitxConfigBindValue(cfile, g, o, &config->var, NULL, NULL); \ 383 } while(0); 384 385 /** 386 * register a binding with filter 387 **/ 388 #define CONFIG_BINDING_REGISTER_WITH_FILTER(g, o, var, filter_func) \ 389 do { \ 390 FcitxConfigBindValue(cfile, g, o, &config->var, filter_func, NULL); \ 391 } while(0); 392 393 /** 394 * register a binding with filter and extra argument 395 **/ 396 #define CONFIG_BINDING_REGISTER_WITH_FILTER_ARG(g, o, var, filter_func, arg) \ 397 do { \ 398 FcitxConfigBindValue(cfile, g, o, &config->var, filter_func, arg); \ 399 } while(0); 400 401 /** 402 * binding group end 403 **/ 404 #define CONFIG_BINDING_END() } 405 406 /** 407 * define a singleton function to load config file description 408 **/ 409 #define CONFIG_DESC_DEFINE(funcname, path) \ 410 FcitxConfigFileDesc *funcname() \ 411 { \ 412 static FcitxConfigFileDesc *configDesc = NULL; \ 413 if (!configDesc) \ 414 { \ 415 FILE *tmpfp; \ 416 tmpfp = FcitxXDGGetFileWithPrefix("configdesc", path, "r", NULL); \ 417 if (tmpfp == NULL) \ 418 { \ 419 FcitxLog(ERROR, "Load Config Description File %s Error, Please Check your install.", path); \ 420 return NULL; \ 421 } \ 422 configDesc = FcitxConfigParseConfigFileDescFp(tmpfp); \ 423 fclose(tmpfp); \ 424 } \ 425 return configDesc; \ 426 } 427 428 #define CONFIG_DEFINE_LOAD_AND_SAVE(name, type, config_name) \ 429 CONFIG_DESC_DEFINE(Get##name##Desc, config_name ".desc") \ 430 void name##SaveConfig(type* _cfg) \ 431 { \ 432 FcitxConfigFileDesc* configDesc = Get##name##Desc(); \ 433 FILE *fp = FcitxXDGGetFileUserWithPrefix("conf", config_name ".config", "w", NULL); \ 434 FcitxConfigSaveConfigFileFp(fp, &_cfg->gconfig, configDesc); \ 435 if (fp) \ 436 fclose(fp); \ 437 } \ 438 boolean name##LoadConfig(type* _cfg) { \ 439 FcitxConfigFileDesc* configDesc = Get##name##Desc(); \ 440 if (configDesc == NULL) \ 441 return false; \ 442 \ 443 FILE *fp; \ 444 fp = FcitxXDGGetFileUserWithPrefix("conf", config_name ".config", "r", NULL); \ 445 if (!fp) { \ 446 if (errno == ENOENT) \ 447 name##SaveConfig(_cfg); \ 448 } \ 449 FcitxConfigFile *cfile = FcitxConfigParseConfigFileFp(fp, configDesc); \ 450 type##ConfigBind(_cfg, cfile, configDesc); \ 451 FcitxConfigBindSync((FcitxGenericConfig*)_cfg); \ 452 if (fp) \ 453 fclose(fp); \ 454 return true; \ 455 } \ 456 457 /** 458 * parse a config file with file name. 459 * even the file cannot be read, or with wrong format, 460 * it will try to return a usable FcitxConfigFile (missing 461 * entry with defaul value). 462 * 463 * @param filename file name of a configfile 464 * @param cfdesc config file description 465 * @return FcitxConfigFile* 466 **/ 467 FcitxConfigFile *FcitxConfigParseConfigFile(char *filename, FcitxConfigFileDesc* cfdesc); 468 469 /** 470 * parse multi config file, the main difference 471 * between ParseConfigFile is that it parse multiple file 472 * and the duplicate entry will be overwritten with the 473 * file behind the previous one. 474 * 475 * @see ParseConfigFile 476 * @param filename filenames 477 * @param len len of filenames 478 * @param cfdesc config file description 479 * @return FcitxConfigFile* 480 **/ 481 FcitxConfigFile *FcitxConfigParseMultiConfigFile(char **filename, int len, FcitxConfigFileDesc* cfdesc); 482 483 /** 484 * same with ParseConfigFile, the input is FILE* 485 * 486 * @see ParseConfigFile 487 * @param fp file pointer 488 * @param fileDesc config file description 489 * @return FcitxConfigFile* 490 **/ 491 FcitxConfigFile *FcitxConfigParseConfigFileFp(FILE* fp, FcitxConfigFileDesc* fileDesc); 492 493 /** 494 * same with FcitxConfigParseMultiConfigFileFp, the input is array of FILE* 495 * 496 * @see FcitxConfigParseMultiConfigFileFp 497 * @param fp array of file pointers 498 * @param len length of fp 499 * @param fileDesc config file description 500 * @return FcitxConfigFile* 501 **/ 502 FcitxConfigFile *FcitxConfigParseMultiConfigFileFp(FILE **fp, int len, FcitxConfigFileDesc* fileDesc); 503 504 /** 505 * Check the raw FcitxConfigFile and try to fill the default value 506 * 507 * @param configFile config file 508 * @param fileDesc config file description 509 * @return boolean 510 **/ 511 boolean FcitxConfigCheckConfigFile(FcitxConfigFile *configFile, FcitxConfigFileDesc* fileDesc); 512 513 /** 514 * parse config file description from file 515 * 516 * @param filename filename 517 * @return FcitxConfigFileDesc* 518 **/ 519 FcitxConfigFileDesc *FcitxConfigParseConfigFileDesc(char* filename); 520 521 /** 522 * parse config file description from file pointer 523 * 524 * @see ParseConfigFileDesc 525 * @param fp file pointer 526 * @return FcitxConfigFileDesc* 527 **/ 528 FcitxConfigFileDesc *FcitxConfigParseConfigFileDescFp(FILE* fp); 529 530 /** 531 * internal raw file parse, it can merge the config to existing config file 532 * 533 * @param filename file 534 * @param reuse NULL or existing config file 535 * @return FcitxConfigFile* 536 **/ 537 FcitxConfigFile* FcitxConfigParseIni(char* filename, FcitxConfigFile* reuse); 538 539 /** 540 * internal raw file parse, it can merge the config to existing config file 541 * 542 * @see ParseIni 543 * @param fp file pointer 544 * @param reuse NULL or existing config file 545 * @return FcitxConfigFile* 546 **/ 547 FcitxConfigFile* FcitxConfigParseIniFp(FILE* fp, FcitxConfigFile* reuse); 548 549 /** 550 * free a config file 551 * 552 * @param cfile config file 553 * @return void 554 **/ 555 void FcitxConfigFreeConfigFile(FcitxConfigFile* cfile); 556 557 /** 558 * free a config file description 559 * 560 * @param cfdesc config file description 561 * @return void 562 **/ 563 void FcitxConfigFreeConfigFileDesc(FcitxConfigFileDesc* cfdesc); 564 565 /** 566 * free a config group 567 * 568 * @param group config group 569 * @return void 570 **/ 571 void FcitxConfigFreeConfigGroup(FcitxConfigGroup *group); 572 573 /** 574 * free a config group description 575 * 576 * @param cgdesc config group description 577 * @return void 578 **/ 579 void FcitxConfigFreeConfigGroupDesc(FcitxConfigGroupDesc *cgdesc); 580 581 /** 582 * free a config option 583 * 584 * @param option config option 585 * @return void 586 **/ 587 void FcitxConfigFreeConfigOption(FcitxConfigOption *option); 588 589 /** 590 * free a config option description 591 * 592 * @param codesc config option description 593 * @return void 594 **/ 595 void FcitxConfigFreeConfigOptionDesc(FcitxConfigOptionDesc *codesc); 596 597 /** 598 * Save config file to fp, it will do the Value2Raw sync 599 * 600 * @param filename file name 601 * @param cfile config 602 * @param cdesc config file description 603 * @return boolean 604 **/ 605 boolean FcitxConfigSaveConfigFile(char *filename, FcitxGenericConfig *cfile, FcitxConfigFileDesc* cdesc); 606 607 /** 608 * Save config file to fp 609 * 610 * @see SaveConfigFile 611 * @param fp file pointer 612 * @param cfile config 613 * @param cdesc config file dsecription 614 * @return boolean 615 **/ 616 boolean FcitxConfigSaveConfigFileFp(FILE* fp, FcitxGenericConfig *cfile, FcitxConfigFileDesc* cdesc); 617 618 /** 619 * sync a single value 620 * 621 * @param config config 622 * @param group config group 623 * @param option config option 624 * @param sync sync direction 625 * @return Svoid 626 **/ 627 void FcitxConfigSyncValue(FcitxGenericConfig* config, FcitxConfigGroup* group, FcitxConfigOption* option, FcitxConfigSync sync); 628 629 /** 630 * Get the binded value type 631 * 632 * @param config config 633 * @param group group name 634 * @param option option name 635 * @return FcitxConfigValueType 636 **/ 637 FcitxConfigValueType FcitxConfigGetBindValue(FcitxGenericConfig *config, const char *group, const char* option); 638 639 /** 640 * Get a option description from config file description, return NULL if not found. 641 * 642 * @param cfdesc config file description 643 * @param groupName group name 644 * @param optionName option name 645 * @return const FcitxConfigOptionDesc* 646 **/ 647 const FcitxConfigOptionDesc* FcitxConfigDescGetOptionDesc(FcitxConfigFileDesc* cfdesc, const char* groupName, const char* optionName); 648 649 650 /** 651 * Get a option description from config file description, return NULL if not found. 652 * 653 * @param cfile config file 654 * @param groupName group name 655 * @param optionName option name 656 * @return const FcitxConfigOptionDesc* 657 * 658 * @since 4.1.2 659 **/ 660 FcitxConfigOption* FcitxConfigFileGetOption(FcitxConfigFile* cfile, const char* groupName, const char* optionName); 661 662 663 /** 664 * Get the I18NString value from current locale 665 * 666 * @param option config option 667 * @return const char* 668 **/ 669 const char* FcitxConfigOptionGetLocaleString(FcitxConfigOption* option); 670 671 /** 672 * do the Raw2Value sync for config 673 * 674 * @param config config 675 * @return void 676 **/ 677 void FcitxConfigBindSync(FcitxGenericConfig* config); 678 679 /** 680 * reset a config to default value 681 * 682 * @param config config 683 * @return Svoid 684 **/ 685 void FcitxConfigResetConfigToDefaultValue(FcitxGenericConfig* config); 686 687 /** 688 * bind a value with a struct, normally you should use 689 * CONFIG_BINDING_ series macro, not directly this function. 690 * 691 * @param cfile config file 692 * @param groupName group name 693 * @param optionName option name 694 * @param var pointer to value 695 * @param filter filter function 696 * @param arg extra argument 697 * @return void 698 **/ 699 void FcitxConfigBindValue(FcitxConfigFile* cfile, const char *groupName, const char *optionName, void* var, FcitxSyncFilter filter, void *arg); 700 701 /** 702 * free a binded config struct with all related value 703 * @param config config 704 * @return void 705 */ 706 void FcitxConfigFree(FcitxGenericConfig* config); 707 708 #ifdef __cplusplus 709 } 710 711 #endif 712 713 #endif 714 715 /** 716 * @} 717 */ 718 719 // kate: indent-mode cstyle; space-indent on; indent-width 0; 720