Line data Source code
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/FileUtils.h" // AutoFILE
13 :
14 : // System headers (alphabetical)
15 : #include <stdio.h>
16 : #include <stdlib.h>
17 : #ifdef XP_WIN
18 : #include <windows.h>
19 : #endif
20 :
21 : #if defined(XP_WIN)
22 : #define READ_BINARYMODE L"rb"
23 : #else
24 : #define READ_BINARYMODE "r"
25 : #endif
26 :
27 : using namespace mozilla;
28 :
29 : #ifdef XP_WIN
30 : inline FILE*
31 : TS_tfopen(const char* aPath, const wchar_t* aMode)
32 : {
33 : wchar_t wPath[MAX_PATH];
34 : MultiByteToWideChar(CP_UTF8, 0, aPath, -1, wPath, MAX_PATH);
35 : return _wfopen(wPath, aMode);
36 : }
37 : #else
38 : inline FILE*
39 0 : TS_tfopen(const char* aPath, const char* aMode)
40 : {
41 0 : return fopen(aPath, aMode);
42 : }
43 : #endif
44 :
45 : // Stack based FILE wrapper to ensure that fclose is called, copied from
46 : // toolkit/mozapps/update/updater/readstrings.cpp
47 :
48 : class AutoFILE
49 : {
50 : public:
51 2 : explicit AutoFILE(FILE* aFp = nullptr) : fp_(aFp) {}
52 2 : ~AutoFILE()
53 2 : {
54 2 : if (fp_) {
55 2 : fclose(fp_);
56 : }
57 2 : }
58 4 : operator FILE*() { return fp_; }
59 : FILE** operator&() { return &fp_; }
60 2 : void operator=(FILE* aFp) { fp_ = aFp; }
61 : private:
62 : FILE* fp_;
63 : };
64 :
65 : nsresult
66 2 : nsINIParser::Init(nsIFile* aFile)
67 : {
68 : /* open the file. Don't use OpenANSIFileDesc, because you mustn't
69 : pass FILE* across shared library boundaries, which may be using
70 : different CRTs */
71 :
72 4 : AutoFILE fd;
73 :
74 : #ifdef XP_WIN
75 : nsAutoString path;
76 : nsresult rv = aFile->GetPath(path);
77 : if (NS_WARN_IF(NS_FAILED(rv))) {
78 : return rv;
79 : }
80 :
81 : fd = _wfopen(path.get(), READ_BINARYMODE);
82 : #else
83 4 : nsAutoCString path;
84 2 : aFile->GetNativePath(path);
85 :
86 2 : fd = fopen(path.get(), READ_BINARYMODE);
87 : #endif
88 :
89 2 : if (!fd) {
90 0 : return NS_ERROR_FAILURE;
91 : }
92 :
93 2 : return InitFromFILE(fd);
94 : }
95 :
96 : nsresult
97 0 : nsINIParser::Init(const char* aPath)
98 : {
99 : /* open the file */
100 0 : AutoFILE fd(TS_tfopen(aPath, READ_BINARYMODE));
101 0 : if (!fd) {
102 0 : return NS_ERROR_FAILURE;
103 : }
104 :
105 0 : return InitFromFILE(fd);
106 : }
107 :
108 : static const char kNL[] = "\r\n";
109 : static const char kEquals[] = "=";
110 : static const char kWhitespace[] = " \t";
111 : static const char kRBracket[] = "]";
112 :
113 : nsresult
114 2 : nsINIParser::InitFromFILE(FILE* aFd)
115 : {
116 : /* get file size */
117 2 : if (fseek(aFd, 0, SEEK_END) != 0) {
118 0 : return NS_ERROR_FAILURE;
119 : }
120 :
121 2 : long flen = ftell(aFd);
122 : /* zero-sized file, or an error */
123 2 : if (flen <= 0) {
124 0 : return NS_ERROR_FAILURE;
125 : }
126 :
127 : /* malloc an internal buf the size of the file */
128 2 : mFileContents = MakeUnique<char[]>(flen + 2);
129 2 : if (!mFileContents) {
130 0 : return NS_ERROR_OUT_OF_MEMORY;
131 : }
132 :
133 : /* read the file in one swoop */
134 2 : if (fseek(aFd, 0, SEEK_SET) != 0) {
135 0 : return NS_BASE_STREAM_OSERROR;
136 : }
137 :
138 2 : int rd = fread(mFileContents.get(), sizeof(char), flen, aFd);
139 2 : if (rd != flen) {
140 0 : return NS_BASE_STREAM_OSERROR;
141 : }
142 :
143 : // We write a UTF16 null so that the file is easier to convert to UTF8
144 2 : mFileContents[flen] = mFileContents[flen + 1] = '\0';
145 :
146 2 : char* buffer = &mFileContents[0];
147 :
148 4 : if (flen >= 3 &&
149 2 : mFileContents[0] == '\xEF' &&
150 2 : mFileContents[1] == '\xBB' &&
151 0 : mFileContents[2] == '\xBF') {
152 : // Someone set us up the Utf-8 BOM
153 : // This case is easy, since we assume that BOM-less
154 : // files are Utf-8 anyway. Just skip the BOM and process as usual.
155 0 : buffer = &mFileContents[3];
156 : }
157 :
158 : #ifdef XP_WIN
159 : if (flen >= 2 &&
160 : mFileContents[0] == '\xFF' &&
161 : mFileContents[1] == '\xFE') {
162 : // Someone set us up the Utf-16LE BOM
163 : buffer = &mFileContents[2];
164 : // Get the size required for our Utf8 buffer
165 : flen = WideCharToMultiByte(CP_UTF8,
166 : 0,
167 : reinterpret_cast<LPWSTR>(buffer),
168 : -1,
169 : nullptr,
170 : 0,
171 : nullptr,
172 : nullptr);
173 : if (flen == 0) {
174 : return NS_ERROR_FAILURE;
175 : }
176 :
177 : UniquePtr<char[]> utf8Buffer(new char[flen]);
178 : if (WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPWSTR>(buffer), -1,
179 : utf8Buffer.get(), flen, nullptr, nullptr) == 0) {
180 : return NS_ERROR_FAILURE;
181 : }
182 : mFileContents = Move(utf8Buffer);
183 : buffer = mFileContents.get();
184 : }
185 : #endif
186 :
187 2 : char* currSection = nullptr;
188 :
189 : // outer loop tokenizes into lines
190 13 : while (char* token = NS_strtok(kNL, &buffer)) {
191 11 : if (token[0] == '#' || token[0] == ';') { // it's a comment
192 6 : continue;
193 : }
194 :
195 11 : token = (char*)NS_strspnp(kWhitespace, token);
196 11 : if (!*token) { // empty line
197 0 : continue;
198 : }
199 :
200 11 : if (token[0] == '[') { // section header!
201 3 : ++token;
202 3 : currSection = token;
203 :
204 3 : char* rb = NS_strtok(kRBracket, &token);
205 3 : if (!rb || NS_strtok(kWhitespace, &token)) {
206 : // there's either an unclosed [Section or a [Section]Moretext!
207 : // we could frankly decide that this INI file is malformed right
208 : // here and stop, but we won't... keep going, looking for
209 : // a well-formed [section] to continue working with
210 0 : currSection = nullptr;
211 : }
212 :
213 3 : continue;
214 : }
215 :
216 8 : if (!currSection) {
217 : // If we haven't found a section header (or we found a malformed
218 : // section header), don't bother parsing this line.
219 0 : continue;
220 : }
221 :
222 8 : char* key = token;
223 8 : char* e = NS_strtok(kEquals, &token);
224 8 : if (!e || !token) {
225 0 : continue;
226 : }
227 :
228 : INIValue* v;
229 8 : if (!mSections.Get(currSection, &v)) {
230 3 : v = new INIValue(key, token);
231 3 : if (!v) {
232 0 : return NS_ERROR_OUT_OF_MEMORY;
233 : }
234 :
235 3 : mSections.Put(currSection, v);
236 3 : continue;
237 : }
238 :
239 : // Check whether this key has already been specified; overwrite
240 : // if so, or append if not.
241 13 : while (v) {
242 9 : if (!strcmp(key, v->key)) {
243 0 : v->value = token;
244 0 : break;
245 : }
246 9 : if (!v->next) {
247 5 : v->next = MakeUnique<INIValue>(key, token);
248 5 : if (!v->next) {
249 0 : return NS_ERROR_OUT_OF_MEMORY;
250 : }
251 5 : break;
252 : }
253 4 : v = v->next.get();
254 : }
255 5 : NS_ASSERTION(v, "v should never be null coming out of this loop");
256 11 : }
257 :
258 2 : return NS_OK;
259 : }
260 :
261 : nsresult
262 11 : nsINIParser::GetString(const char* aSection, const char* aKey,
263 : nsACString& aResult)
264 : {
265 : INIValue* val;
266 11 : mSections.Get(aSection, &val);
267 :
268 43 : while (val) {
269 24 : if (strcmp(val->key, aKey) == 0) {
270 8 : aResult.Assign(val->value);
271 8 : return NS_OK;
272 : }
273 :
274 16 : val = val->next.get();
275 : }
276 :
277 3 : return NS_ERROR_FAILURE;
278 : }
279 :
280 : nsresult
281 0 : nsINIParser::GetString(const char* aSection, const char* aKey,
282 : char* aResult, uint32_t aResultLen)
283 : {
284 : INIValue* val;
285 0 : mSections.Get(aSection, &val);
286 :
287 0 : while (val) {
288 0 : if (strcmp(val->key, aKey) == 0) {
289 0 : strncpy(aResult, val->value, aResultLen);
290 0 : aResult[aResultLen - 1] = '\0';
291 0 : if (strlen(val->value) >= aResultLen) {
292 0 : return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
293 : }
294 :
295 0 : return NS_OK;
296 : }
297 :
298 0 : val = val->next.get();
299 : }
300 :
301 0 : return NS_ERROR_FAILURE;
302 : }
303 :
304 : nsresult
305 0 : nsINIParser::GetSections(INISectionCallback aCB, void* aClosure)
306 : {
307 0 : for (auto iter = mSections.Iter(); !iter.Done(); iter.Next()) {
308 0 : if (!aCB(iter.Key(), aClosure)) {
309 0 : break;
310 : }
311 : }
312 0 : return NS_OK;
313 : }
314 :
315 : nsresult
316 0 : nsINIParser::GetStrings(const char* aSection,
317 : INIStringCallback aCB, void* aClosure)
318 : {
319 : INIValue* val;
320 :
321 0 : for (mSections.Get(aSection, &val);
322 0 : val;
323 0 : val = val->next.get()) {
324 :
325 0 : if (!aCB(val->key, val->value, aClosure)) {
326 0 : return NS_OK;
327 : }
328 : }
329 :
330 0 : return NS_OK;
331 : }
|