1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code, released
16 : * March 31, 1998.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Samir Gehani <sgehani@netscape.com>
25 : * Benjamin Smedberg <bsmedberg@covad.net>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsINIParser.h"
42 : #include "nsError.h"
43 : #include "nsILocalFile.h"
44 : #include "nsCRTGlue.h"
45 :
46 : #include <stdlib.h>
47 : #include <stdio.h>
48 : #ifdef XP_WIN
49 : #include <windows.h>
50 : #endif
51 :
52 : #if defined(XP_WIN)
53 : #define READ_BINARYMODE L"rb"
54 : #elif defined(XP_OS2)
55 : #define READ_BINARYMODE "rb"
56 : #else
57 : #define READ_BINARYMODE "r"
58 : #endif
59 :
60 : #ifdef XP_WIN
61 : inline FILE *TS_tfopen (const char *path, const wchar_t *mode)
62 : {
63 : wchar_t wPath[MAX_PATH];
64 : MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH);
65 : return _wfopen(wPath, mode);
66 : }
67 : #else
68 0 : inline FILE *TS_tfopen (const char *path, const char *mode)
69 : {
70 0 : return fopen(path, mode);
71 : }
72 : #endif
73 :
74 : // Stack based FILE wrapper to ensure that fclose is called, copied from
75 : // toolkit/mozapps/update/updater/readstrings.cpp
76 :
77 : class AutoFILE {
78 : public:
79 1214 : AutoFILE(FILE *fp = nsnull) : fp_(fp) {}
80 1214 : ~AutoFILE() { if (fp_) fclose(fp_); }
81 2428 : operator FILE *() { return fp_; }
82 : FILE** operator &() { return &fp_; }
83 1214 : void operator=(FILE *fp) { fp_ = fp; }
84 : private:
85 : FILE *fp_;
86 : };
87 :
88 : nsresult
89 1214 : nsINIParser::Init(nsILocalFile* aFile)
90 : {
91 : /* open the file. Don't use OpenANSIFileDesc, because you mustn't
92 : pass FILE* across shared library boundaries, which may be using
93 : different CRTs */
94 :
95 2428 : AutoFILE fd;
96 :
97 : #ifdef XP_WIN
98 : nsAutoString path;
99 : nsresult rv = aFile->GetPath(path);
100 : NS_ENSURE_SUCCESS(rv, rv);
101 :
102 : fd = _wfopen(path.get(), READ_BINARYMODE);
103 : #else
104 2428 : nsCAutoString path;
105 1214 : aFile->GetNativePath(path);
106 :
107 1214 : fd = fopen(path.get(), READ_BINARYMODE);
108 : #endif
109 :
110 1214 : if (!fd)
111 0 : return NS_ERROR_FAILURE;
112 :
113 1214 : return InitFromFILE(fd);
114 : }
115 :
116 : nsresult
117 0 : nsINIParser::Init(const char *aPath)
118 : {
119 : /* open the file */
120 0 : AutoFILE fd = TS_tfopen(aPath, READ_BINARYMODE);
121 :
122 0 : if (!fd)
123 0 : return NS_ERROR_FAILURE;
124 :
125 0 : return InitFromFILE(fd);
126 : }
127 :
128 : static const char kNL[] = "\r\n";
129 : static const char kEquals[] = "=";
130 : static const char kWhitespace[] = " \t";
131 : static const char kRBracket[] = "]";
132 :
133 : nsresult
134 1214 : nsINIParser::InitFromFILE(FILE *fd)
135 : {
136 1214 : if (!mSections.Init())
137 0 : return NS_ERROR_OUT_OF_MEMORY;
138 :
139 : /* get file size */
140 1214 : if (fseek(fd, 0, SEEK_END) != 0)
141 0 : return NS_ERROR_FAILURE;
142 :
143 1214 : long flen = ftell(fd);
144 1214 : if (flen == 0)
145 0 : return NS_ERROR_FAILURE;
146 :
147 : /* malloc an internal buf the size of the file */
148 2428 : mFileContents = new char[flen + 1];
149 1214 : if (!mFileContents)
150 0 : return NS_ERROR_OUT_OF_MEMORY;
151 :
152 : /* read the file in one swoop */
153 1214 : if (fseek(fd, 0, SEEK_SET) != 0)
154 0 : return NS_BASE_STREAM_OSERROR;
155 :
156 1214 : int rd = fread(mFileContents, sizeof(char), flen, fd);
157 1214 : if (rd != flen)
158 0 : return NS_BASE_STREAM_OSERROR;
159 :
160 1214 : mFileContents[flen] = '\0';
161 :
162 1214 : char *buffer = mFileContents;
163 1214 : char *currSection = nsnull;
164 :
165 : // outer loop tokenizes into lines
166 11732 : while (char *token = NS_strtok(kNL, &buffer)) {
167 5259 : if (token[0] == '#' || token[0] == ';') // it's a comment
168 5 : continue;
169 :
170 5254 : token = (char*) NS_strspnp(kWhitespace, token);
171 5254 : if (!*token) // empty line
172 0 : continue;
173 :
174 5254 : if (token[0] == '[') { // section header!
175 2410 : ++token;
176 2410 : currSection = token;
177 :
178 2410 : char *rb = NS_strtok(kRBracket, &token);
179 2410 : if (!rb || NS_strtok(kWhitespace, &token)) {
180 : // there's either an unclosed [Section or a [Section]Moretext!
181 : // we could frankly decide that this INI file is malformed right
182 : // here and stop, but we won't... keep going, looking for
183 : // a well-formed [section] to continue working with
184 2 : currSection = nsnull;
185 : }
186 :
187 2410 : continue;
188 : }
189 :
190 2844 : if (!currSection) {
191 : // If we haven't found a section header (or we found a malformed
192 : // section header), don't bother parsing this line.
193 0 : continue;
194 : }
195 :
196 2844 : char *key = token;
197 2844 : char *e = NS_strtok(kEquals, &token);
198 2844 : if (!e || !token)
199 1 : continue;
200 :
201 : INIValue *v;
202 2843 : if (!mSections.Get(currSection, &v)) {
203 1533 : v = new INIValue(key, token);
204 1533 : if (!v)
205 0 : return NS_ERROR_OUT_OF_MEMORY;
206 :
207 1533 : mSections.Put(currSection, v);
208 1533 : continue;
209 : }
210 :
211 : // Check whether this key has already been specified; overwrite
212 : // if so, or append if not.
213 4764 : while (v) {
214 3454 : if (!strcmp(key, v->key)) {
215 1 : v->value = token;
216 1 : break;
217 : }
218 3453 : if (!v->next) {
219 1309 : v->next = new INIValue(key, token);
220 1309 : if (!v->next)
221 0 : return NS_ERROR_OUT_OF_MEMORY;
222 1309 : break;
223 : }
224 2144 : v = v->next;
225 : }
226 1310 : NS_ASSERTION(v, "v should never be null coming out of this loop");
227 : }
228 :
229 1214 : return NS_OK;
230 : }
231 :
232 : nsresult
233 2820 : nsINIParser::GetString(const char *aSection, const char *aKey,
234 : nsACString &aResult)
235 : {
236 : INIValue *val;
237 2820 : mSections.Get(aSection, &val);
238 :
239 9070 : while (val) {
240 6249 : if (strcmp(val->key, aKey) == 0) {
241 2819 : aResult.Assign(val->value);
242 2819 : return NS_OK;
243 : }
244 :
245 3430 : val = val->next;
246 : }
247 :
248 1 : return NS_ERROR_FAILURE;
249 : }
250 :
251 : nsresult
252 0 : nsINIParser::GetString(const char *aSection, const char *aKey,
253 : char *aResult, PRUint32 aResultLen)
254 : {
255 : INIValue *val;
256 0 : mSections.Get(aSection, &val);
257 :
258 0 : while (val) {
259 0 : if (strcmp(val->key, aKey) == 0) {
260 0 : strncpy(aResult, val->value, aResultLen);
261 0 : aResult[aResultLen - 1] = '\0';
262 0 : if (strlen(val->value) >= aResultLen)
263 0 : return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
264 :
265 0 : return NS_OK;
266 : }
267 :
268 0 : val = val->next;
269 : }
270 :
271 0 : return NS_ERROR_FAILURE;
272 : }
273 :
274 : PLDHashOperator
275 31 : nsINIParser::GetSectionsCB(const char *aKey, INIValue *aData,
276 : void *aClosure)
277 : {
278 31 : GSClosureStruct *cs = reinterpret_cast<GSClosureStruct*>(aClosure);
279 :
280 31 : return cs->usercb(aKey, cs->userclosure) ? PL_DHASH_NEXT : PL_DHASH_STOP;
281 : }
282 :
283 : nsresult
284 27 : nsINIParser::GetSections(INISectionCallback aCB, void *aClosure)
285 : {
286 : GSClosureStruct gs = {
287 : aCB,
288 : aClosure
289 27 : };
290 :
291 27 : mSections.EnumerateRead(GetSectionsCB, &gs);
292 27 : return NS_OK;
293 : }
294 :
295 : nsresult
296 2389 : nsINIParser::GetStrings(const char *aSection,
297 : INIStringCallback aCB, void *aClosure)
298 : {
299 : INIValue *val;
300 :
301 5207 : for (mSections.Get(aSection, &val);
302 : val;
303 2818 : val = val->next) {
304 :
305 2818 : if (!aCB(val->key, val->value, aClosure))
306 0 : return NS_OK;
307 : }
308 :
309 2389 : return NS_OK;
310 : }
|