Skip to content

Commit

Permalink
Simplifications in IniFile
Browse files Browse the repository at this point in the history
  • Loading branch information
aslze committed Jun 7, 2024
1 parent 9e54eb0 commit 78fb0bd
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 69 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ threshold=0.25
```cpp
IniFile config("config.ini");
float threshold = config["parameters/threshold"] | 0.2;
config["parameters/threshold"] = 0.5;
config.set("parameters/threshold", 0.5);
```

Do HTTP requests (you can post a body or send headers, too):
Expand Down
2 changes: 1 addition & 1 deletion doc/doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ With a recent CMake you can also build mbedTLS together with ASL as subprojects
set(ASL_TLS ON)
set(ENABLE_PROGRAMS OFF CACHE BOOL "") # skip samples
FetchContent_Declare(mbedtls URL https://github.com/Mbed-TLS/mbedtls/archive/v3.2.1.zip)
FetchContent_Declare(asl URL https://github.com/aslze/asl/archive/1.11.8.zip)
FetchContent_Declare(asl URL https://github.com/aslze/asl/archive/1.11.11.zip)
FetchContent_MakeAvailable(mbedtls asl)
```
Expand Down
34 changes: 13 additions & 21 deletions include/asl/IniFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ namespace asl {

/**
A utility to read and write configuration files in INI format. On destruction, if there was any variable added or modified
the file is automatically saved. When reading a variable name, it is read from the current section. But if the name
contains a `/` then it is considered a "section/name" pair.
the file is automatically saved. When reading a variable name it is considered a "section/name" pair.
Example:
Expand All @@ -29,7 +28,7 @@ Can be read and written with:
~~~
IniFile config("settings.ini");
int num_retries = config["network/num_retries"];
config["main/color"] = "black";
config.set("main/color", "black");
~~~
The file in that example will be saved if the original `color` in section `main` was not "black".
Expand Down Expand Up @@ -57,26 +56,12 @@ int num_retries = config["network/num_retries"] | 5;
class ASL_API IniFile
{
public:
class Section
{
String _title;
HashDic<String> _vars;
public:
Section() {}
Section(const String& t) : _title(t) {}
const String& title() const {return _title;}
String& operator[](const String& k) {return _vars[k];}
const String& operator[](const String& k) const { return _vars[k]; }
const HashDic<String>& vars() const {return _vars;}
Section clone() const;
bool has(const String& k) const { return _vars.has(k); }
friend class IniFile;
};
typedef HashDic<String> Section;

/**
Opens an INI file from the given file
Opens an INI file from the given path
*/
IniFile(const String& fname, bool shouldwrite = true);
IniFile(const String& path, bool shouldwrite = true);
/**
Destroys the object and save the file if there were modifications
*/
Expand All @@ -95,11 +80,17 @@ class ASL_API IniFile
/**
Returns the value associated with the key `name` in the current section
if the name is like `section/name` then the given section is used.
In the future this might return a const ref, so that modifications must be made with .set(k, v)
*/
String& operator[](const String& name);

const String operator[](const String& name) const;

/**
Sets the value for a key like 'section/name'
*/
void set(const String& name, const String& value);

/**
Returns the value associated with the key `name` or `defaultVal` if it was not found.
*/
Expand All @@ -116,8 +107,9 @@ class ASL_API IniFile

/**
Sets the current section to 'name' (variables will be read from here by default)
\deprecated Use full names like "section/key"
*/
Section& section(const String& name);
ASL_DEPRECATED(Section& section(const String& name), "Use full names like 'section/key'");

const Dic<Section>& sections() const { return _sections; }

Expand Down
89 changes: 48 additions & 41 deletions src/IniFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,8 @@
#include <asl/TextFile.h>

#define NOSECTION "-"
namespace asl {

IniFile::Section IniFile::Section::clone() const
{
IniFile::Section s;
s._title = _title;
s._vars = _vars.clone();
return s;
}
namespace asl {

IniFile::IniFile(const String& fname, bool shouldwrite)
{
Expand Down Expand Up @@ -46,7 +39,7 @@ IniFile::IniFile(const String& fname, bool shouldwrite)
String name = line.substring(1, end);
_currentTitle = name;
if (!_sections.has(_currentTitle))
_sections[_currentTitle] = Section(_currentTitle);
_sections[_currentTitle] = Section();
}
else if(line[i0] != '#' && line[i0] > 47 && line[i0] != ';')
{
Expand All @@ -65,10 +58,9 @@ IniFile::IniFile(const String& fname, bool shouldwrite)
else
break;
}
String key = line.substring(0,i).trimmed();
String value = line.substring(i+1).trimmed();
key.replaceme('/', '\\');
_sections[_currentTitle][key] = value;
String key = line.substring(0,i);
String value = line.substring(i+1);
_sections[_currentTitle][key.trim().replaceme('/', '\\')] = value.trim();
}
}
_currentTitle = NOSECTION;
Expand All @@ -82,7 +74,6 @@ IniFile::IniFile(const String& fname, bool shouldwrite)
IniFile::Section& IniFile::section(const String& name)
{
_currentTitle = name;
_sections[name]._title = name;
return _sections[name];
}

Expand All @@ -91,15 +82,12 @@ String& IniFile::operator[](const String& name)
int slash = name.indexOf('/');
if(slash < 0)
{
_sections[_currentTitle]._title= _currentTitle;
return _sections[_currentTitle][name];
}
else
{
String sec = name.substring(0, slash);
Section& section = _sections[sec];
if (!section._title.ok())
section._title = sec;
return section[name.substring(slash + 1)];
}
}
Expand All @@ -122,47 +110,56 @@ const String IniFile::operator[](const String& name) const
}
}

void IniFile::set(const String& name, const String& value)
{
(*this)[name] = value;
_modified = true;
}

bool IniFile::has(const String& name) const
{
int slash = name.indexOf('/');
if(slash < 0)
{
return _sections[_currentTitle]._vars.has(name);
return _sections[_currentTitle].has(name);
}
else
{
String sec = name.substring(0, slash);
if(!_sections.has(sec))
return false;
return _sections[sec]._vars.has(name.substring(slash+1));
return _sections[sec].has(name.substring(slash+1));
}
}

void IniFile::write(const String& fname)
{
Dic<Section> newsec = _sections.clone();
foreach(Section & s, newsec)
Dic<Section> newsecs = _sections.clone();
foreach(Section & s, newsecs)
s = s.clone();

Section* psection = &_sections[NOSECTION];

Array<String> oldlines = _lines.clone();

String secname = NOSECTION;

foreach(String& line, _lines)
{
int i0 = 0;
while (myisspace(line[i0]) && line[i0] != '\0')
i0++;
char c = line[i0];
if (line[0] == '[')
{
int end = line.indexOf(']');
if (end < 0)
continue;
String name = line.substring(1, end);
_currentTitle = name;
secname = name;
psection = &_sections[name];
}
else if (line[i0] != '#' && line[i0] > 47 && line[i0] != ';')
else if (c != '#' && c >= '0' && c != ';')
{
int i = line.indexOf('=');
if (i < 0)
Expand All @@ -171,37 +168,45 @@ void IniFile::write(const String& fname)
String value0 = line.substring(i+1).trimmed();
const String& value1 = (*psection)[key];
line = _indent; line << key << '=' << value1;
if(value0 != value1 && value1 != "")
if(value0 != value1)
{
_modified = true;
}

newsec[_currentTitle]._vars.remove(key);
newsecs[secname].remove(key);
}
}
Array<String> emptynewsecs;

foreach(Section& section, newsec)
foreach2(String& title, Section& section, newsecs)
{
bool empty = true;
foreach(String& value, section._vars)
foreach(String& value, section)
{
if(value != "")
{
empty = false;
break;
}
}
if(empty)
section._title = "";
emptynewsecs << title;
}

foreach(Section& section, newsec)
foreach(String& s, emptynewsecs)
newsecs.remove(s);

foreach2(String& title, Section& section, newsecs)
{
if(section._vars.length()==0 || section._title == "")
if(section.length()==0)
continue;
int j=-1;
bool notitle = title == NOSECTION;
for(int i=0; i<_lines.length(); i++)
{
if (section.title() == NOSECTION || _lines[i] == '[' + section.title() + ']')
if (notitle || _lines[i] == '[' + title + ']')
{
int k = (section.title() == NOSECTION) ? 0 : 1;
int k = notitle ? 0 : 1;
for(j=i+k; j<_lines.length() && _lines[j][0]!='['; j++)
{}
j--;
Expand All @@ -218,14 +223,16 @@ void IniFile::write(const String& fname)
if(j>0)
while(_lines[j][0]=='\0') j--;
j++;
if (_lines.length() > 0)
_lines.insert(j++, "");
_lines.insert(j++, String(0, "[%s]", *section.title()));
_lines.insert(j++, String::f("[%s]", *title));
}

foreach2(String& name, String& value, section._vars)
String line;
foreach2(String& name, String& value, section)
{
String line = _indent;
line << name << "=" << value;
line.clear();
line << _indent << name << "=" << value;
_lines.insert(j++, line);
_modified = true;
}
Expand All @@ -238,7 +245,7 @@ void IniFile::write(const String& fname)
return;
foreach(String& line, _lines)
{
file.printf("%s\n", *line);
file << line << '\n';
}
file.close();
}
Expand All @@ -255,11 +262,11 @@ IniFile::~IniFile()
Dic<> IniFile::values() const
{
Dic<> vals;
foreach (Section sec, _sections)
foreach2 (String title, const Section& sec, _sections)
{
foreach2 (String& k, const String& v, sec._vars)
foreach2 (String& k, const String& v, sec)
{
vals[sec.title() + "/" + k] = v;
vals[title + "/" + k] = v;
}
}
return vals;
Expand All @@ -271,7 +278,7 @@ Dic<> IniFile::values(const String& secname) const
if (!_sections.has(secname))
return vals;
const Section& sec = _sections[secname];
foreach2 (String& k, const String& v, sec._vars)
foreach2 (String& k, const String& v, sec)
vals[k] = v;
return vals;
}
Expand Down
7 changes: 2 additions & 5 deletions tests/unittests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ ASL_TEST(IniFile)
IniFile file("config.ini");
file["global"] = "global value";
file["sec1/field1"] = "value1";
file["sec1/field2"] = "value2";
file.set("sec1/field2", "value2");
file["sec2/field"] = "value3";
}
{
IniFile file("config.ini");
ASL_ASSERT(file.sections()["sec1"].vars().length() == 2);
//ASL_ASSERT(file.sections()["sec1"].length() == 2);
ASL_ASSERT(file["global"] == "global value");
ASL_ASSERT(file["sec1/field1"] == "value1");
ASL_ASSERT(file["sec1/field2"] == "value2");
Expand All @@ -77,9 +77,6 @@ ASL_TEST(IniFile)
ASL_ASSERT(file("sec1/field1", "none") == "value1");
ASL_ASSERT(file("sec1/field9", "none") == "none");

file.section("sec2");
ASL_ASSERT(file["field"] == "value3");

Dic<> all = file.values();
ASL_ASSERT(all["sec1/field1"] == "value1");
ASL_ASSERT(file.values("sec1")["field1"] == "value1");
Expand Down

0 comments on commit 78fb0bd

Please sign in to comment.