1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
---|
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.org code.
|
---|
16 | *
|
---|
17 | * The Initial Developer of the Original Code is
|
---|
18 | * Netscape Communications Corporation.
|
---|
19 | * Portions created by the Initial Developer are Copyright (C) 1998
|
---|
20 | * the Initial Developer. All Rights Reserved.
|
---|
21 | *
|
---|
22 | * Contributor(s):
|
---|
23 | * Pierre Phaneuf <pp@ludusdesign.com>
|
---|
24 | *
|
---|
25 | * Alternatively, the contents of this file may be used under the terms of
|
---|
26 | * either of the GNU General Public License Version 2 or later (the "GPL"),
|
---|
27 | * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
---|
28 | * in which case the provisions of the GPL or the LGPL are applicable instead
|
---|
29 | * of those above. If you wish to allow use of your version of this file only
|
---|
30 | * under the terms of either the GPL or the LGPL, and not to allow others to
|
---|
31 | * use your version of this file under the terms of the MPL, indicate your
|
---|
32 | * decision by deleting the provisions above and replace them with the notice
|
---|
33 | * and other provisions required by the GPL or the LGPL. If you do not delete
|
---|
34 | * the provisions above, a recipient may use your version of this file under
|
---|
35 | * the terms of any one of the MPL, the GPL or the LGPL.
|
---|
36 | *
|
---|
37 | * ***** END LICENSE BLOCK ***** */
|
---|
38 |
|
---|
39 | #include "nsID.h"
|
---|
40 | #include "nsCRT.h"
|
---|
41 | #include "nsReadableUtils.h"
|
---|
42 | #include "nsIInputStream.h"
|
---|
43 | #include "nsIUnicharInputStream.h"
|
---|
44 | #include "pratom.h"
|
---|
45 | #include "nsEnumeratorUtils.h"
|
---|
46 | #include "nsReadableUtils.h"
|
---|
47 | #include "nsPrintfCString.h"
|
---|
48 |
|
---|
49 | #define PL_ARENA_CONST_ALIGN_MASK 3
|
---|
50 | #include "nsPersistentProperties.h"
|
---|
51 | #include "nsIProperties.h"
|
---|
52 | #include "nsProperties.h"
|
---|
53 |
|
---|
54 | struct PropertyTableEntry : public PLDHashEntryHdr
|
---|
55 | {
|
---|
56 | // both of these are arena-allocated
|
---|
57 | const char *mKey;
|
---|
58 | const PRUnichar *mValue;
|
---|
59 | };
|
---|
60 |
|
---|
61 | static PRUnichar*
|
---|
62 | ArenaStrdup(const nsAFlatString& aString, PLArenaPool* aArena)
|
---|
63 | {
|
---|
64 | void *mem;
|
---|
65 | // add one to include the null terminator
|
---|
66 | PRInt32 len = (aString.Length()+1) * sizeof(PRUnichar);
|
---|
67 | PL_ARENA_ALLOCATE(mem, aArena, len);
|
---|
68 | NS_ASSERTION(mem, "Couldn't allocate space!\n");
|
---|
69 | if (mem) {
|
---|
70 | memcpy(mem, aString.get(), len);
|
---|
71 | }
|
---|
72 | return NS_STATIC_CAST(PRUnichar*, mem);
|
---|
73 | }
|
---|
74 |
|
---|
75 | static char*
|
---|
76 | ArenaStrdup(const nsAFlatCString& aString, PLArenaPool* aArena)
|
---|
77 | {
|
---|
78 | void *mem;
|
---|
79 | // add one to include the null terminator
|
---|
80 | PRInt32 len = (aString.Length()+1) * sizeof(char);
|
---|
81 | PL_ARENA_ALLOCATE(mem, aArena, len);
|
---|
82 | NS_ASSERTION(mem, "Couldn't allocate space!\n");
|
---|
83 | if (mem)
|
---|
84 | memcpy(mem, aString.get(), len);
|
---|
85 | return NS_STATIC_CAST(char*, mem);
|
---|
86 | }
|
---|
87 |
|
---|
88 | static const struct PLDHashTableOps property_HashTableOps = {
|
---|
89 | PL_DHashAllocTable,
|
---|
90 | PL_DHashFreeTable,
|
---|
91 | PL_DHashGetKeyStub,
|
---|
92 | PL_DHashStringKey,
|
---|
93 | PL_DHashMatchStringKey,
|
---|
94 | PL_DHashMoveEntryStub,
|
---|
95 | PL_DHashClearEntryStub,
|
---|
96 | PL_DHashFinalizeStub,
|
---|
97 | nsnull,
|
---|
98 | };
|
---|
99 |
|
---|
100 | nsPersistentProperties::nsPersistentProperties()
|
---|
101 | : mIn(nsnull)
|
---|
102 | {
|
---|
103 | mSubclass = NS_STATIC_CAST(nsIPersistentProperties*, this);
|
---|
104 | mTable.ops = nsnull;
|
---|
105 | PL_INIT_ARENA_POOL(&mArena, "PersistentPropertyArena", 2048);
|
---|
106 | }
|
---|
107 |
|
---|
108 | nsPersistentProperties::~nsPersistentProperties()
|
---|
109 | {
|
---|
110 | PL_FinishArenaPool(&mArena);
|
---|
111 | if (mTable.ops)
|
---|
112 | PL_DHashTableFinish(&mTable);
|
---|
113 | }
|
---|
114 |
|
---|
115 | nsresult
|
---|
116 | nsPersistentProperties::Init()
|
---|
117 | {
|
---|
118 | if (!PL_DHashTableInit(&mTable, &property_HashTableOps, nsnull,
|
---|
119 | sizeof(PropertyTableEntry), 20)) {
|
---|
120 | mTable.ops = nsnull;
|
---|
121 | return NS_ERROR_OUT_OF_MEMORY;
|
---|
122 | }
|
---|
123 | return NS_OK;
|
---|
124 | }
|
---|
125 |
|
---|
126 | NS_METHOD
|
---|
127 | nsPersistentProperties::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
|
---|
128 | {
|
---|
129 | if (aOuter)
|
---|
130 | return NS_ERROR_NO_AGGREGATION;
|
---|
131 | nsPersistentProperties* props = new nsPersistentProperties();
|
---|
132 | if (props == nsnull)
|
---|
133 | return NS_ERROR_OUT_OF_MEMORY;
|
---|
134 |
|
---|
135 | NS_ADDREF(props);
|
---|
136 | nsresult rv = props->Init();
|
---|
137 | if (NS_SUCCEEDED(rv))
|
---|
138 | rv = props->QueryInterface(aIID, aResult);
|
---|
139 |
|
---|
140 | NS_RELEASE(props);
|
---|
141 | return rv;
|
---|
142 | }
|
---|
143 |
|
---|
144 | NS_IMPL_THREADSAFE_ISUPPORTS2(nsPersistentProperties, nsIPersistentProperties, nsIProperties)
|
---|
145 |
|
---|
146 | NS_IMETHODIMP
|
---|
147 | nsPersistentProperties::Load(nsIInputStream *aIn)
|
---|
148 | {
|
---|
149 | PRInt32 c;
|
---|
150 | nsresult ret = NS_NewUTF8ConverterStream(&mIn, aIn, 0);
|
---|
151 |
|
---|
152 | if (ret != NS_OK) {
|
---|
153 | NS_WARNING("NS_NewUTF8ConverterStream failed");
|
---|
154 | return NS_ERROR_FAILURE;
|
---|
155 | }
|
---|
156 | c = Read();
|
---|
157 | while (1) {
|
---|
158 | c = SkipWhiteSpace(c);
|
---|
159 | if (c < 0) {
|
---|
160 | break;
|
---|
161 | }
|
---|
162 | else if ((c == '#') || (c == '!')) {
|
---|
163 | c = SkipLine(c);
|
---|
164 | continue;
|
---|
165 | }
|
---|
166 | else {
|
---|
167 | nsAutoString key;
|
---|
168 | while ((c >= 0) && (c != '=') && (c != ':')) {
|
---|
169 | key.Append(PRUnichar(c));
|
---|
170 | c = Read();
|
---|
171 | }
|
---|
172 | if (c < 0) {
|
---|
173 | break;
|
---|
174 | }
|
---|
175 | static const char trimThese[] = " \t";
|
---|
176 | key.Trim(trimThese, PR_FALSE, PR_TRUE);
|
---|
177 | c = Read();
|
---|
178 | nsAutoString value;
|
---|
179 | PRUint32 state = 0;
|
---|
180 | PRUnichar uchar = 0;
|
---|
181 | while ((c >= 0) && (c != '\r') && (c != '\n')) {
|
---|
182 | switch(state) {
|
---|
183 | case 0:
|
---|
184 | if (c == '\\') {
|
---|
185 | c = Read();
|
---|
186 | switch(c) {
|
---|
187 | case '\r':
|
---|
188 | case '\n':
|
---|
189 | // Only skip first EOL characters and then next line's
|
---|
190 | // whitespace characters. Skipping all EOL characters
|
---|
191 | // and all upcoming whitespace is too agressive.
|
---|
192 | if (c == '\r')
|
---|
193 | c = Read();
|
---|
194 | if (c == '\n')
|
---|
195 | c = Read();
|
---|
196 | while (c == ' ' || c == '\t')
|
---|
197 | c = Read();
|
---|
198 | continue;
|
---|
199 | case 'u':
|
---|
200 | case 'U':
|
---|
201 | state = 1;
|
---|
202 | uchar=0;
|
---|
203 | break;
|
---|
204 | case 't':
|
---|
205 | value.Append(PRUnichar('\t'));
|
---|
206 | break;
|
---|
207 | case 'n':
|
---|
208 | value.Append(PRUnichar('\n'));
|
---|
209 | break;
|
---|
210 | case 'r':
|
---|
211 | value.Append(PRUnichar('\r'));
|
---|
212 | break;
|
---|
213 | default:
|
---|
214 | value.Append((PRUnichar) c);
|
---|
215 | } // switch(c)
|
---|
216 | } else {
|
---|
217 | value.Append((PRUnichar) c);
|
---|
218 | }
|
---|
219 | c = Read();
|
---|
220 | break;
|
---|
221 | case 1:
|
---|
222 | case 2:
|
---|
223 | case 3:
|
---|
224 | case 4:
|
---|
225 | if (('0' <= c) && (c <= '9')) {
|
---|
226 | uchar = (uchar << 4) | (c - '0');
|
---|
227 | state++;
|
---|
228 | c = Read();
|
---|
229 | } else if (('a' <= c) && (c <= 'f')) {
|
---|
230 | uchar = (uchar << 4) | (c - 'a' + 0x0a);
|
---|
231 | state++;
|
---|
232 | c = Read();
|
---|
233 | } else if (('A' <= c) && (c <= 'F')) {
|
---|
234 | uchar = (uchar << 4) | (c - 'A' + 0x0a);
|
---|
235 | state++;
|
---|
236 | c = Read();
|
---|
237 | } else {
|
---|
238 | value.Append((PRUnichar) uchar);
|
---|
239 | state = 0;
|
---|
240 | }
|
---|
241 | break;
|
---|
242 | case 5:
|
---|
243 | value.Append((PRUnichar) uchar);
|
---|
244 | state = 0;
|
---|
245 | }
|
---|
246 | }
|
---|
247 | if (state != 0) {
|
---|
248 | value.Append((PRUnichar) uchar);
|
---|
249 | state = 0;
|
---|
250 | }
|
---|
251 |
|
---|
252 | value.Trim(trimThese, PR_TRUE, PR_TRUE);
|
---|
253 | nsAutoString oldValue;
|
---|
254 | mSubclass->SetStringProperty(NS_ConvertUCS2toUTF8(key), value, oldValue);
|
---|
255 | }
|
---|
256 | }
|
---|
257 | mIn->Close();
|
---|
258 | NS_RELEASE(mIn);
|
---|
259 |
|
---|
260 | return NS_OK;
|
---|
261 | }
|
---|
262 |
|
---|
263 | NS_IMETHODIMP
|
---|
264 | nsPersistentProperties::SetStringProperty(const nsACString& aKey,
|
---|
265 | const nsAString& aNewValue,
|
---|
266 | nsAString& aOldValue)
|
---|
267 | {
|
---|
268 | #if 0
|
---|
269 | cout << "will add " << aKey.get() << "=" <<
|
---|
270 | NS_LossyConvertUCS2ToASCII(aNewValue).get() << endl;
|
---|
271 | #endif
|
---|
272 |
|
---|
273 | const nsAFlatCString& flatKey = PromiseFlatCString(aKey);
|
---|
274 | PropertyTableEntry *entry =
|
---|
275 | NS_STATIC_CAST(PropertyTableEntry*,
|
---|
276 | PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_ADD));
|
---|
277 |
|
---|
278 | if (entry->mKey) {
|
---|
279 | aOldValue = entry->mValue;
|
---|
280 | NS_WARNING(nsPrintfCString(aKey.Length() + 30,
|
---|
281 | "the property %s already exists\n",
|
---|
282 | flatKey.get()).get());
|
---|
283 | }
|
---|
284 |
|
---|
285 | entry->mKey = ArenaStrdup(flatKey, &mArena);
|
---|
286 | entry->mValue = ArenaStrdup(PromiseFlatString(aNewValue), &mArena);
|
---|
287 |
|
---|
288 | return NS_OK;
|
---|
289 | }
|
---|
290 |
|
---|
291 | NS_IMETHODIMP
|
---|
292 | nsPersistentProperties::Save(nsIOutputStream* aOut, const nsACString& aHeader)
|
---|
293 | {
|
---|
294 | return NS_ERROR_NOT_IMPLEMENTED;
|
---|
295 | }
|
---|
296 |
|
---|
297 | NS_IMETHODIMP
|
---|
298 | nsPersistentProperties::Subclass(nsIPersistentProperties* aSubclass)
|
---|
299 | {
|
---|
300 | if (aSubclass) {
|
---|
301 | mSubclass = aSubclass;
|
---|
302 | }
|
---|
303 |
|
---|
304 | return NS_OK;
|
---|
305 | }
|
---|
306 |
|
---|
307 | NS_IMETHODIMP
|
---|
308 | nsPersistentProperties::GetStringProperty(const nsACString& aKey,
|
---|
309 | nsAString& aValue)
|
---|
310 | {
|
---|
311 | const nsAFlatCString& flatKey = PromiseFlatCString(aKey);
|
---|
312 |
|
---|
313 | PropertyTableEntry *entry =
|
---|
314 | NS_STATIC_CAST(PropertyTableEntry*,
|
---|
315 | PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_LOOKUP));
|
---|
316 |
|
---|
317 | if (PL_DHASH_ENTRY_IS_FREE(entry))
|
---|
318 | return NS_ERROR_FAILURE;
|
---|
319 |
|
---|
320 | aValue = entry->mValue;
|
---|
321 | return NS_OK;
|
---|
322 | }
|
---|
323 |
|
---|
324 | PR_STATIC_CALLBACK(PLDHashOperator)
|
---|
325 | AddElemToArray(PLDHashTable* table, PLDHashEntryHdr *hdr,
|
---|
326 | PRUint32 i, void *arg)
|
---|
327 | {
|
---|
328 | nsISupportsArray *propArray = (nsISupportsArray *) arg;
|
---|
329 | PropertyTableEntry* entry =
|
---|
330 | NS_STATIC_CAST(PropertyTableEntry*, hdr);
|
---|
331 |
|
---|
332 | nsPropertyElement *element =
|
---|
333 | new nsPropertyElement(nsDependentCString(entry->mKey),
|
---|
334 | nsDependentString(entry->mValue));
|
---|
335 | if (!element)
|
---|
336 | return PL_DHASH_STOP;
|
---|
337 |
|
---|
338 | NS_ADDREF(element);
|
---|
339 | propArray->InsertElementAt(element, i);
|
---|
340 |
|
---|
341 | return PL_DHASH_NEXT;
|
---|
342 | }
|
---|
343 |
|
---|
344 |
|
---|
345 | NS_IMETHODIMP
|
---|
346 | nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult)
|
---|
347 | {
|
---|
348 | nsCOMPtr<nsIBidirectionalEnumerator> iterator;
|
---|
349 |
|
---|
350 | nsISupportsArray* propArray;
|
---|
351 | nsresult rv = NS_NewISupportsArray(&propArray);
|
---|
352 | if (rv != NS_OK)
|
---|
353 | return rv;
|
---|
354 |
|
---|
355 | // Step through hash entries populating a transient array
|
---|
356 | PRUint32 n =
|
---|
357 | PL_DHashTableEnumerate(&mTable, AddElemToArray, (void *)propArray);
|
---|
358 | if (n < mTable.entryCount)
|
---|
359 | return NS_ERROR_OUT_OF_MEMORY;
|
---|
360 |
|
---|
361 | return NS_NewArrayEnumerator(aResult, propArray);
|
---|
362 | }
|
---|
363 |
|
---|
364 |
|
---|
365 | PRInt32
|
---|
366 | nsPersistentProperties::Read()
|
---|
367 | {
|
---|
368 | PRUnichar c;
|
---|
369 | PRUint32 nRead;
|
---|
370 | nsresult ret;
|
---|
371 |
|
---|
372 | ret = mIn->Read(&c, 1, &nRead);
|
---|
373 | if (ret == NS_OK && nRead == 1) {
|
---|
374 | return c;
|
---|
375 | }
|
---|
376 |
|
---|
377 | return -1;
|
---|
378 | }
|
---|
379 |
|
---|
380 | #define IS_WHITE_SPACE(c) \
|
---|
381 | (((c) == ' ') || ((c) == '\t') || ((c) == '\r') || ((c) == '\n'))
|
---|
382 |
|
---|
383 | PRInt32
|
---|
384 | nsPersistentProperties::SkipWhiteSpace(PRInt32 c)
|
---|
385 | {
|
---|
386 | while (IS_WHITE_SPACE(c)) {
|
---|
387 | c = Read();
|
---|
388 | }
|
---|
389 |
|
---|
390 | return c;
|
---|
391 | }
|
---|
392 |
|
---|
393 | PRInt32
|
---|
394 | nsPersistentProperties::SkipLine(PRInt32 c)
|
---|
395 | {
|
---|
396 | while ((c >= 0) && (c != '\r') && (c != '\n')) {
|
---|
397 | c = Read();
|
---|
398 | }
|
---|
399 | if (c == '\r') {
|
---|
400 | c = Read();
|
---|
401 | }
|
---|
402 | if (c == '\n') {
|
---|
403 | c = Read();
|
---|
404 | }
|
---|
405 |
|
---|
406 | return c;
|
---|
407 | }
|
---|
408 |
|
---|
409 | ////////////////////////////////////////////////////////////////////////////////
|
---|
410 | // XXX Some day we'll unify the nsIPersistentProperties interface with
|
---|
411 | // nsIProperties, but until now...
|
---|
412 |
|
---|
413 | NS_IMETHODIMP
|
---|
414 | nsPersistentProperties::Get(const char* prop, const nsIID & uuid, void* *result)
|
---|
415 | {
|
---|
416 | return NS_ERROR_NOT_IMPLEMENTED;
|
---|
417 | }
|
---|
418 |
|
---|
419 | NS_IMETHODIMP
|
---|
420 | nsPersistentProperties::Set(const char* prop, nsISupports* value)
|
---|
421 | {
|
---|
422 | return NS_ERROR_NOT_IMPLEMENTED;
|
---|
423 | }
|
---|
424 | NS_IMETHODIMP
|
---|
425 | nsPersistentProperties::Undefine(const char* prop)
|
---|
426 | {
|
---|
427 | return NS_ERROR_NOT_IMPLEMENTED;
|
---|
428 | }
|
---|
429 |
|
---|
430 | NS_IMETHODIMP
|
---|
431 | nsPersistentProperties::Has(const char* prop, PRBool *result)
|
---|
432 | {
|
---|
433 | return NS_ERROR_NOT_IMPLEMENTED;
|
---|
434 | }
|
---|
435 |
|
---|
436 | NS_IMETHODIMP
|
---|
437 | nsPersistentProperties::GetKeys(PRUint32 *count, char ***keys)
|
---|
438 | {
|
---|
439 | return NS_ERROR_NOT_IMPLEMENTED;
|
---|
440 | }
|
---|
441 |
|
---|
442 | ////////////////////////////////////////////////////////////////////////////////
|
---|
443 | // PropertyElement
|
---|
444 | ////////////////////////////////////////////////////////////////////////////////
|
---|
445 |
|
---|
446 |
|
---|
447 | NS_METHOD
|
---|
448 | nsPropertyElement::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
|
---|
449 | {
|
---|
450 | if (aOuter)
|
---|
451 | return NS_ERROR_NO_AGGREGATION;
|
---|
452 | nsPropertyElement* propElem = new nsPropertyElement();
|
---|
453 | if (propElem == nsnull)
|
---|
454 | return NS_ERROR_OUT_OF_MEMORY;
|
---|
455 | NS_ADDREF(propElem);
|
---|
456 | nsresult rv = propElem->QueryInterface(aIID, aResult);
|
---|
457 | NS_RELEASE(propElem);
|
---|
458 | return rv;
|
---|
459 | }
|
---|
460 |
|
---|
461 | NS_IMPL_ISUPPORTS1(nsPropertyElement, nsIPropertyElement)
|
---|
462 |
|
---|
463 | NS_IMETHODIMP
|
---|
464 | nsPropertyElement::GetKey(nsACString& aReturnKey)
|
---|
465 | {
|
---|
466 | aReturnKey = mKey;
|
---|
467 | return NS_OK;
|
---|
468 | }
|
---|
469 |
|
---|
470 | NS_IMETHODIMP
|
---|
471 | nsPropertyElement::GetValue(nsAString& aReturnValue)
|
---|
472 | {
|
---|
473 | aReturnValue = mValue;
|
---|
474 | return NS_OK;
|
---|
475 | }
|
---|
476 |
|
---|
477 | NS_IMETHODIMP
|
---|
478 | nsPropertyElement::SetKey(const nsACString& aKey)
|
---|
479 | {
|
---|
480 | mKey = aKey;
|
---|
481 | return NS_OK;
|
---|
482 | }
|
---|
483 |
|
---|
484 | NS_IMETHODIMP
|
---|
485 | nsPropertyElement::SetValue(const nsAString& aValue)
|
---|
486 | {
|
---|
487 | mValue = aValue;
|
---|
488 | return NS_OK;
|
---|
489 | }
|
---|
490 |
|
---|
491 | ////////////////////////////////////////////////////////////////////////////////
|
---|
492 |
|
---|