| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | #include <LibCore/CConfigFile.h>
 | 
					
						
							|  |  |  | #include <LibCore/CFile.h>
 | 
					
						
							| 
									
										
										
										
											2019-05-25 16:58:22 -07:00
										 |  |  | #include <LibCore/CUserInfo.h>
 | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | #include <pwd.h>
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-21 18:37:47 +02:00
										 |  |  | NonnullRefPtr<CConfigFile> CConfigFile::get_for_app(const String& app_name) | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-25 16:58:22 -07:00
										 |  |  |     String home_path = get_current_user_home_path(); | 
					
						
							|  |  |  |     if (home_path == "/") | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  |         home_path = String::format("/tmp"); | 
					
						
							|  |  |  |     auto path = String::format("%s/%s.ini", home_path.characters(), app_name.characters()); | 
					
						
							|  |  |  |     return adopt(*new CConfigFile(path)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-21 18:37:47 +02:00
										 |  |  | NonnullRefPtr<CConfigFile> CConfigFile::get_for_system(const String& app_name) | 
					
						
							| 
									
										
										
										
											2019-05-24 21:10:23 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     auto path = String::format("/etc/%s.ini", app_name.characters()); | 
					
						
							|  |  |  |     return adopt(*new CConfigFile(path)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | CConfigFile::CConfigFile(const String& file_name) | 
					
						
							|  |  |  |     : m_file_name(file_name) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     reparse(); | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CConfigFile::~CConfigFile() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     sync(); | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CConfigFile::reparse() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     m_groups.clear(); | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     CFile file(m_file_name); | 
					
						
							|  |  |  |     if (!file.open(CIODevice::OpenMode::ReadOnly)) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     HashMap<String, String>* current_group = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (file.can_read_line()) { | 
					
						
							|  |  |  |         auto line = file.read_line(BUFSIZ); | 
					
						
							|  |  |  |         auto* cp = (const char*)line.pointer(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |         while (*cp && (*cp == ' ' || *cp == '\t' || *cp == '\n')) | 
					
						
							|  |  |  |             ++cp; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         switch (*cp) { | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |         case '\0': // EOL...
 | 
					
						
							|  |  |  |         case '#':  // Comment, skip entire line.
 | 
					
						
							|  |  |  |         case ';':  // -||-
 | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         case '[': { // Start of new group.
 | 
					
						
							|  |  |  |             StringBuilder builder; | 
					
						
							|  |  |  |             ++cp; // Skip the '['
 | 
					
						
							|  |  |  |             while (*cp && (*cp != ']')) | 
					
						
							|  |  |  |                 builder.append(*(cp++)); | 
					
						
							|  |  |  |             current_group = &m_groups.ensure(builder.to_string()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         default: { // Start of key{
 | 
					
						
							|  |  |  |             StringBuilder key_builder; | 
					
						
							|  |  |  |             StringBuilder value_builder; | 
					
						
							|  |  |  |             while (*cp && (*cp != '=')) | 
					
						
							|  |  |  |                 key_builder.append(*(cp++)); | 
					
						
							|  |  |  |             ++cp; // Skip the '='
 | 
					
						
							|  |  |  |             while (*cp && (*cp != '\n')) | 
					
						
							|  |  |  |                 value_builder.append(*(cp++)); | 
					
						
							|  |  |  |             if (!current_group) { | 
					
						
							|  |  |  |                 // We're not in a group yet, create one with the name ""...
 | 
					
						
							|  |  |  |                 current_group = &m_groups.ensure(""); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             current_group->set(key_builder.to_string(), value_builder.to_string()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | String CConfigFile::read_entry(const String& group, const String& key, const String& default_value) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!has_key(group, key)) { | 
					
						
							|  |  |  |         const_cast<CConfigFile&>(*this).write_entry(group, key, default_value); | 
					
						
							|  |  |  |         return default_value; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     auto it = m_groups.find(group); | 
					
						
							|  |  |  |     auto jt = it->value.find(key); | 
					
						
							|  |  |  |     return jt->value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  | int CConfigFile::read_num_entry(const String& group, const String& key, int default_value) const | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!has_key(group, key)) { | 
					
						
							|  |  |  |         const_cast<CConfigFile&>(*this).write_num_entry(group, key, default_value); | 
					
						
							|  |  |  |         return default_value; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool ok; | 
					
						
							|  |  |  |     int value = read_entry(group, key).to_uint(ok); | 
					
						
							|  |  |  |     if (!ok) | 
					
						
							|  |  |  |         return default_value; | 
					
						
							|  |  |  |     return value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  | Color CConfigFile::read_color_entry(const String& group, const String& key, Color default_value) const | 
					
						
							| 
									
										
										
										
											2019-05-24 21:10:23 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!has_key(group, key)) { | 
					
						
							|  |  |  |         const_cast<CConfigFile&>(*this).write_color_entry(group, key, default_value); | 
					
						
							|  |  |  |         return default_value; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-26 10:14:03 -07:00
										 |  |  |     auto shades = read_entry(group, key).split(','); | 
					
						
							|  |  |  |     if (shades.size() < 3) | 
					
						
							|  |  |  |         return default_value; | 
					
						
							|  |  |  |     bool ok1 = true, | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |          ok2 = true, | 
					
						
							|  |  |  |          ok3 = true, | 
					
						
							|  |  |  |          ok4 = true; | 
					
						
							| 
									
										
										
										
											2019-05-24 21:10:23 -07:00
										 |  |  |     Color value; | 
					
						
							| 
									
										
										
										
											2019-05-26 10:14:03 -07:00
										 |  |  |     if (shades.size() == 3) { | 
					
						
							|  |  |  |         value = Color(shades[0].to_uint(ok1), | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |             shades[1].to_uint(ok2), | 
					
						
							|  |  |  |             shades[2].to_uint(ok3)); | 
					
						
							| 
									
										
										
										
											2019-05-26 10:14:03 -07:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         value = Color(shades[0].to_uint(ok1), | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |             shades[1].to_uint(ok2), | 
					
						
							|  |  |  |             shades[2].to_uint(ok3), | 
					
						
							|  |  |  |             shades[3].to_uint(ok4)); | 
					
						
							| 
									
										
										
										
											2019-05-26 10:14:03 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (!(ok1 && ok2 && ok3 && ok4)) | 
					
						
							| 
									
										
										
										
											2019-05-24 21:10:23 -07:00
										 |  |  |         return default_value; | 
					
						
							|  |  |  |     return value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  | bool CConfigFile::read_bool_entry(const String& group, const String& key, bool default_value) const | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     return read_entry(group, key, default_value ? "1" : "0") == "1"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CConfigFile::write_entry(const String& group, const String& key, const String& value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_groups.ensure(group).ensure(key) = value; | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     m_dirty = true; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CConfigFile::write_num_entry(const String& group, const String& key, int value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-07-03 14:56:27 +02:00
										 |  |  |     write_entry(group, key, String::number(value)); | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-01 15:54:30 +10:00
										 |  |  | void CConfigFile::write_bool_entry(const String& group, const String& key, bool value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     write_entry(group, key, value ? "1" : "0"); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-05-24 21:10:23 -07:00
										 |  |  | void CConfigFile::write_color_entry(const String& group, const String& key, Color value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     write_entry(group, key, String::format("%d,%d,%d,%d", value.red(), value.green(), value.blue(), value.alpha())); | 
					
						
							| 
									
										
										
										
											2019-05-24 21:10:23 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool CConfigFile::sync() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!m_dirty) | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     FILE* fp = fopen(m_file_name.characters(), "wb"); | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  |     if (!fp) | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (auto& it : m_groups) { | 
					
						
							|  |  |  |         fprintf(fp, "[%s]\n", it.key.characters()); | 
					
						
							|  |  |  |         for (auto& jt : it.value) | 
					
						
							|  |  |  |             fprintf(fp, "%s=%s\n", jt.key.characters(), jt.value.characters()); | 
					
						
							|  |  |  |         fprintf(fp, "\n"); | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     fclose(fp); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     m_dirty = false; | 
					
						
							|  |  |  |     return true; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CConfigFile::dump() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     for (auto& it : m_groups) { | 
					
						
							|  |  |  |         printf("[%s]\n", it.key.characters()); | 
					
						
							|  |  |  |         for (auto& jt : it.value) | 
					
						
							|  |  |  |             printf("%s=%s\n", jt.key.characters(), jt.value.characters()); | 
					
						
							|  |  |  |         printf("\n"); | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<String> CConfigFile::groups() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return m_groups.keys(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<String> CConfigFile::keys(const String& group) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto it = m_groups.find(group); | 
					
						
							|  |  |  |     if (it == m_groups.end()) | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  |     return it->value.keys(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CConfigFile::has_key(const String& group, const String& key) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto it = m_groups.find(group); | 
					
						
							|  |  |  |     if (it == m_groups.end()) | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  |     return it->value.contains(key); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CConfigFile::has_group(const String& group) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return m_groups.contains(group); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CConfigFile::remove_group(const String& group) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_groups.remove(group); | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     m_dirty = true; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CConfigFile::remove_entry(const String& group, const String& key) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto it = m_groups.find(group); | 
					
						
							|  |  |  |     if (it == m_groups.end()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     it->value.remove(key); | 
					
						
							| 
									
										
										
										
											2019-05-28 11:53:16 +02:00
										 |  |  |     m_dirty = true; | 
					
						
							| 
									
										
										
										
											2019-04-15 02:22:08 +02:00
										 |  |  | } |