Merge tracemonkey and mozilla-central. (a=blockers)
[mozilla-central.git] / xpcom / glue / nsINIParser.cpp
blobb0f7be7e4df7b0538bee84f31de654a56d104d2b
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
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/
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.
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
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.
23 * Contributor(s):
24 * Samir Gehani <sgehani@netscape.com>
25 * Benjamin Smedberg <bsmedberg@covad.net>
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.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsINIParser.h"
42 #include "nsError.h"
43 #include "nsILocalFile.h"
44 #include "nsCRTGlue.h"
46 #include <stdlib.h>
47 #include <stdio.h>
48 #ifdef XP_WIN
49 #include <windows.h>
50 #endif
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
60 #ifdef XP_WIN
61 inline FILE *TS_tfopen (const char *path, const wchar_t *mode)
63 wchar_t wPath[MAX_PATH];
64 MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH);
65 return _wfopen(wPath, mode);
67 #else
68 inline FILE *TS_tfopen (const char *path, const char *mode)
70 return fopen(path, mode);
72 #endif
74 // Stack based FILE wrapper to ensure that fclose is called, copied from
75 // toolkit/mozapps/update/updater/readstrings.cpp
77 class AutoFILE {
78 public:
79 AutoFILE(FILE *fp = nsnull) : fp_(fp) {}
80 ~AutoFILE() { if (fp_) fclose(fp_); }
81 operator FILE *() { return fp_; }
82 FILE** operator &() { return &fp_; }
83 void operator=(FILE *fp) { fp_ = fp; }
84 private:
85 FILE *fp_;
88 nsresult
89 nsINIParser::Init(nsILocalFile* aFile)
91 nsresult rv;
93 /* open the file. Don't use OpenANSIFileDesc, because you mustn't
94 pass FILE* across shared library boundaries, which may be using
95 different CRTs */
97 AutoFILE fd;
99 #ifdef XP_WIN
100 nsAutoString path;
101 rv = aFile->GetPath(path);
102 NS_ENSURE_SUCCESS(rv, rv);
104 fd = _wfopen(path.get(), READ_BINARYMODE);
105 #else
106 nsCAutoString path;
107 rv = aFile->GetNativePath(path);
109 fd = fopen(path.get(), READ_BINARYMODE);
110 #endif
112 if (!fd)
113 return NS_ERROR_FAILURE;
115 return InitFromFILE(fd);
118 nsresult
119 nsINIParser::Init(const char *aPath)
121 /* open the file */
122 AutoFILE fd = TS_tfopen(aPath, READ_BINARYMODE);
124 if (!fd)
125 return NS_ERROR_FAILURE;
127 return InitFromFILE(fd);
130 static const char kNL[] = "\r\n";
131 static const char kEquals[] = "=";
132 static const char kWhitespace[] = " \t";
133 static const char kRBracket[] = "]";
135 nsresult
136 nsINIParser::InitFromFILE(FILE *fd)
138 if (!mSections.Init())
139 return NS_ERROR_OUT_OF_MEMORY;
141 /* get file size */
142 if (fseek(fd, 0, SEEK_END) != 0)
143 return NS_ERROR_FAILURE;
145 long flen = ftell(fd);
146 if (flen == 0)
147 return NS_ERROR_FAILURE;
149 /* malloc an internal buf the size of the file */
150 mFileContents = new char[flen + 1];
151 if (!mFileContents)
152 return NS_ERROR_OUT_OF_MEMORY;
154 /* read the file in one swoop */
155 if (fseek(fd, 0, SEEK_SET) != 0)
156 return NS_BASE_STREAM_OSERROR;
158 int rd = fread(mFileContents, sizeof(char), flen, fd);
159 if (rd != flen)
160 return NS_BASE_STREAM_OSERROR;
162 mFileContents[flen] = '\0';
164 char *buffer = mFileContents;
165 char *currSection = nsnull;
167 // outer loop tokenizes into lines
168 while (char *token = NS_strtok(kNL, &buffer)) {
169 if (token[0] == '#' || token[0] == ';') // it's a comment
170 continue;
172 token = (char*) NS_strspnp(kWhitespace, token);
173 if (!*token) // empty line
174 continue;
176 if (token[0] == '[') { // section header!
177 ++token;
178 currSection = token;
180 char *rb = NS_strtok(kRBracket, &token);
181 if (!rb || NS_strtok(kWhitespace, &token)) {
182 // there's either an unclosed [Section or a [Section]Moretext!
183 // we could frankly decide that this INI file is malformed right
184 // here and stop, but we won't... keep going, looking for
185 // a well-formed [section] to continue working with
186 currSection = nsnull;
189 continue;
192 if (!currSection) {
193 // If we haven't found a section header (or we found a malformed
194 // section header), don't bother parsing this line.
195 continue;
198 char *key = token;
199 char *e = NS_strtok(kEquals, &token);
200 if (!e || !token)
201 continue;
203 INIValue *v;
204 if (!mSections.Get(currSection, &v)) {
205 v = new INIValue(key, token);
206 if (!v)
207 return NS_ERROR_OUT_OF_MEMORY;
209 mSections.Put(currSection, v);
210 continue;
213 // Check whether this key has already been specified; overwrite
214 // if so, or append if not.
215 while (v) {
216 if (!strcmp(key, v->key)) {
217 v->value = token;
218 break;
220 if (!v->next) {
221 v->next = new INIValue(key, token);
222 if (!v->next)
223 return NS_ERROR_OUT_OF_MEMORY;
224 break;
226 v = v->next;
228 NS_ASSERTION(v, "v should never be null coming out of this loop");
231 return NS_OK;
234 nsresult
235 nsINIParser::GetString(const char *aSection, const char *aKey,
236 nsACString &aResult)
238 INIValue *val;
239 mSections.Get(aSection, &val);
241 while (val) {
242 if (strcmp(val->key, aKey) == 0) {
243 aResult.Assign(val->value);
244 return NS_OK;
247 val = val->next;
250 return NS_ERROR_FAILURE;
253 nsresult
254 nsINIParser::GetString(const char *aSection, const char *aKey,
255 char *aResult, PRUint32 aResultLen)
257 INIValue *val;
258 mSections.Get(aSection, &val);
260 while (val) {
261 if (strcmp(val->key, aKey) == 0) {
262 strncpy(aResult, val->value, aResultLen);
263 aResult[aResultLen - 1] = '\0';
264 if (strlen(val->value) >= aResultLen)
265 return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
267 return NS_OK;
270 val = val->next;
273 return NS_ERROR_FAILURE;
276 PLDHashOperator
277 nsINIParser::GetSectionsCB(const char *aKey, INIValue *aData,
278 void *aClosure)
280 GSClosureStruct *cs = reinterpret_cast<GSClosureStruct*>(aClosure);
282 return cs->usercb(aKey, cs->userclosure) ? PL_DHASH_NEXT : PL_DHASH_STOP;
285 nsresult
286 nsINIParser::GetSections(INISectionCallback aCB, void *aClosure)
288 GSClosureStruct gs = {
289 aCB,
290 aClosure
293 mSections.EnumerateRead(GetSectionsCB, &gs);
294 return NS_OK;
297 nsresult
298 nsINIParser::GetStrings(const char *aSection,
299 INIStringCallback aCB, void *aClosure)
301 INIValue *val;
303 for (mSections.Get(aSection, &val);
304 val;
305 val = val->next) {
307 if (!aCB(val->key, val->value, aClosure))
308 return NS_OK;
311 return NS_OK;