1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Moz headers (alphabetical)
11 #include "nsINIParser.h"
12 #include "mozilla/ResultExtensions.h"
13 #include "mozilla/Try.h"
14 #include "mozilla/URLPreloader.h"
16 using namespace mozilla
;
18 nsresult
nsINIParser::Init(nsIFile
* aFile
) {
20 MOZ_TRY_VAR(result
, URLPreloader::ReadFile(aFile
));
22 return InitFromString(result
);
25 static const char kNL
[] = "\r\n";
26 static const char kEquals
[] = "=";
27 static const char kWhitespace
[] = " \t";
28 static const char kRBracket
[] = "]";
30 nsresult
nsINIParser::InitFromString(const nsCString
& aStr
) {
31 nsCString fileContents
;
34 if (StringHead(aStr
, 3) == "\xEF\xBB\xBF") {
35 // Someone set us up the Utf-8 BOM
36 // This case is easy, since we assume that BOM-less
37 // files are Utf-8 anyway. Just skip the BOM and process as usual.
38 fileContents
.Append(aStr
);
39 buffer
= fileContents
.BeginWriting() + 3;
41 if (StringHead(aStr
, 2) == "\xFF\xFE") {
42 // Someone set us up the Utf-16LE BOM
43 nsDependentSubstring
str(reinterpret_cast<const char16_t
*>(aStr
.get()),
46 AppendUTF16toUTF8(Substring(str
, 1), fileContents
);
48 fileContents
.Append(aStr
);
51 buffer
= fileContents
.BeginWriting();
54 char* currSection
= nullptr;
56 // outer loop tokenizes into lines
57 while (char* token
= NS_strtok(kNL
, &buffer
)) {
58 if (token
[0] == '#' || token
[0] == ';') { // it's a comment
62 token
= (char*)NS_strspnp(kWhitespace
, token
);
63 if (!*token
) { // empty line
67 if (token
[0] == '[') { // section header!
71 char* rb
= NS_strtok(kRBracket
, &token
);
72 if (!rb
|| NS_strtok(kWhitespace
, &token
)) {
73 // there's either an unclosed [Section or a [Section]Moretext!
74 // we could frankly decide that this INI file is malformed right
75 // here and stop, but we won't... keep going, looking for
76 // a well-formed [section] to continue working with
77 currSection
= nullptr;
84 // If we haven't found a section header (or we found a malformed
85 // section header), don't bother parsing this line.
90 char* e
= NS_strtok(kEquals
, &token
);
95 SetString(currSection
, key
, token
);
101 bool nsINIParser::IsValidSection(const char* aSection
) {
102 if (aSection
[0] == '\0') {
106 const char* found
= strpbrk(aSection
, "\r\n[]");
107 return found
== nullptr;
110 bool nsINIParser::IsValidKey(const char* aKey
) {
111 if (aKey
[0] == '\0') {
115 const char* found
= strpbrk(aKey
, "\r\n=");
116 return found
== nullptr;
119 bool nsINIParser::IsValidValue(const char* aValue
) {
120 const char* found
= strpbrk(aValue
, "\r\n");
121 return found
== nullptr;
124 nsresult
nsINIParser::GetString(const char* aSection
, const char* aKey
,
125 nsACString
& aResult
) {
126 if (!IsValidSection(aSection
) || !IsValidKey(aKey
)) {
127 return NS_ERROR_INVALID_ARG
;
131 mSections
.Get(aSection
, &val
);
134 if (strcmp(val
->key
, aKey
) == 0) {
135 aResult
.Assign(val
->value
);
139 val
= val
->next
.get();
142 return NS_ERROR_FAILURE
;
145 nsresult
nsINIParser::GetString(const char* aSection
, const char* aKey
,
146 char* aResult
, uint32_t aResultLen
) {
147 if (!IsValidSection(aSection
) || !IsValidKey(aKey
)) {
148 return NS_ERROR_INVALID_ARG
;
152 mSections
.Get(aSection
, &val
);
155 if (strcmp(val
->key
, aKey
) == 0) {
156 strncpy(aResult
, val
->value
, aResultLen
);
157 aResult
[aResultLen
- 1] = '\0';
158 if (strlen(val
->value
) >= aResultLen
) {
159 return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA
;
165 val
= val
->next
.get();
168 return NS_ERROR_FAILURE
;
171 nsresult
nsINIParser::GetSections(
172 std::function
<bool(const char*)>&& aCallback
) {
173 for (const auto& key
: mSections
.Keys()) {
174 if (!aCallback(key
)) {
181 nsresult
nsINIParser::GetStrings(
182 const char* aSection
,
183 std::function
<bool(const char*, const char*)>&& aCallback
) {
184 if (!IsValidSection(aSection
)) {
185 return NS_ERROR_INVALID_ARG
;
190 for (mSections
.Get(aSection
, &val
); val
; val
= val
->next
.get()) {
191 if (!aCallback(val
->key
, val
->value
)) {
199 nsresult
nsINIParser::SetString(const char* aSection
, const char* aKey
,
200 const char* aValue
) {
201 if (!IsValidSection(aSection
) || !IsValidKey(aKey
) || !IsValidValue(aValue
)) {
202 return NS_ERROR_INVALID_ARG
;
205 mSections
.WithEntryHandle(aSection
, [&](auto&& entry
) {
207 entry
.Insert(MakeUnique
<INIValue
>(aKey
, aValue
));
211 INIValue
* v
= entry
->get();
213 // Check whether this key has already been specified; overwrite
214 // if so, or append if not.
216 if (!strcmp(aKey
, v
->key
)) {
221 v
->next
= MakeUnique
<INIValue
>(aKey
, aValue
);
226 NS_ASSERTION(v
, "v should never be null coming out of this loop");
232 nsresult
nsINIParser::DeleteString(const char* aSection
, const char* aKey
) {
233 if (!IsValidSection(aSection
) || !IsValidKey(aKey
)) {
234 return NS_ERROR_INVALID_ARG
;
238 if (!mSections
.Get(aSection
, &val
)) {
239 return NS_ERROR_FAILURE
;
242 // Special case the first result
243 if (strcmp(val
->key
, aKey
) == 0) {
245 mSections
.Remove(aSection
);
247 mSections
.InsertOrUpdate(aSection
, std::move(val
->next
));
253 if (strcmp(val
->next
->key
, aKey
) == 0) {
254 val
->next
= std::move(val
->next
->next
);
259 val
= val
->next
.get();
262 return NS_ERROR_FAILURE
;
265 nsresult
nsINIParser::DeleteSection(const char* aSection
) {
266 if (!IsValidSection(aSection
)) {
267 return NS_ERROR_INVALID_ARG
;
270 if (!mSections
.Remove(aSection
)) {
271 return NS_ERROR_FAILURE
;
276 nsresult
nsINIParser::RenameSection(const char* aSection
,
277 const char* aNewName
) {
278 if (!IsValidSection(aSection
) || !IsValidSection(aNewName
)) {
279 return NS_ERROR_INVALID_ARG
;
282 if (mSections
.Contains(aNewName
)) {
283 return NS_ERROR_ILLEGAL_VALUE
;
286 mozilla::UniquePtr
<INIValue
> val
;
287 if (mSections
.Remove(aSection
, &val
)) {
288 mSections
.InsertOrUpdate(aNewName
, std::move(val
));
290 return NS_ERROR_FAILURE
;
296 nsresult
nsINIParser::WriteToFile(nsIFile
* aFile
) {
299 WriteToString(buffer
);
302 nsresult rv
= aFile
->OpenANSIFileDesc("w", &writeFile
);
303 NS_ENSURE_SUCCESS(rv
, rv
);
305 unsigned int length
= buffer
.Length();
307 if (fwrite(buffer
.get(), sizeof(char), length
, writeFile
) != length
) {
309 return NS_ERROR_UNEXPECTED
;
316 void nsINIParser::WriteToString(nsACString
& aOutput
) {
317 for (const auto& entry
: mSections
) {
318 aOutput
.AppendPrintf("[%s]\n", entry
.GetKey());
319 INIValue
* val
= entry
.GetWeak();
321 aOutput
.AppendPrintf("%s=%s\n", val
->key
, val
->value
);
322 val
= val
->next
.get();
324 aOutput
.AppendLiteral("\n");