CConfigFile.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #include <AK/StringBuilder.h>
  2. #include <LibCore/CConfigFile.h>
  3. #include <LibCore/CFile.h>
  4. #include <LibCore/CUserInfo.h>
  5. #include <pwd.h>
  6. #include <stdio.h>
  7. #include <unistd.h>
  8. NonnullRefPtr<CConfigFile> CConfigFile::get_for_app(const String& app_name)
  9. {
  10. String home_path = get_current_user_home_path();
  11. if (home_path == "/")
  12. home_path = String::format("/tmp");
  13. auto path = String::format("%s/%s.ini", home_path.characters(), app_name.characters());
  14. return adopt(*new CConfigFile(path));
  15. }
  16. NonnullRefPtr<CConfigFile> CConfigFile::get_for_system(const String& app_name)
  17. {
  18. auto path = String::format("/etc/%s.ini", app_name.characters());
  19. return adopt(*new CConfigFile(path));
  20. }
  21. NonnullRefPtr<CConfigFile> CConfigFile::open(const String& path)
  22. {
  23. return adopt(*new CConfigFile(path));
  24. }
  25. CConfigFile::CConfigFile(const String& file_name)
  26. : m_file_name(file_name)
  27. {
  28. reparse();
  29. }
  30. CConfigFile::~CConfigFile()
  31. {
  32. sync();
  33. }
  34. void CConfigFile::reparse()
  35. {
  36. m_groups.clear();
  37. auto file = CFile::construct(m_file_name);
  38. if (!file->open(CIODevice::OpenMode::ReadOnly))
  39. return;
  40. HashMap<String, String>* current_group = nullptr;
  41. while (file->can_read_line()) {
  42. auto line = file->read_line(BUFSIZ);
  43. auto* cp = (const char*)line.data();
  44. while (*cp && (*cp == ' ' || *cp == '\t' || *cp == '\n'))
  45. ++cp;
  46. switch (*cp) {
  47. case '\0': // EOL...
  48. case '#': // Comment, skip entire line.
  49. case ';': // -||-
  50. continue;
  51. case '[': { // Start of new group.
  52. StringBuilder builder;
  53. ++cp; // Skip the '['
  54. while (*cp && (*cp != ']'))
  55. builder.append(*(cp++));
  56. current_group = &m_groups.ensure(builder.to_string());
  57. break;
  58. }
  59. default: { // Start of key{
  60. StringBuilder key_builder;
  61. StringBuilder value_builder;
  62. while (*cp && (*cp != '='))
  63. key_builder.append(*(cp++));
  64. ++cp; // Skip the '='
  65. while (*cp && (*cp != '\n'))
  66. value_builder.append(*(cp++));
  67. if (!current_group) {
  68. // We're not in a group yet, create one with the name ""...
  69. current_group = &m_groups.ensure("");
  70. }
  71. current_group->set(key_builder.to_string(), value_builder.to_string());
  72. }
  73. }
  74. }
  75. }
  76. String CConfigFile::read_entry(const String& group, const String& key, const String& default_value) const
  77. {
  78. if (!has_key(group, key)) {
  79. const_cast<CConfigFile&>(*this).write_entry(group, key, default_value);
  80. return default_value;
  81. }
  82. auto it = m_groups.find(group);
  83. auto jt = it->value.find(key);
  84. return jt->value;
  85. }
  86. int CConfigFile::read_num_entry(const String& group, const String& key, int default_value) const
  87. {
  88. if (!has_key(group, key)) {
  89. const_cast<CConfigFile&>(*this).write_num_entry(group, key, default_value);
  90. return default_value;
  91. }
  92. bool ok;
  93. int value = read_entry(group, key).to_uint(ok);
  94. if (!ok)
  95. return default_value;
  96. return value;
  97. }
  98. bool CConfigFile::read_bool_entry(const String& group, const String& key, bool default_value) const
  99. {
  100. return read_entry(group, key, default_value ? "1" : "0") == "1";
  101. }
  102. void CConfigFile::write_entry(const String& group, const String& key, const String& value)
  103. {
  104. m_groups.ensure(group).ensure(key) = value;
  105. m_dirty = true;
  106. }
  107. void CConfigFile::write_num_entry(const String& group, const String& key, int value)
  108. {
  109. write_entry(group, key, String::number(value));
  110. }
  111. void CConfigFile::write_bool_entry(const String& group, const String& key, bool value)
  112. {
  113. write_entry(group, key, value ? "1" : "0");
  114. }
  115. void CConfigFile::write_color_entry(const String& group, const String& key, Color value)
  116. {
  117. write_entry(group, key, String::format("%d,%d,%d,%d", value.red(), value.green(), value.blue(), value.alpha()));
  118. }
  119. bool CConfigFile::sync()
  120. {
  121. if (!m_dirty)
  122. return true;
  123. FILE* fp = fopen(m_file_name.characters(), "wb");
  124. if (!fp)
  125. return false;
  126. for (auto& it : m_groups) {
  127. fprintf(fp, "[%s]\n", it.key.characters());
  128. for (auto& jt : it.value)
  129. fprintf(fp, "%s=%s\n", jt.key.characters(), jt.value.characters());
  130. fprintf(fp, "\n");
  131. }
  132. fclose(fp);
  133. m_dirty = false;
  134. return true;
  135. }
  136. void CConfigFile::dump() const
  137. {
  138. for (auto& it : m_groups) {
  139. printf("[%s]\n", it.key.characters());
  140. for (auto& jt : it.value)
  141. printf("%s=%s\n", jt.key.characters(), jt.value.characters());
  142. printf("\n");
  143. }
  144. }
  145. Vector<String> CConfigFile::groups() const
  146. {
  147. return m_groups.keys();
  148. }
  149. Vector<String> CConfigFile::keys(const String& group) const
  150. {
  151. auto it = m_groups.find(group);
  152. if (it == m_groups.end())
  153. return {};
  154. return it->value.keys();
  155. }
  156. bool CConfigFile::has_key(const String& group, const String& key) const
  157. {
  158. auto it = m_groups.find(group);
  159. if (it == m_groups.end())
  160. return {};
  161. return it->value.contains(key);
  162. }
  163. bool CConfigFile::has_group(const String& group) const
  164. {
  165. return m_groups.contains(group);
  166. }
  167. void CConfigFile::remove_group(const String& group)
  168. {
  169. m_groups.remove(group);
  170. m_dirty = true;
  171. }
  172. void CConfigFile::remove_entry(const String& group, const String& key)
  173. {
  174. auto it = m_groups.find(group);
  175. if (it == m_groups.end())
  176. return;
  177. it->value.remove(key);
  178. m_dirty = true;
  179. }