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
15 * The Original Code is Mozilla Communicator client code, released
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.
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"
43 #include "nsILocalFile.h"
44 #include "nsCRTGlue.h"
53 #define READ_BINARYMODE L"rb"
55 #define READ_BINARYMODE "rb"
57 #define READ_BINARYMODE "r"
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
);
68 inline FILE *TS_tfopen (const char *path
, const char *mode
)
70 return fopen(path
, mode
);
74 // Stack based FILE wrapper to ensure that fclose is called, copied from
75 // toolkit/mozapps/update/updater/readstrings.cpp
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
; }
89 nsINIParser::Init(nsILocalFile
* aFile
)
93 /* open the file. Don't use OpenANSIFileDesc, because you mustn't
94 pass FILE* across shared library boundaries, which may be using
101 rv
= aFile
->GetPath(path
);
102 NS_ENSURE_SUCCESS(rv
, rv
);
104 fd
= _wfopen(path
.get(), READ_BINARYMODE
);
107 rv
= aFile
->GetNativePath(path
);
109 fd
= fopen(path
.get(), READ_BINARYMODE
);
113 return NS_ERROR_FAILURE
;
115 return InitFromFILE(fd
);
119 nsINIParser::Init(const char *aPath
)
122 AutoFILE fd
= TS_tfopen(aPath
, READ_BINARYMODE
);
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
[] = "]";
136 nsINIParser::InitFromFILE(FILE *fd
)
138 if (!mSections
.Init())
139 return NS_ERROR_OUT_OF_MEMORY
;
142 if (fseek(fd
, 0, SEEK_END
) != 0)
143 return NS_ERROR_FAILURE
;
145 long flen
= ftell(fd
);
147 return NS_ERROR_FAILURE
;
149 /* malloc an internal buf the size of the file */
150 mFileContents
= new char[flen
+ 1];
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
);
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
172 token
= (char*) NS_strspnp(kWhitespace
, token
);
173 if (!*token
) // empty line
176 if (token
[0] == '[') { // section header!
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
;
193 // If we haven't found a section header (or we found a malformed
194 // section header), don't bother parsing this line.
199 char *e
= NS_strtok(kEquals
, &token
);
204 if (!mSections
.Get(currSection
, &v
)) {
205 v
= new INIValue(key
, token
);
207 return NS_ERROR_OUT_OF_MEMORY
;
209 mSections
.Put(currSection
, v
);
213 // Check whether this key has already been specified; overwrite
214 // if so, or append if not.
216 if (!strcmp(key
, v
->key
)) {
221 v
->next
= new INIValue(key
, token
);
223 return NS_ERROR_OUT_OF_MEMORY
;
228 NS_ASSERTION(v
, "v should never be null coming out of this loop");
235 nsINIParser::GetString(const char *aSection
, const char *aKey
,
239 mSections
.Get(aSection
, &val
);
242 if (strcmp(val
->key
, aKey
) == 0) {
243 aResult
.Assign(val
->value
);
250 return NS_ERROR_FAILURE
;
254 nsINIParser::GetString(const char *aSection
, const char *aKey
,
255 char *aResult
, PRUint32 aResultLen
)
258 mSections
.Get(aSection
, &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
;
273 return NS_ERROR_FAILURE
;
277 nsINIParser::GetSectionsCB(const char *aKey
, INIValue
*aData
,
280 GSClosureStruct
*cs
= reinterpret_cast<GSClosureStruct
*>(aClosure
);
282 return cs
->usercb(aKey
, cs
->userclosure
) ? PL_DHASH_NEXT
: PL_DHASH_STOP
;
286 nsINIParser::GetSections(INISectionCallback aCB
, void *aClosure
)
288 GSClosureStruct gs
= {
293 mSections
.EnumerateRead(GetSectionsCB
, &gs
);
298 nsINIParser::GetStrings(const char *aSection
,
299 INIStringCallback aCB
, void *aClosure
)
303 for (mSections
.Get(aSection
, &val
);
307 if (!aCB(val
->key
, val
->value
, aClosure
))