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"
49 #if defined(XP_WIN) || defined(XP_OS2)
50 #define BINARY_MODE "b"
55 // Stack based FILE wrapper to ensure that fclose is called, copied from
56 // toolkit/mozapps/update/src/updater/readstrings.cpp
60 AutoFILE(FILE *fp
= nsnull
) : fp_(fp
) {}
61 ~AutoFILE() { if (fp_
) fclose(fp_
); }
62 operator FILE *() { return fp_
; }
63 FILE** operator &() { return &fp_
; }
64 void operator=(FILE *fp
) { fp_
= fp
; }
70 nsINIParser::Init(nsILocalFile
* aFile
)
74 /* open the file. Don't use OpenANSIFileDesc, because you mustn't
75 pass FILE* across shared library boundaries, which may be using
82 rv
= aFile
->GetPath(path
);
83 NS_ENSURE_SUCCESS(rv
, rv
);
85 fd
= _wfopen(path
.get(), L
"rb");
88 rv
= aFile
->GetNativePath(path
);
89 NS_ENSURE_SUCCESS(rv
, rv
);
91 fd
= fopen(path
.get(), "r" BINARY_MODE
);
94 return NS_ERROR_FAILURE
;
96 return InitFromFILE(fd
);
100 nsINIParser::Init(const char *aPath
)
103 AutoFILE fd
= fopen(aPath
, "r" BINARY_MODE
);
105 return NS_ERROR_FAILURE
;
107 return InitFromFILE(fd
);
110 static const char kNL
[] = "\r\n";
111 static const char kEquals
[] = "=";
112 static const char kWhitespace
[] = " \t";
113 static const char kRBracket
[] = "]";
116 nsINIParser::InitFromFILE(FILE *fd
)
118 if (!mSections
.Init())
119 return NS_ERROR_OUT_OF_MEMORY
;
122 if (fseek(fd
, 0, SEEK_END
) != 0)
123 return NS_ERROR_FAILURE
;
125 long flen
= ftell(fd
);
127 return NS_ERROR_FAILURE
;
129 /* malloc an internal buf the size of the file */
130 mFileContents
= new char[flen
+ 1];
132 return NS_ERROR_OUT_OF_MEMORY
;
134 /* read the file in one swoop */
135 if (fseek(fd
, 0, SEEK_SET
) != 0)
136 return NS_BASE_STREAM_OSERROR
;
138 int rd
= fread(mFileContents
, sizeof(char), flen
, fd
);
140 return NS_BASE_STREAM_OSERROR
;
142 mFileContents
[flen
] = '\0';
144 char *buffer
= mFileContents
;
145 char *currSection
= nsnull
;
146 INIValue
*last
= nsnull
;
148 // outer loop tokenizes into lines
149 while (char *token
= NS_strtok(kNL
, &buffer
)) {
150 if (token
[0] == '#' || token
[0] == ';') // it's a comment
153 token
= (char*) NS_strspnp(kWhitespace
, token
);
154 if (!*token
) // empty line
157 if (token
[0] == '[') { // section header!
162 char *rb
= NS_strtok(kRBracket
, &token
);
163 if (!rb
|| NS_strtok(kWhitespace
, &token
)) {
164 // there's either an unclosed [Section or a [Section]Moretext!
165 // we could frankly decide that this INI file is malformed right
166 // here and stop, but we won't... keep going, looking for
167 // a well-formed [section] to continue working with
168 currSection
= nsnull
;
175 // If we haven't found a section header (or we found a malformed
176 // section header), don't bother parsing this line.
181 char *e
= NS_strtok(kEquals
, &token
);
185 INIValue
*val
= new INIValue(key
, token
);
187 return NS_ERROR_OUT_OF_MEMORY
;
189 // If we haven't already added something to this section, "last" will
192 mSections
.Get(currSection
, &last
);
193 while (last
&& last
->next
)
198 // Add this element on to the tail of the existing list
205 // We've never encountered this section before, add it to the head
206 mSections
.Put(currSection
, val
);
213 nsINIParser::GetString(const char *aSection
, const char *aKey
,
217 mSections
.Get(aSection
, &val
);
220 if (strcmp(val
->key
, aKey
) == 0) {
221 aResult
.Assign(val
->value
);
228 return NS_ERROR_FAILURE
;
232 nsINIParser::GetString(const char *aSection
, const char *aKey
,
233 char *aResult
, PRUint32 aResultLen
)
236 mSections
.Get(aSection
, &val
);
239 if (strcmp(val
->key
, aKey
) == 0) {
240 strncpy(aResult
, val
->value
, aResultLen
);
241 aResult
[aResultLen
- 1] = '\0';
242 if (strlen(val
->value
) >= aResultLen
)
243 return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA
;
251 return NS_ERROR_FAILURE
;
255 nsINIParser::GetSectionsCB(const char *aKey
, INIValue
*aData
,
258 GSClosureStruct
*cs
= reinterpret_cast<GSClosureStruct
*>(aClosure
);
260 return cs
->usercb(aKey
, cs
->userclosure
) ? PL_DHASH_NEXT
: PL_DHASH_STOP
;
264 nsINIParser::GetSections(INISectionCallback aCB
, void *aClosure
)
266 GSClosureStruct gs
= {
271 mSections
.EnumerateRead(GetSectionsCB
, &gs
);
276 nsINIParser::GetStrings(const char *aSection
,
277 INIStringCallback aCB
, void *aClosure
)
281 for (mSections
.Get(aSection
, &val
);
285 if (!aCB(val
->key
, val
->value
, aClosure
))