8#define IDEBUG(...) Ase::debug ("inifile", __VA_ARGS__)
10#define ISASCIINLSPACE(c) (c == ' ' || (c >= 9 && c <= 13))
11#define ISASCIIWHITESPACE(c) (c == ' ' || c == '\t' || (c >= 11 && c <= 13))
42parse_whitespaces (
const char **stringp,
int min_spaces)
44 const char *p = *stringp;
45 while (ISASCIIWHITESPACE (*p))
47 if (p - *stringp >= min_spaces)
56skip_whitespaces (
const char **stringp)
58 return parse_whitespaces (stringp, 0);
62scan_escaped (
const char **stringp,
size_t *linenop,
const char term)
64 const char *p = *stringp;
65 size_t lineno = *linenop;
74 else if (p[0] ==
'\\' && p[1])
88 const char *s = str.
data();
89 const char *e = s + str.size();
90 while (e > s && ISASCIINLSPACE (e[-1]))
92 const size_t l = e - s;
93 str.erase (str.begin() + l, str.end());
94 return String (str.data(), str.size());
98scan_value (
const char **stringp,
size_t *linenop,
String *valuep,
const char *termchars =
"")
100 const char *p = *stringp;
101 size_t lineno = *linenop;
109 if (p[1] ==
'\n' || (p[1] ==
'\r' && p[2] ==
'\n'))
111 p += 1 + (p[1] ==
'\r');
118 v +=
String (
"\\") + p[0];
123 if (scan_escaped (&p, &lineno, d[0]))
132 if (p[1] && p[1] !=
'\n')
136 if (!strchr (termchars, p[0]))
142 case 0:
case '\n':
case ';':
case '#':
145 *valuep = string_rtrim (v);
151skip_line (
const char **stringp,
size_t *linenop,
String *textp)
153 const char *p = *stringp, *
const start = p;
154 size_t lineno = *linenop;
155 while (*p && *p !=
'\n')
158 *textp =
String (start, p - start);
170skip_commentline (
const char **stringp,
size_t *linenop,
String *commentp = NULL)
172 const char *p = *stringp;
173 skip_whitespaces (&p);
174 if (*p !=
'#' && *p !=
';')
178 return skip_line (stringp, linenop, commentp);
182skip_to_eol (
const char **stringp,
size_t *linenop)
184 const char *p = *stringp;
185 if (*p ==
'#' || *p ==
';')
186 return skip_commentline (stringp, linenop);
205parse_assignment (
const char **stringp,
size_t *linenop,
String *keyp,
String *localep,
String *valuep)
207 const char *p = *stringp;
208 size_t lineno = *linenop;
209 String key, locale, value;
211 success = success && skip_whitespaces (&p);
212 success = success && scan_value (&p, &lineno, &key,
"[]=:");
213 success = success && skip_whitespaces (&p);
214 if (success && *p ==
'[')
217 success = success && skip_whitespaces (&p);
218 success = success && scan_value (&p, &lineno, &locale,
"[]");
219 success = success && skip_whitespaces (&p);
223 success = success && skip_whitespaces (&p);
227 if (*p !=
'=' && *p !=
':')
230 success = success && skip_whitespaces (&p);
231 success = success && scan_value (&p, &lineno, &value);
232 success = success && skip_to_eol (&p, &lineno);
244parse_section (
const char **stringp,
size_t *linenop,
String *sectionp)
246 const char *p = *stringp;
247 size_t lineno = *linenop;
250 success = success && skip_whitespaces (&p);
254 success = success && skip_whitespaces (&p);
255 success = success && scan_value (&p, &lineno, §ion,
"[]");
256 success = success && skip_whitespaces (&p);
260 success = success && skip_whitespaces (&p);
261 success = success && skip_to_eol (&p, &lineno);
271IniFile::load_ini (
const String &inputname,
const String &data)
273 const char *p =
data.c_str();
278 const size_t lineno = nextno;
279 String text, key, locale, *debugp = 0 ? &text : NULL;
280 if (skip_commentline (&p, &nextno, debugp))
283 printerr (
"%s:%d: #%s\n", inputname.c_str(), lineno, debugp->c_str());
285 else if (parse_section (&p, &nextno, &text))
288 printerr (
"%s:%d: %s\n", inputname.c_str(), lineno, text.c_str());
290 if (strchr (section.c_str(),
'"'))
294 if (s.c_str()[0] ==
'"')
299 else if (parse_assignment (&p, &nextno, &key, &locale, &text))
302 printerr (
"%s:%d:\t%s[%s] = %s\n", inputname.c_str(), lineno, key.c_str(), locale.c_str(),
string_to_cquote (text));
305 k +=
"[" + locale +
"]";
306 if (strchr (section.c_str(),
'=') ||
strchr (key.c_str(),
'.'))
307 IDEBUG (
"%s:%d: invalid key name: %s.%s", inputname.c_str(), lineno, section.c_str(), k.c_str());
309 sections_[section].push_back (k +
"=" + text);
311 else if (skip_line (&p, &nextno, debugp))
314 printerr (
"%s:%d:~ %s\n", inputname.c_str(), lineno, debugp->c_str());
323 load_ini (name, inidata);
324 if (sections_.
empty())
332 if (sections_.
empty())
344 sections_ = source.sections_;
351 return !sections_.
empty();
355IniFile::section (
const String &name)
const
357 SectionMap::const_iterator cit = sections_.
find (name);
358 if (cit != sections_.
end())
360 static const StringS empty_dummy;
367 SectionMap::const_iterator cit = sections_.
find (section);
368 return cit != sections_.
end();
375 for (
auto it : sections_)
384 SectionMap::const_iterator cit = sections_.
find (section);
385 if (cit != sections_.
end())
386 for (
auto s : cit->second)
387 opts.
push_back (s.substr (0, s.find (
'=')));
394 SectionMap::const_iterator cit = sections_.
find (section);
395 if (cit == sections_.
end())
397 for (
auto s : cit->second)
407 for (
auto it : sections_)
408 for (
auto s : it.second)
421 const StringS &sv = section (secname);
424 const size_t l = dotpath.
size() - (d - p);
426 if (kv.size() > l && kv[l] ==
'=' &&
memcmp (kv.data(), d, l) == 0)
429 *valuep = kv.
substr (l + 1);
448 for (
const char *p = input.
c_str(); *p; p++)
456 case 'n': v +=
'\n';
break;
457 case 'r': v +=
'\r';
break;
458 case 't': v +=
'\t';
break;
459 case 'b': v +=
'\b';
break;
460 case 'f': v +=
'\f';
break;
461 case 'v': v +=
'\v';
break;
462 default: v += p[1];
break;
468 if (scan_escaped (&p, &dummy, start[-1]))
507IniWriter::find_section (
String name,
bool create)
509 for (
size_t i = 0; i < sections_.
size(); i++)
510 if (sections_[i].name == name)
511 return §ions_[i];
514 const size_t i = sections_.
size();
516 sections_[i].name = name;
517 return §ions_[i];
523IniWriter::find_entry (IniWriter::Section §ion,
String name,
bool create)
525 for (
size_t i = 0; i < section.entries.size(); i++)
526 if (section.entries[i].size() > name.size() &&
527 section.entries[i][name.size()] ==
'=' &&
528 section.entries[i].compare (0, name.size(), name) == 0)
532 const size_t i = section.entries.
size();
533 section.entries.push_back (name +
"=");
543 const size_t p = key.
rfind (
'.');
544 if (p <= 0 || p + 1 >= key.
size())
546 warning (
"%s: invalid key: %s", __func__, key);
549 Section *section = find_section (key.
substr (0, p),
true);
551 const size_t idx = find_entry (*section, entry_key,
true);
552 section->entries[idx] = entry_key +
"=" + value;
560 for (
size_t i = 0; i < sections_.
size(); i++)
561 if (!sections_[i].entries.
empty())
563 String sec = sections_[i].name;
565 if (d >= 0 && d < sec.
size())
567 s +=
String (
"[") + sec +
"]\n";
568 for (
size_t j = 0; j < sections_[i].entries.
size(); j++)
570 const String raw = sections_[i].entries[j];
571 const size_t p = raw.
find (
'=');
Binary large object storage container.
String name()
Retrieve the Blob's filename or url.
String string()
Copy Blob data into a zero terminated string.
Class to parse INI configuration file sections and values.
bool has_value(const String &dotpath, String *valuep=NULL) const
Check and possibly retrieve value if present.
bool has_raw_value(const String &dotpath, String *valuep=NULL) const
Check and possibly retrieve raw value if present.
StringS attributes(const String §ion) const
List all attributes available in section.
bool has_attribute(const String §ion, const String &key) const
Return if section contains key.
IniFile & operator=(const IniFile &source)
Assignment operator.
bool has_sections() const
Checks if IniFile is non-empty.
bool has_section(const String §ion) const
Check presence of a section.
static String cook_string(const String &input_string)
Unquote contents of input_string
StringS sections() const
List all sections.
String raw_value(const String &dotpath) const
Retrieve raw (uncooked) value of section.attribute[locale].
IniFile(const String &name, const String &inidata)
Load INI file from immediate data.
StringS raw_values() const
List all section.attribute=value pairs.
String value_as_string(const String &dotpath) const
Retrieve value of section.attribute[locale].
void set(String key, String value)
Set (or add) a value with INI file semantics: section.key = value.
String output()
Generate INI file syntax for all values store in the class.
The Anklang C++ API namespace.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
String string_from_cquote(const String &input)
Parse a possibly quoted C string into regular string.
String string_join(const String &junctor, const StringS &strvec)
StringS string_split(const String &string, const String &splitter, size_t maxn)
bool string_is_canonified(const String &string, const String &valid_chars)
Check if string_canonify() would modify string.
std::vector< String > StringS
Convenience alias for a std::vector<std::string>.
const String & string_set_ascii_alnum()
Returns a string containing all of 0-9, A-Z and a-z.
std::string String
Convenience alias for std::string.
String string_to_cquote(const String &str)
Returns a string as C string including double quotes.