/src/mozilla-central/xpcom/base/nsINIParser.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | // Moz headers (alphabetical) |
8 | | #include "nsCRTGlue.h" |
9 | | #include "nsError.h" |
10 | | #include "nsIFile.h" |
11 | | #include "nsINIParser.h" |
12 | | #include "mozilla/ResultExtensions.h" |
13 | | #include "mozilla/URLPreloader.h" |
14 | | |
15 | | using namespace mozilla; |
16 | | |
17 | | nsresult |
18 | | nsINIParser::Init(nsIFile* aFile) |
19 | 0 | { |
20 | 0 | nsCString result; |
21 | 0 | MOZ_TRY_VAR(result, URLPreloader::ReadFile(aFile)); |
22 | 0 |
|
23 | 0 | return InitFromString(result); |
24 | 0 | } |
25 | | |
26 | | static const char kNL[] = "\r\n"; |
27 | | static const char kEquals[] = "="; |
28 | | static const char kWhitespace[] = " \t"; |
29 | | static const char kRBracket[] = "]"; |
30 | | |
31 | | nsresult |
32 | | nsINIParser::InitFromString(const nsCString& aStr) |
33 | 0 | { |
34 | 0 | nsCString fileContents; |
35 | 0 | char* buffer; |
36 | 0 |
|
37 | 0 | if (StringHead(aStr, 3) == "\xEF\xBB\xBF") { |
38 | 0 | // Someone set us up the Utf-8 BOM |
39 | 0 | // This case is easy, since we assume that BOM-less |
40 | 0 | // files are Utf-8 anyway. Just skip the BOM and process as usual. |
41 | 0 | fileContents.Append(aStr); |
42 | 0 | buffer = fileContents.BeginWriting() + 3; |
43 | 0 | } else { |
44 | 0 | if (StringHead(aStr, 2) == "\xFF\xFE") { |
45 | 0 | // Someone set us up the Utf-16LE BOM |
46 | 0 | nsDependentSubstring str(reinterpret_cast<const char16_t*>(aStr.get()), |
47 | 0 | aStr.Length() / 2); |
48 | 0 |
|
49 | 0 | AppendUTF16toUTF8(Substring(str, 1), fileContents); |
50 | 0 | } else { |
51 | 0 | fileContents.Append(aStr); |
52 | 0 | } |
53 | 0 |
|
54 | 0 | buffer = fileContents.BeginWriting(); |
55 | 0 | } |
56 | 0 |
|
57 | 0 | char* currSection = nullptr; |
58 | 0 |
|
59 | 0 | // outer loop tokenizes into lines |
60 | 0 | while (char* token = NS_strtok(kNL, &buffer)) { |
61 | 0 | if (token[0] == '#' || token[0] == ';') { // it's a comment |
62 | 0 | continue; |
63 | 0 | } |
64 | 0 | |
65 | 0 | token = (char*)NS_strspnp(kWhitespace, token); |
66 | 0 | if (!*token) { // empty line |
67 | 0 | continue; |
68 | 0 | } |
69 | 0 | |
70 | 0 | if (token[0] == '[') { // section header! |
71 | 0 | ++token; |
72 | 0 | currSection = token; |
73 | 0 |
|
74 | 0 | char* rb = NS_strtok(kRBracket, &token); |
75 | 0 | if (!rb || NS_strtok(kWhitespace, &token)) { |
76 | 0 | // there's either an unclosed [Section or a [Section]Moretext! |
77 | 0 | // we could frankly decide that this INI file is malformed right |
78 | 0 | // here and stop, but we won't... keep going, looking for |
79 | 0 | // a well-formed [section] to continue working with |
80 | 0 | currSection = nullptr; |
81 | 0 | } |
82 | 0 |
|
83 | 0 | continue; |
84 | 0 | } |
85 | 0 |
|
86 | 0 | if (!currSection) { |
87 | 0 | // If we haven't found a section header (or we found a malformed |
88 | 0 | // section header), don't bother parsing this line. |
89 | 0 | continue; |
90 | 0 | } |
91 | 0 | |
92 | 0 | char* key = token; |
93 | 0 | char* e = NS_strtok(kEquals, &token); |
94 | 0 | if (!e || !token) { |
95 | 0 | continue; |
96 | 0 | } |
97 | 0 | |
98 | 0 | SetString(currSection, key, token); |
99 | 0 | } |
100 | 0 |
|
101 | 0 | return NS_OK; |
102 | 0 | } |
103 | | |
104 | | bool |
105 | | nsINIParser::IsValidSection(const char* aSection) |
106 | 0 | { |
107 | 0 | if (aSection[0] == '\0') { |
108 | 0 | return false; |
109 | 0 | } |
110 | 0 | |
111 | 0 | const char* found = strpbrk(aSection, "\r\n[]"); |
112 | 0 | return found == nullptr; |
113 | 0 | } |
114 | | |
115 | | bool |
116 | | nsINIParser::IsValidKey(const char* aKey) |
117 | 0 | { |
118 | 0 | if (aKey[0] == '\0') { |
119 | 0 | return false; |
120 | 0 | } |
121 | 0 | |
122 | 0 | const char* found = strpbrk(aKey, "\r\n="); |
123 | 0 | return found == nullptr; |
124 | 0 | } |
125 | | |
126 | | bool |
127 | | nsINIParser::IsValidValue(const char* aValue) |
128 | 0 | { |
129 | 0 | const char* found = strpbrk(aValue, "\r\n"); |
130 | 0 | return found == nullptr; |
131 | 0 | } |
132 | | |
133 | | nsresult |
134 | | nsINIParser::GetString(const char* aSection, const char* aKey, |
135 | | nsACString& aResult) |
136 | 0 | { |
137 | 0 | if (!IsValidSection(aSection) || !IsValidKey(aKey)) { |
138 | 0 | return NS_ERROR_INVALID_ARG; |
139 | 0 | } |
140 | 0 | |
141 | 0 | INIValue* val; |
142 | 0 | mSections.Get(aSection, &val); |
143 | 0 |
|
144 | 0 | while (val) { |
145 | 0 | if (strcmp(val->key, aKey) == 0) { |
146 | 0 | aResult.Assign(val->value); |
147 | 0 | return NS_OK; |
148 | 0 | } |
149 | 0 | |
150 | 0 | val = val->next.get(); |
151 | 0 | } |
152 | 0 |
|
153 | 0 | return NS_ERROR_FAILURE; |
154 | 0 | } |
155 | | |
156 | | nsresult |
157 | | nsINIParser::GetString(const char* aSection, const char* aKey, |
158 | | char* aResult, uint32_t aResultLen) |
159 | 0 | { |
160 | 0 | if (!IsValidSection(aSection) || !IsValidKey(aKey)) { |
161 | 0 | return NS_ERROR_INVALID_ARG; |
162 | 0 | } |
163 | 0 | |
164 | 0 | INIValue* val; |
165 | 0 | mSections.Get(aSection, &val); |
166 | 0 |
|
167 | 0 | while (val) { |
168 | 0 | if (strcmp(val->key, aKey) == 0) { |
169 | 0 | strncpy(aResult, val->value, aResultLen); |
170 | 0 | aResult[aResultLen - 1] = '\0'; |
171 | 0 | if (strlen(val->value) >= aResultLen) { |
172 | 0 | return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; |
173 | 0 | } |
174 | 0 | |
175 | 0 | return NS_OK; |
176 | 0 | } |
177 | 0 | |
178 | 0 | val = val->next.get(); |
179 | 0 | } |
180 | 0 |
|
181 | 0 | return NS_ERROR_FAILURE; |
182 | 0 | } |
183 | | |
184 | | nsresult |
185 | | nsINIParser::GetSections(INISectionCallback aCB, void* aClosure) |
186 | 0 | { |
187 | 0 | for (auto iter = mSections.Iter(); !iter.Done(); iter.Next()) { |
188 | 0 | if (!aCB(iter.Key(), aClosure)) { |
189 | 0 | break; |
190 | 0 | } |
191 | 0 | } |
192 | 0 | return NS_OK; |
193 | 0 | } |
194 | | |
195 | | nsresult |
196 | | nsINIParser::GetStrings(const char* aSection, |
197 | | INIStringCallback aCB, void* aClosure) |
198 | 0 | { |
199 | 0 | if (!IsValidSection(aSection)) { |
200 | 0 | return NS_ERROR_INVALID_ARG; |
201 | 0 | } |
202 | 0 | |
203 | 0 | INIValue* val; |
204 | 0 |
|
205 | 0 | for (mSections.Get(aSection, &val); |
206 | 0 | val; |
207 | 0 | val = val->next.get()) { |
208 | 0 |
|
209 | 0 | if (!aCB(val->key, val->value, aClosure)) { |
210 | 0 | return NS_OK; |
211 | 0 | } |
212 | 0 | } |
213 | 0 |
|
214 | 0 | return NS_OK; |
215 | 0 | } |
216 | | |
217 | | nsresult |
218 | | nsINIParser::SetString(const char* aSection, const char* aKey, const char* aValue) |
219 | 0 | { |
220 | 0 | if (!IsValidSection(aSection) || !IsValidKey(aKey) || !IsValidValue(aValue)) { |
221 | 0 | return NS_ERROR_INVALID_ARG; |
222 | 0 | } |
223 | 0 | |
224 | 0 | INIValue* v; |
225 | 0 | if (!mSections.Get(aSection, &v)) { |
226 | 0 | v = new INIValue(aKey, aValue); |
227 | 0 |
|
228 | 0 | mSections.Put(aSection, v); |
229 | 0 | return NS_OK; |
230 | 0 | } |
231 | 0 | |
232 | 0 | // Check whether this key has already been specified; overwrite |
233 | 0 | // if so, or append if not. |
234 | 0 | while (v) { |
235 | 0 | if (!strcmp(aKey, v->key)) { |
236 | 0 | v->SetValue(aValue); |
237 | 0 | break; |
238 | 0 | } |
239 | 0 | if (!v->next) { |
240 | 0 | v->next = MakeUnique<INIValue>(aKey, aValue); |
241 | 0 | break; |
242 | 0 | } |
243 | 0 | v = v->next.get(); |
244 | 0 | } |
245 | 0 | NS_ASSERTION(v, "v should never be null coming out of this loop"); |
246 | 0 |
|
247 | 0 | return NS_OK; |
248 | 0 | } |
249 | | |
250 | | nsresult |
251 | | nsINIParser::DeleteString(const char* aSection, const char* aKey) |
252 | 0 | { |
253 | 0 | if (!IsValidSection(aSection) || !IsValidKey(aKey)) { |
254 | 0 | return NS_ERROR_INVALID_ARG; |
255 | 0 | } |
256 | 0 | |
257 | 0 | INIValue* val; |
258 | 0 | if (!mSections.Get(aSection, &val)) { |
259 | 0 | return NS_ERROR_FAILURE; |
260 | 0 | } |
261 | 0 | |
262 | 0 | // Special case the first result |
263 | 0 | if (strcmp(val->key, aKey) == 0) { |
264 | 0 | if (!val->next) { |
265 | 0 | mSections.Remove(aSection); |
266 | 0 | } else { |
267 | 0 | mSections.Put(aSection, val->next.release()); |
268 | 0 | delete val; |
269 | 0 | } |
270 | 0 | return NS_OK; |
271 | 0 | } |
272 | 0 |
|
273 | 0 | while (val->next) { |
274 | 0 | if (strcmp(val->next->key, aKey) == 0) { |
275 | 0 | val->next = std::move(val->next->next); |
276 | 0 |
|
277 | 0 | return NS_OK; |
278 | 0 | } |
279 | 0 | |
280 | 0 | val = val->next.get(); |
281 | 0 | } |
282 | 0 |
|
283 | 0 | return NS_ERROR_FAILURE; |
284 | 0 | } |
285 | | |
286 | | nsresult |
287 | | nsINIParser::DeleteSection(const char* aSection) |
288 | 0 | { |
289 | 0 | if (!IsValidSection(aSection)) { |
290 | 0 | return NS_ERROR_INVALID_ARG; |
291 | 0 | } |
292 | 0 | |
293 | 0 | if (!mSections.Remove(aSection)) { |
294 | 0 | return NS_ERROR_FAILURE; |
295 | 0 | } |
296 | 0 | return NS_OK; |
297 | 0 | } |
298 | | |
299 | | nsresult |
300 | 0 | nsINIParser::WriteToFile(nsIFile *aFile) { |
301 | 0 | nsCString buffer; |
302 | 0 |
|
303 | 0 | for (auto iter = mSections.Iter(); !iter.Done(); iter.Next()) { |
304 | 0 | buffer.AppendPrintf("[%s]\n", iter.Key()); |
305 | 0 | INIValue* val = iter.Data(); |
306 | 0 | while (val) { |
307 | 0 | buffer.AppendPrintf("%s=%s\n", val->key, val->value); |
308 | 0 | val = val->next.get(); |
309 | 0 | } |
310 | 0 | buffer.AppendLiteral("\n"); |
311 | 0 | } |
312 | 0 |
|
313 | 0 | FILE* writeFile; |
314 | 0 | nsresult rv = aFile->OpenANSIFileDesc("w", &writeFile); |
315 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
316 | 0 |
|
317 | 0 | unsigned int length = buffer.Length(); |
318 | 0 |
|
319 | 0 | if (fwrite(buffer.get(), sizeof(char), length, writeFile) != length) { |
320 | 0 | fclose(writeFile); |
321 | 0 | return NS_ERROR_UNEXPECTED; |
322 | 0 | } |
323 | 0 | |
324 | 0 | fclose(writeFile); |
325 | 0 | return NS_OK; |
326 | 0 | } |