1 //this file is part of notepad++ 2 //Copyright (C)2003 Don HO <donho@altern.org> 3 // 4 //This program is free software; you can redistribute it and/or 5 //modify it under the terms of the GNU General Public License 6 //as published by the Free Software Foundation; either 7 //version 2 of the License, or (at your option) any later version. 8 // 9 //This program is distributed in the hope that it will be useful, 10 //but WITHOUT ANY WARRANTY; without even the implied warranty of 11 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 //GNU General Public License for more details. 13 // 14 //You should have received a copy of the GNU General Public License 15 //along with this program; if not, write to the Free Software 16 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 /** 18 * ini file setting 19 * 20 * Author: dokutoku, https://twitter.com/dokutoku3 21 * License: GPL-2.0 or later 22 */ 23 module npp_api.pluginfunc.ini_setting; 24 25 26 version (Windows): 27 version (Not_betterC): 28 29 private static import core.sys.windows.winbase; 30 private static import core.sys.windows.windef; 31 private static import core.sys.windows.winnt; 32 private static import core.sys.windows.winuser; 33 private static import std.conv; 34 private static import std.file; 35 private static import std.path; 36 private static import std.stdio; 37 private static import std.string; 38 private static import std.traits; 39 private static import std.utf; 40 private static import npp_api.pluginfunc.string; 41 private static import npp_api.pluginfunc.npp_msgs; 42 private static import npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs; 43 private static import npp_api.powereditor.misc.pluginsmanager.plugininterface; 44 private static import npp_api.pluginfunc.menu; 45 private static import npp_api.pluginfunc.path; 46 private static import npp_api.pluginfunc.config_file; 47 48 struct ini_setting_item 49 { 50 mixin npp_api.pluginfunc.config_file.setting_item_internal!(wchar); 51 } 52 53 ini_setting_item[output_length] convert_setting(size_t output_length)(npp_api.pluginfunc.config_file.setting_item[] input_setting) 54 55 do 56 { 57 static import npp_api.pluginfunc.config_file; 58 59 ini_setting_item[output_length] output; 60 61 for (size_t i = 0; i < input_setting.length; i++) { 62 output[i].name = std.utf.toUTF16(input_setting[i].name[0 .. $]); 63 output[i].name = npp_api.pluginfunc..string.copy_string(output[i].name); 64 output[i].type = input_setting[i].type; 65 66 switch (input_setting[i].type) { 67 case npp_api.pluginfunc.config_file.value_type.string_type: 68 case npp_api.pluginfunc.config_file.value_type.path_type: 69 if (input_setting[i].value.string_value != null) { 70 output[i].value.wstring_value = std.utf.toUTF16(input_setting[i].value.string_value[0 .. $]); 71 } 72 73 break; 74 75 default: 76 output[i].value = input_setting[i].value; 77 78 break; 79 } 80 81 output[i].extra_filter = input_setting[i].extra_filter; 82 } 83 84 return output; 85 } 86 87 pure nothrow @safe 88 wstring[] create_setting_idetifiers(size_t setting_length)(ini_setting_item[setting_length] setting_items) 89 90 do 91 { 92 static import npp_api.pluginfunc.string; 93 94 wstring[] output = new wstring[setting_length]; 95 96 for (size_t i = 0; i < setting_items.length; i++) { 97 output[i] = npp_api.pluginfunc..string.from_stringz(setting_items[i].name); 98 } 99 100 return output; 101 } 102 103 struct npp_ini_session 104 { 105 static import core.sys.windows.winbase; 106 static import core.sys.windows.windef; 107 static import core.sys.windows.winnt; 108 static import core.sys.windows.winuser; 109 static import std.conv; 110 static import std.file; 111 static import std.path; 112 static import std.stdio; 113 static import std.string; 114 static import std.traits; 115 static import std.utf; 116 static import npp_api.powereditor.misc.pluginsmanager.plugininterface; 117 static import npp_api.pluginfunc.npp_msgs; 118 static import npp_api.pluginfunc.string; 119 static import npp_api.pluginfunc.path; 120 static import npp_api.pluginfunc.config_file; 121 122 private: 123 wstring npp_config_path = null; 124 wstring plugin_config_path = null; 125 wstring config_file = null; 126 core.sys.windows.winnt.WCHAR[] section_name = null; 127 bool is_valid = false; 128 wchar[npp_api.pluginfunc.config_file.setting_name_length] setting_name_buf; 129 //path_buf; 130 131 public: 132 this(core.sys.windows.windef.HWND _nppHandle, const wchar[] plugin_directry, const wchar[] config_filename, const wchar[] section_name) 133 134 in 135 { 136 assert(section_name.length != 0); 137 138 if (plugin_directry.length != 0) { 139 assert(std.path.isValidPath(plugin_directry)); 140 } 141 } 142 143 do 144 { 145 this.section_name = section_name.dup; 146 this.section_name ~= "\0"w; 147 core.sys.windows.winnt.WCHAR[npp_api.pluginfunc.path.OS_MAX_PATH] input_buf = '\0'; 148 npp_api.pluginfunc.npp_msgs.send_NPPM_GETPLUGINSCONFIGDIR(_nppHandle, input_buf.length, &(input_buf[0])); 149 this.npp_config_path = npp_api.pluginfunc..string.from_stringz(input_buf); 150 151 if (std.file.exists(this.npp_config_path) && std.file.isDir(this.npp_config_path)) { 152 if (plugin_directry.length != 0) { 153 this.plugin_config_path = std.path.buildPath(this.npp_config_path, plugin_directry).idup; 154 155 if (!std.file.exists(this.plugin_config_path) || !std.file.isDir(this.plugin_config_path)) { 156 std.file.mkdir(this.plugin_config_path); 157 } 158 } else { 159 this.plugin_config_path = this.npp_config_path.idup; 160 } 161 162 this.config_file = std.path.buildPath(this.plugin_config_path, std.path.setExtension(config_filename, `.ini`w)).idup; 163 164 if (!std.file.exists(this.config_file) || !std.file.isFile(this.config_file)) { 165 try { 166 std.stdio.File new_file = std.stdio.File(this.config_file, `w`w); 167 new_file.close(); 168 this.is_valid = true; 169 } catch (Exception e) { 170 //ToDo: 171 } 172 } else { 173 this.is_valid = true; 174 } 175 176 this.config_file ~= "\0"w; 177 } else { 178 throw new Exception(`Unknown Notepad++ config path`); 179 } 180 } 181 182 pragma(inline, true) 183 pure nothrow @safe @nogc 184 bool is_valid_setting() const 185 186 do 187 { 188 return this.is_valid; 189 } 190 191 /** 192 * メニューにチェックをつけるかどうか 193 * 194 * Params: 195 * key_name = 設定名 196 * default_value = 設定の読み込みに失敗した場合のデフォルト値 197 * 198 * Returns: メニューにチェックをつけるかどうか 199 */ 200 nothrow @nogc 201 bool is_mfs_checked(const wchar[] key_name, bool default_value = false) 202 203 in 204 { 205 assert(key_name.length != 0); 206 } 207 208 do 209 { 210 npp_api.pluginfunc..string.copy_string(this.setting_name_buf, key_name); 211 core.sys.windows.windef.UINT result = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), &(this.setting_name_buf[0]), int.max, &(this.config_file[0])); 212 213 if (result == int.max) { 214 return default_value; 215 } 216 217 return (result == 1) ? (true) : (false); 218 } 219 220 nothrow @nogc 221 void load_menu_checked(size_t menu_index_length)(ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index) 222 223 do 224 { 225 for (size_t i = 0; i < menu_index.length; i++) { 226 if (menu_index[i].menu_checked_identifier.length != 0) { 227 menu_index[i].func_item._init2Check = this.is_mfs_checked(menu_index[i].menu_checked_identifier, menu_index[i].func_item._init2Check); 228 } 229 } 230 } 231 232 nothrow 233 void auto_load(size_t setting_length)(ref .ini_setting_item[setting_length] settings) const 234 235 do 236 { 237 npp_api.pluginfunc.config_file.value_buf value_buf; 238 239 bool result_temp; 240 241 foreach (ref setting; settings) { 242 result_temp = false; 243 244 switch (setting.type) { 245 case npp_api.pluginfunc.config_file.value_type.bool_type: 246 result_temp = this.load_config(setting.name, value_buf.bool_value); 247 248 if ((result_temp) && (setting.extra_filter != null)) { 249 result_temp = setting.extra_filter(value_buf); 250 } 251 252 if (result_temp) { 253 setting.value.bool_value = value_buf.bool_value; 254 } 255 256 break; 257 258 case npp_api.pluginfunc.config_file.value_type.int_type: 259 result_temp = this.load_config(setting.name, int.max, value_buf.int_value); 260 261 if ((result_temp) && (setting.extra_filter != null)) { 262 result_temp = setting.extra_filter(value_buf); 263 } 264 265 if (result_temp) { 266 setting.value.int_value = value_buf.int_value; 267 } 268 269 break; 270 271 case npp_api.pluginfunc.config_file.value_type.uint_type: 272 result_temp = this.load_config(setting.name, uint.max, value_buf.uint_value); 273 274 if ((result_temp) && (setting.extra_filter != null)) { 275 result_temp = setting.extra_filter(value_buf); 276 } 277 278 if (result_temp) { 279 setting.value.uint_value = value_buf.uint_value; 280 } 281 282 break; 283 284 case npp_api.pluginfunc.config_file.value_type.string_type: 285 value_buf.wstring_value[] = '\0'; 286 result_temp = this.load_config(setting.name, value_buf.wstring_value); 287 288 if ((result_temp) && (setting.extra_filter != null)) { 289 result_temp = setting.extra_filter(value_buf); 290 } 291 292 if (result_temp) { 293 setting.value.wstring_value = npp_api.pluginfunc..string.from_stringz(value_buf.wstring_value); 294 } 295 296 break; 297 298 case npp_api.pluginfunc.config_file.value_type.path_type: 299 value_buf.wstring_value[] = '\0'; 300 result_temp = this.load_config(setting.name, value_buf.wstring_value); 301 302 if ((result_temp) && (setting.extra_filter != null)) { 303 result_temp = setting.extra_filter(value_buf); 304 } 305 306 if (result_temp) { 307 setting.value.wstring_value = npp_api.pluginfunc..string.from_stringz(value_buf.path_value); 308 } 309 310 break; 311 312 default: 313 break; 314 } 315 } 316 } 317 318 pragma(inline, true) 319 nothrow 320 void auto_load(size_t setting_length, size_t menu_index_length)(ref .ini_setting_item[setting_length] settings, ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index) 321 322 do 323 { 324 this.auto_load(settings); 325 this.load_menu_checked(menu_index); 326 } 327 328 pragma(inline, true) 329 nothrow @nogc 330 void write_menu_checked(const wchar[] key_name, bool value) 331 332 in 333 { 334 assert(key_name.length != 0); 335 } 336 337 do 338 { 339 npp_api.pluginfunc..string.copy_string(this.setting_name_buf, key_name); 340 core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), &(this.setting_name_buf[0]), &(((value) ? (npp_api.pluginfunc..string.c_wstring!(`1`w)) : (npp_api.pluginfunc..string.c_wstring!(`0`w)))[0]), &(this.config_file[0])); 341 } 342 343 pragma(inline, true) 344 nothrow @nogc 345 void write_menu_checked(size_t menu_index_length)(ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index) 346 347 do 348 { 349 for (size_t i = 0; i < menu_index.length; i++) { 350 if (menu_index[i].menu_checked_identifier.length != 0) { 351 this.write_menu_checked(menu_index[i].menu_checked_identifier, menu_index[i].func_item._init2Check); 352 } 353 } 354 } 355 356 pragma(inline, true) 357 nothrow 358 bool auto_write(size_t setting_length)(const ref .ini_setting_item[setting_length] settings) 359 360 do 361 { 362 foreach (setting; settings) { 363 switch (setting.type) { 364 case npp_api.pluginfunc.config_file.value_type.bool_type: 365 this.write_config(setting.name, setting.value.bool_value); 366 367 break; 368 369 case npp_api.pluginfunc.config_file.value_type.int_type: 370 this.write_config(setting.name, setting.value.int_value); 371 372 break; 373 374 case npp_api.pluginfunc.config_file.value_type.uint_type: 375 this.write_config(setting.name, setting.value.uint_value); 376 377 break; 378 379 case npp_api.pluginfunc.config_file.value_type.string_type: 380 this.write_config(setting.name, setting.value.wstring_value); 381 382 break; 383 384 case npp_api.pluginfunc.config_file.value_type.path_type: 385 this.write_config(setting.name, setting.value.wstring_value); 386 387 break; 388 389 default: 390 break; 391 } 392 } 393 } 394 395 pragma(inline, true) 396 nothrow 397 void auto_write(size_t setting_length, size_t menu_index_length)(ref .ini_setting_item[setting_length] settings, ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index) 398 399 do 400 { 401 this.auto_write(settings); 402 this.load_menu_checked(menu_index); 403 } 404 405 /** 406 * ??? 407 * 408 * Params: 409 * key_name = ??? 410 * result_value = オプションの格納場所 411 * 412 * Returns: 正常にloadできたかどうか 413 */ 414 nothrow @nogc 415 bool load_config(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, ref bool result_value) const 416 417 in 418 { 419 assert(key_name[$ - 1] == '\0'); 420 } 421 422 do 423 { 424 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), &(key_name[0]), int.max, &(this.config_file[0])); 425 426 switch (temp_value) { 427 case 0: 428 result_value = false; 429 430 return true; 431 432 case 1: 433 result_value = true; 434 435 return true; 436 437 default: 438 return false; 439 } 440 } 441 442 /** 443 * ??? 444 * 445 * Params: 446 * key_name = ??? 447 * invaild_value = ??? 448 * result_value = オプションの格納場所 449 * 450 * Returns: 正常にloadできたかどうか 451 */ 452 nothrow @nogc 453 bool load_config(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, core.sys.windows.windef.INT invaild_value, ref core.sys.windows.windef.UINT result_value) const 454 455 in 456 { 457 assert(key_name[$ - 1] == '\0'); 458 } 459 460 do 461 { 462 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), &(key_name[0]), invaild_value, &(this.config_file[0])); 463 464 if (temp_value == invaild_value) { 465 return false; 466 } 467 468 result_value = temp_value; 469 470 return true; 471 } 472 473 /** 474 * ??? 475 * 476 * Params: 477 * key_name = ??? 478 * invaild_value = ??? 479 * result_value = オプションの格納場所 480 * 481 * Returns: 正常にloadできたかどうか 482 */ 483 nothrow @nogc 484 bool load_config(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, core.sys.windows.windef.INT invaild_value, ref int result_value) const 485 486 in 487 { 488 assert(key_name[$ - 1] == '\0'); 489 } 490 491 do 492 { 493 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), &(key_name[0]), invaild_value, &(this.config_file[0])); 494 495 if (temp_value == invaild_value) { 496 return false; 497 } 498 499 result_value = cast(int)(temp_value); 500 501 return true; 502 } 503 504 /** 505 * ??? 506 * 507 * Params: 508 * key_name = ??? 509 * invaild_value = ??? 510 * result_value = オプションの格納場所 511 * 512 * Returns: 正常にloadできたかどうか 513 */ 514 pragma(inline, true) 515 nothrow @nogc 516 bool load_config(size_t value_length)(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, ref wchar[value_length] result_value) const 517 518 in 519 { 520 assert(key_name[$ - 1] == '\0'); 521 } 522 523 do 524 { 525 core.sys.windows.windef.DWORD result_temp = core.sys.windows.winbase.GetPrivateProfileStringW(&(this.section_name[0]), &(key_name[0]), &("\0"w[0]), &(result_value[0]), result_value.length, &(this.config_file[0])); 526 527 if ((result_temp == 0) || (result_value[0] == '\0')) { 528 return false; 529 } 530 531 return true; 532 } 533 534 /** 535 * ??? 536 * 537 * Params: 538 * key_name = ??? 539 * result_value = オプションの格納場所 540 * 541 * Returns: 正常にloadできたかどうか 542 */ 543 pragma(inline, true) 544 bool load_config(C)(const (C)[] key_name, ref bool result_value) const 545 if (std.traits.isSomeChar!(C)) 546 547 in 548 { 549 } 550 551 do 552 { 553 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), std.utf.toUTF16z(key_name), int.max, &(this.config_file[0])); 554 555 switch (temp_value) { 556 case 0: 557 result_value = false; 558 559 return true; 560 561 case 1: 562 result_value = true; 563 564 return true; 565 566 default: 567 return false; 568 } 569 } 570 571 /** 572 * ??? 573 * 574 * Params: 575 * key_name = ??? 576 * invaild_value = ??? 577 * result_value = オプションの格納場所 578 * 579 * Returns: 正常にloadできたかどうか 580 */ 581 pragma(inline, true) 582 bool load_config(C)(const (C)[] key_name, core.sys.windows.windef.INT invaild_value, ref core.sys.windows.windef.UINT result_value) const 583 if (std.traits.isSomeChar!(C)) 584 585 in 586 { 587 } 588 589 do 590 { 591 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), std.utf.toUTF16z(key_name), invaild_value, &(this.config_file[0])); 592 593 if (temp_value == invaild_value) { 594 return false; 595 } 596 597 result_value = temp_value; 598 599 return true; 600 } 601 602 /** 603 * ??? 604 * 605 * Params: 606 * key_name = ??? 607 * value = ??? 608 * 609 * Returns: WritePrivateProfileStringWの結果 610 */ 611 pragma(inline, true) 612 nothrow @nogc 613 bool write_config(size_t value_length)(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, bool value) const 614 615 in 616 { 617 assert(key_name[$ - 1] == '\0'); 618 } 619 620 do 621 { 622 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), &(key_name[0]), &(((value) ? (npp_api.pluginfunc..string.c_wstring!(`1`w)) : (npp_api.pluginfunc..string.c_wstring!(`0`w)))[0]), &(this.config_file[0])) != 0) ? (true) : (false); 623 } 624 625 /** 626 * ??? 627 * 628 * Params: 629 * key_name = ??? 630 * value = ??? 631 * 632 * Returns: 正常にwriteできたかどうか 633 */ 634 pragma(inline, true) 635 nothrow 636 bool write_config(size_t value_length, D)(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, const D value) const 637 if (std.traits.isNumeric!(D)) 638 639 in 640 { 641 assert(key_name[$ - 1] == '\0'); 642 } 643 644 do 645 { 646 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), &(key_name[0]), std.utf.toUTF16z(std.conv.to!(wstring)(value)), &(this.config_file[0])) != 0) ? (true) : (false); 647 } 648 649 /** 650 * ??? 651 * 652 * Params: 653 * key_name = ??? 654 * value = ??? 655 * 656 * Returns: 正常にwriteできたかどうか 657 */ 658 pragma(inline, true) 659 nothrow 660 bool write_config(size_t value_length)(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, const wchar[] value) const 661 662 in 663 { 664 assert(key_name[$ - 1] == '\0'); 665 } 666 667 do 668 { 669 670 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), &(key_name[0]), (value.length != 0) ? (std.utf.toUTF16z(value)) : (core.sys.windows.windef.NULL), &(this.config_file[0])) != 0) ? (true) : (false); 671 } 672 673 /** 674 * ??? 675 * 676 * Params: 677 * key_name = ??? 678 * value = ??? 679 * 680 * Returns: WritePrivateProfileStringWの結果 681 */ 682 pragma(inline, true) 683 bool write_config(C)(const (C)[] key_name, bool value) const 684 if (std.traits.isSomeChar!(C)) 685 686 in 687 { 688 assert(key_name.length != 0); 689 } 690 691 do 692 { 693 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), std.utf.toUTF16z(key_name), &(((value) ? (npp_api.pluginfunc..string.c_wstring!(`1`w)) : (npp_api.pluginfunc..string.c_wstring!(`0`w)))[0]), &(this.config_file[0])) != 0) ? (true) : (false); 694 } 695 696 /** 697 * ??? 698 * 699 * Params: 700 * key_name = ??? 701 * value = ??? 702 * 703 * Returns: 正常にwriteできたかどうか 704 */ 705 pragma(inline, true) 706 bool write_config(C, D)(const (C)[] key_name, const D value) const 707 if (std.traits.isSomeChar!(C) && std.traits.isNumeric!(D)) 708 709 in 710 { 711 } 712 713 do 714 { 715 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), std.utf.toUTF16z(key_name), std.utf.toUTF16z(std.conv.to!(wstring)(value)), &(this.config_file[0])) != 0) ? (true) : (false); 716 } 717 718 /** 719 * ??? 720 * 721 * Params: 722 * key_name = ??? 723 * value = ??? 724 * 725 * Returns: 正常にwriteできたかどうか 726 */ 727 bool write_config(C)(const (C)[] key_name, const (C)[] value) const 728 if (std.traits.isSomeChar!(C)) 729 730 in 731 { 732 } 733 734 do 735 { 736 if (value.length == 0) { 737 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), std.utf.toUTF16z(key_name), core.sys.windows.windef.NULL, &(this.config_file[0])) != 0) ? (true) : (false); 738 } else { 739 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), std.utf.toUTF16z(key_name), std.utf.toUTF16z(value), &(this.config_file[0])) != 0) ? (true) : (false); 740 } 741 } 742 743 /** 744 * ??? 745 * 746 * Params: 747 * ??? = ??? 748 */ 749 void write_config(C)(const (C)[] key_name, const npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem func_item) const 750 if (std.traits.isSomeChar!(C)) 751 752 do 753 { 754 this.write_config(key_name, func_item._init2Check); 755 } 756 757 pragma(inline, true) 758 pure nothrow @safe 759 wstring get_config_file_path() const 760 761 do 762 { 763 return this.config_file.idup; 764 } 765 766 pragma(inline, true) 767 pure nothrow @safe 768 wstring get_config_folder_path() const 769 770 do 771 { 772 return this.plugin_config_path.idup; 773 } 774 775 /** 776 * 777 */ 778 void mkdir(const wchar[] sub_directry = ``w) 779 780 in 781 { 782 if (sub_directry.length != 0) { 783 assert(std.path.isValidPath(sub_directry)); 784 } 785 } 786 787 do 788 { 789 wstring path = std.path.buildPath(this.plugin_config_path, sub_directry); 790 791 if (!std.file.exists(path) && !std.file.isDir(path)) { 792 std.file.mkdir(path); 793 } 794 } 795 796 invariant 797 { 798 assert((this.section_name.length != 0) && (this.section_name[$ - 1] == '\0')); 799 assert((this.config_file.length != 0)); 800 } 801 }