|
1 // Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // f32\sfsrv\cl_cdir.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include "cl_std.h" |
|
19 #include <collate.h> |
|
20 |
|
21 const TUint KCDirArrayGranularity=0x200; |
|
22 const TInt KPartKeyLength = 8; |
|
23 const TInt KCollationLevel0 = 0; |
|
24 const TInt KCollationLevelMax = 3; |
|
25 |
|
26 #define KCollationKeyAllocFail ((HBufC8*)-1) |
|
27 |
|
28 /////////////////////////////////////////////////////////////////////////////// |
|
29 /** |
|
30 * @class TEntry2 |
|
31 * @description TEntry's variant with pointer to collation key buffers |
|
32 * @internalComponent |
|
33 */ |
|
34 NONSHARABLE_CLASS(TEntry2) |
|
35 { |
|
36 public: |
|
37 TEntry2(const TEntry& aEntry); |
|
38 ~TEntry2(); |
|
39 public: |
|
40 TBool IsDir() const {return iEntry.IsDir();} |
|
41 #ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API |
|
42 TInt64 Size() {return MAKE_TINT64(0,iEntry.iSize);} |
|
43 #else |
|
44 TInt64 Size() {return iEntry.FileSize();} |
|
45 #endif |
|
46 TTime Modified() const {return iEntry.iModified;} |
|
47 const TUidType& Type() const {return iEntry.iType;} |
|
48 const TDesC& Name() const {return iEntry.iName;} |
|
49 private: |
|
50 TEntry2(const TEntry2& aEntry); |
|
51 TEntry2& operator=(const TEntry2& aEntry); |
|
52 public: |
|
53 HBufC8* iPartKey; |
|
54 HBufC8* iFullKey; |
|
55 TEntry iEntry; |
|
56 }; |
|
57 |
|
58 TEntry2::TEntry2(const TEntry& aEntry) : iPartKey(0), iFullKey(0), iEntry(aEntry) |
|
59 { |
|
60 } |
|
61 |
|
62 TEntry2::~TEntry2() |
|
63 { |
|
64 if (iPartKey != KCollationKeyAllocFail) |
|
65 delete iPartKey; |
|
66 if (iFullKey != KCollationKeyAllocFail) |
|
67 delete iFullKey; |
|
68 } |
|
69 |
|
70 inline TInt Entry2Size(const TEntry2& aEntry) |
|
71 { |
|
72 return sizeof(HBufC8*) * 2 + EntrySize(aEntry.iEntry, ETrue); |
|
73 } |
|
74 /////////////////////////////////////////////////////////////////////////////// |
|
75 |
|
76 NONSHARABLE_CLASS(TKeyDir) : public TKeyArrayVar |
|
77 { |
|
78 public: |
|
79 TKeyDir(TUint aKey); |
|
80 virtual TInt Compare(TInt aLeft,TInt aRight) const; |
|
81 private: |
|
82 TInt CompareByName(TEntry2& aLeft, TEntry2& aRight) const; |
|
83 private: |
|
84 TCollationMethod iCollationMethod; |
|
85 }; |
|
86 |
|
87 TKeyDir::TKeyDir(TUint aKey) |
|
88 // |
|
89 // Constructor |
|
90 // |
|
91 : TKeyArrayVar(0,(TKeyCmpText)(aKey&0xff),aKey&(EDirsFirst|EDirsLast|EDescending|EDirDescending)) |
|
92 { |
|
93 // |
|
94 // Create our own collation method to also consider punctuation when |
|
95 // sorting filenames. |
|
96 // |
|
97 iCollationMethod = *Mem::GetDefaultMatchingTable(); |
|
98 iCollationMethod.iFlags |= TCollationMethod::EIgnoreNone | TCollationMethod::EFoldCase; |
|
99 } |
|
100 |
|
101 |
|
102 TInt TKeyDir::Compare(TInt aLeft,TInt aRight) const |
|
103 // |
|
104 // Compare two directories for sorting. |
|
105 // |
|
106 { |
|
107 |
|
108 if (aLeft==aRight) |
|
109 return(0); |
|
110 TEntry2& left = *(TEntry2*)At(aLeft); |
|
111 TEntry2& right = *(TEntry2*)At(aRight); |
|
112 TInt ret=0; |
|
113 if ((iKeyLength&EDirsFirst)==EDirsFirst) |
|
114 { |
|
115 if (left.IsDir()) |
|
116 { |
|
117 if (!right.IsDir()) |
|
118 ret=(-1); // left is a dir, right is not |
|
119 } |
|
120 else if (right.IsDir()) |
|
121 ret=1; // right is a dir, left is not |
|
122 } |
|
123 else if ((iKeyLength&EDirsLast)==EDirsLast) |
|
124 { |
|
125 if (left.IsDir()) |
|
126 { |
|
127 if (!right.IsDir()) |
|
128 ret=1; // left is a dir, right is not |
|
129 } |
|
130 else if (right.IsDir()) |
|
131 ret=(-1); // right is a dir, left is not |
|
132 } |
|
133 |
|
134 TInt cmpType=iCmpType; |
|
135 TInt keyLength=iKeyLength; |
|
136 TBool orderDirectories=(keyLength&EDirsFirst) || (keyLength&EDirsLast); |
|
137 if (orderDirectories && left.IsDir() && right.IsDir()) |
|
138 { |
|
139 cmpType=ESortByName; |
|
140 if ((keyLength&EDirDescending)!=EDirDescending) |
|
141 keyLength&=~EDescending; |
|
142 else |
|
143 keyLength|=EDescending; |
|
144 } |
|
145 |
|
146 if (ret==0) // Both are the same type |
|
147 { |
|
148 ret=(-1); // left before right by default |
|
149 switch (cmpType) |
|
150 { |
|
151 case ESortNone: |
|
152 ret=1; |
|
153 break; |
|
154 case ESortByDate: |
|
155 if (left.Modified()>right.Modified()) |
|
156 ret=1; |
|
157 else if (left.Modified()==right.Modified()) |
|
158 ret=0; |
|
159 break; |
|
160 case ESortBySize: |
|
161 #ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API |
|
162 if (I64LOW(left.Size()) > I64LOW(right.Size())) |
|
163 ret=1; |
|
164 else if (I64LOW(left.Size())==I64LOW(right.Size())) |
|
165 ret=0; |
|
166 #else |
|
167 if (left.Size() > right.Size()) |
|
168 ret=1; |
|
169 else if (left.Size()==right.Size()) |
|
170 ret=0; |
|
171 #endif |
|
172 break; |
|
173 case ESortByExt: |
|
174 { |
|
175 TInt i1 = KErrNotFound, i2 = KErrNotFound; |
|
176 if (left.Name() != _L(".") && left.Name() != _L("..")) |
|
177 i1 = left.Name().LocateReverse('.'); |
|
178 if (right.Name() != _L(".") && right.Name() != _L("..")) |
|
179 i2 = right.Name().LocateReverse('.'); |
|
180 if (i1==KErrNotFound && i2!=KErrNotFound) |
|
181 ret=(-1); |
|
182 else if (i2==KErrNotFound && i1!=KErrNotFound) |
|
183 ret=1; |
|
184 else if ((i1==KErrNotFound && i2==KErrNotFound) || (ret=left.Name().Mid(i1).CompareC(right.Name().Mid(i2)))==0) |
|
185 goto byName; |
|
186 } |
|
187 break; |
|
188 case ESortByUid: |
|
189 if (left.Type()[1]==right.Type()[1]) |
|
190 { |
|
191 if (left.Type()[2]==right.Type()[2]) |
|
192 ret = CompareByName(left, right); |
|
193 else if (left.Type()[2].iUid>right.Type()[2].iUid) |
|
194 ret=1; |
|
195 } |
|
196 else if (left.Type()[1].iUid==0) |
|
197 ret=1; |
|
198 else if (right.Type()[1].iUid==0) |
|
199 ret=-1; |
|
200 else if (left.Type()[1].iUid>right.Type()[1].iUid) |
|
201 ret=1; |
|
202 break; |
|
203 case ESortByName: |
|
204 byName: |
|
205 // Force the maximum collation level here (i.e. 3) for sorting strings |
|
206 ret = CompareByName(left, right); |
|
207 break; |
|
208 default: // Default is bad news |
|
209 Panic(ECDirBadSortType); |
|
210 } |
|
211 } |
|
212 if ((keyLength&EDescending)==EDescending) |
|
213 ret=(-ret); // Descending sort order |
|
214 return(ret); |
|
215 } |
|
216 |
|
217 TInt TKeyDir::CompareByName(TEntry2& aLeft, TEntry2& aRight) const |
|
218 // |
|
219 // Compare using collation key of entire name |
|
220 // |
|
221 { |
|
222 TInt ret = -1; |
|
223 TInt r = KErrNone; |
|
224 |
|
225 // Allocate partial key first and handle potential error case |
|
226 // by calling old CompareC |
|
227 // Note: only compare on the first collation level (KCollationLevel0) for partial keys, |
|
228 // to avoid potential inconsistency between full key and partial key comparison. |
|
229 if (!aLeft.iPartKey) |
|
230 { |
|
231 TRAP(r, aLeft.iPartKey = aLeft.Name().Left(KPartKeyLength).GetCollationKeysL(KCollationLevel0, &iCollationMethod)); |
|
232 if (r != KErrNone) |
|
233 aLeft.iPartKey = KCollationKeyAllocFail; |
|
234 } |
|
235 if (!aRight.iPartKey) |
|
236 { |
|
237 TRAP(r, aRight.iPartKey = aRight.Name().Left(KPartKeyLength).GetCollationKeysL(KCollationLevel0, &iCollationMethod)); |
|
238 if (r != KErrNone) |
|
239 aRight.iPartKey = KCollationKeyAllocFail; |
|
240 } |
|
241 if (aLeft.iPartKey == KCollationKeyAllocFail || aRight.iPartKey == KCollationKeyAllocFail) |
|
242 return aLeft.Name().CompareC(aRight.Name()); |
|
243 |
|
244 // Compare by partial key first |
|
245 ret = aLeft.iPartKey->Compare(*aRight.iPartKey); |
|
246 if (ret != 0) |
|
247 return ret; |
|
248 |
|
249 // Compare by full key if partial keys are identical |
|
250 if (!aLeft.iFullKey) |
|
251 { |
|
252 TRAP(r, aLeft.iFullKey = aLeft.Name().GetCollationKeysL(KCollationLevelMax, &iCollationMethod)); |
|
253 if (r != KErrNone) |
|
254 aLeft.iFullKey = KCollationKeyAllocFail; |
|
255 } |
|
256 if (!aRight.iFullKey) |
|
257 { |
|
258 TRAP(r, aRight.iFullKey = aRight.Name().GetCollationKeysL(KCollationLevelMax, &iCollationMethod)); |
|
259 if (r != KErrNone) |
|
260 aRight.iFullKey = KCollationKeyAllocFail; |
|
261 } |
|
262 if (aLeft.iFullKey == KCollationKeyAllocFail || aRight.iFullKey == KCollationKeyAllocFail) |
|
263 // Using old CompareC if partial key allocation failed |
|
264 return aLeft.Name().CompareC(aRight.Name()); |
|
265 |
|
266 // Compare using collation key of full names |
|
267 ret = aLeft.iFullKey->Compare(*aRight.iFullKey); |
|
268 |
|
269 return ret; |
|
270 } |
|
271 |
|
272 |
|
273 |
|
274 EXPORT_C CDir::CDir() |
|
275 /** |
|
276 Default constructor. |
|
277 */ |
|
278 { |
|
279 } |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 EXPORT_C CDir::~CDir() |
|
285 /** |
|
286 Destructor. |
|
287 |
|
288 Frees all resources owned by the object, prior to its destruction. |
|
289 */ |
|
290 { |
|
291 |
|
292 delete iArray; |
|
293 } |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 EXPORT_C CDir* CDir::NewL() |
|
299 /** |
|
300 Allocates and constructs a directory object. |
|
301 |
|
302 This function is protected, which prevents objects of this class from being |
|
303 directly constructed. |
|
304 |
|
305 @return A pointer to the newly created object. |
|
306 */ |
|
307 { |
|
308 |
|
309 CDir* pD=new(ELeave) CDir; |
|
310 pD->iArray=new CArrayPakFlat<TEntry>(KCDirArrayGranularity); |
|
311 if (pD->iArray==NULL) |
|
312 { |
|
313 delete pD; |
|
314 User::LeaveNoMemory(); |
|
315 } |
|
316 return(pD); |
|
317 } |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 EXPORT_C TInt CDir::Count() const |
|
323 /** |
|
324 Gets the number of entries in the array of directory |
|
325 entries. |
|
326 |
|
327 @return The number of entries in the array. |
|
328 */ |
|
329 { |
|
330 |
|
331 return(iArray->Count()); |
|
332 } |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 EXPORT_C const TEntry& CDir::operator[](TInt anIndex) const |
|
338 /** |
|
339 Gets an entry from the array of directory |
|
340 entries. |
|
341 |
|
342 @param anIndex of the desired entry within the array. |
|
343 |
|
344 @return A directory entry. |
|
345 */ |
|
346 { |
|
347 |
|
348 return((*iArray)[anIndex]); |
|
349 } |
|
350 |
|
351 |
|
352 /** |
|
353 * Utility class to manage dynamic array memory |
|
354 * @internalComponent |
|
355 */ |
|
356 NONSHARABLE_CLASS(CAutoArray) : public CBase |
|
357 { |
|
358 public: |
|
359 ~CAutoArray(); |
|
360 public: |
|
361 CArrayVarFlat<TEntry2>* iArray; |
|
362 }; |
|
363 |
|
364 // Have to do this trick because CArrayVarFlat won't destroy element one by one |
|
365 CAutoArray::~CAutoArray() |
|
366 { |
|
367 if (iArray) |
|
368 for (TInt i=0; i<iArray->Count(); ++i) |
|
369 { |
|
370 TEntry2& e = (*iArray)[i]; |
|
371 if (e.iPartKey != KCollationKeyAllocFail) |
|
372 delete e.iPartKey; |
|
373 if (e.iFullKey != KCollationKeyAllocFail) |
|
374 delete e.iFullKey; |
|
375 e.iPartKey = e.iFullKey = 0; |
|
376 } |
|
377 delete iArray; |
|
378 } |
|
379 |
|
380 EXPORT_C TInt CDir::Sort(TUint aKey) |
|
381 /** |
|
382 Sorts the array of directory entries. |
|
383 |
|
384 @param aKey A set of flags describing how the directory entries are to be sorted. |
|
385 The set of flags is defined by TEntryKey. |
|
386 |
|
387 @return KErrNone, if successful, otherwise one of the other system-wide error |
|
388 codes. |
|
389 |
|
390 @see TEntryKey |
|
391 */ |
|
392 { |
|
393 CAutoArray autoArray; |
|
394 #define array autoArray.iArray |
|
395 |
|
396 // Create TEntry2 array from iArray. |
|
397 array = new CArrayVarFlat<TEntry2>(KCDirArrayGranularity); |
|
398 if (!array) |
|
399 return KErrNoMemory; |
|
400 |
|
401 TInt arrayCount = iArray->Count(); |
|
402 if (arrayCount == 0) |
|
403 return KErrNone; |
|
404 |
|
405 TEntry2* entry2 = new TEntry2((*iArray)[0]); |
|
406 |
|
407 if (!entry2) |
|
408 return KErrNoMemory; |
|
409 |
|
410 TInt i, r; |
|
411 for (i=0; i<arrayCount; ++i) |
|
412 { |
|
413 entry2->iEntry = (*iArray)[i]; |
|
414 // Pack here |
|
415 TUint32* pSizeHighSrc = PtrAdd((TUint32*)&(entry2->iEntry), sizeof(TEntry) - 2 * sizeof(TInt)); |
|
416 TUint32* pSizeHighDst = PtrAdd((TUint32*)&(entry2->iEntry), EntrySize(entry2->iEntry, EFalse)); |
|
417 |
|
418 *pSizeHighDst++ = *pSizeHighSrc++; // Pack iSizeHigh |
|
419 *pSizeHighDst = *pSizeHighSrc; // Pack iReserved |
|
420 entry2->iEntry.iAtt |= KEntryAttPacked; |
|
421 |
|
422 TRAP(r, array->AppendL(*entry2, Entry2Size(*entry2))); |
|
423 |
|
424 if (r != KErrNone) |
|
425 { |
|
426 delete entry2; |
|
427 return r; |
|
428 } |
|
429 } |
|
430 |
|
431 // Sort new array |
|
432 TKeyDir key(aKey); |
|
433 r = array->Sort(key); |
|
434 if (r != KErrNone) |
|
435 { |
|
436 delete entry2; |
|
437 return r; |
|
438 } |
|
439 |
|
440 // Copy sorted result back to iArray |
|
441 iArray->Reset(); |
|
442 |
|
443 for (i=0; i<array->Count(); ++i) |
|
444 { |
|
445 entry2->iEntry = (*array)[i].iEntry; |
|
446 // Pack here |
|
447 TUint32* pSizeHighSrc = PtrAdd((TUint32*)&(entry2->iEntry), sizeof(TEntry) - 2 * sizeof(TInt)); |
|
448 TUint32* pSizeHighDst = PtrAdd((TUint32*)&(entry2->iEntry), EntrySize(entry2->iEntry, EFalse)); |
|
449 |
|
450 *pSizeHighDst++ = *pSizeHighSrc++; // Pack iSizeHigh |
|
451 *pSizeHighDst = *pSizeHighSrc; // Pack iReserved |
|
452 entry2->iEntry.iAtt |= KEntryAttPacked; |
|
453 |
|
454 TRAP(r, iArray->AppendL(entry2->iEntry, EntrySize(entry2->iEntry, ETrue))); |
|
455 if (r != KErrNone) |
|
456 { |
|
457 delete entry2; |
|
458 return r; |
|
459 } |
|
460 } |
|
461 |
|
462 delete entry2; |
|
463 return r; |
|
464 } |
|
465 |
|
466 |
|
467 EXPORT_C void CDir::AddL(const TEntry& aEntry) |
|
468 /** |
|
469 Adds the specified entry to the directory. |
|
470 |
|
471 Note that the function can leave. |
|
472 |
|
473 @param aEntry The directory entry to be added. |
|
474 */ |
|
475 { |
|
476 if(aEntry.iAtt & KEntryAttPacked) |
|
477 { |
|
478 iArray->AppendL(aEntry,EntrySize(aEntry, ETrue)); |
|
479 } |
|
480 else |
|
481 { |
|
482 TEntry entry = aEntry; |
|
483 // Pack here |
|
484 TUint32* pSizeHighSrc = PtrAdd((TUint32*)&entry, sizeof(TEntry) - 2 * sizeof(TInt)); |
|
485 TUint32* pSizeHighDst = PtrAdd((TUint32*)&entry, EntrySize(entry, EFalse)); |
|
486 |
|
487 *pSizeHighDst++ = *pSizeHighSrc++; // Pack iSizeHigh |
|
488 *pSizeHighDst = *pSizeHighSrc; // Pack iReserved |
|
489 entry.iAtt |= KEntryAttPacked; |
|
490 iArray->AppendL(entry,EntrySize(entry, ETrue)); |
|
491 } |
|
492 } |
|
493 |
|
494 |
|
495 |
|
496 |
|
497 EXPORT_C void CDir::ExtractL(TBool aRemove,CDir* & aDir) |
|
498 /** |
|
499 Copies all directory entries from this directory array, and adds them to |
|
500 a new directory array. |
|
501 |
|
502 The directory entries in this array can be deleted. |
|
503 |
|
504 Note that the function can leave. |
|
505 |
|
506 @param aRemove If ETrue, the directory entries in this array are |
|
507 to be deleted after extraction; |
|
508 if EFalse, the directory entries are not to be deleted. |
|
509 |
|
510 @param aDir On return, a pointer to a CDir object containing |
|
511 the extracted directory entries. |
|
512 */ |
|
513 { |
|
514 |
|
515 aDir=NULL; |
|
516 aDir=CDir::NewL(); |
|
517 CArrayPakFlat<TEntry>& anArray=(*iArray); |
|
518 TInt count=anArray.Count(); |
|
519 |
|
520 if (count == 0) |
|
521 return; |
|
522 |
|
523 TInt i=0; |
|
524 while (i<count) |
|
525 { |
|
526 TEntry& e=anArray[i]; |
|
527 if (e.IsDir()) |
|
528 aDir->AddL(e); |
|
529 i++; |
|
530 } |
|
531 if (aRemove) |
|
532 { |
|
533 i=0; |
|
534 while (i<count) |
|
535 { |
|
536 if (anArray[i].IsDir()) |
|
537 { |
|
538 anArray.Delete(i); |
|
539 count--; |
|
540 continue; |
|
541 } |
|
542 i++; |
|
543 } |
|
544 anArray.Compress(); |
|
545 } |
|
546 aDir->Compress(); |
|
547 } |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 EXPORT_C void CDir::Compress() |
|
553 /** |
|
554 Compresses the directory. |
|
555 |
|
556 This has the effect of potentially reducing the ammount of storage |
|
557 space required on the media for that directory and the files it contains. |
|
558 Some files are already compressed and will not compress further. |
|
559 |
|
560 A potential side effect of compression is that each file is required to |
|
561 be uncompressed prior to use, generally increasing the time and |
|
562 processing cycles required to access that file. |
|
563 |
|
564 */ |
|
565 { |
|
566 |
|
567 iArray->Compress(); |
|
568 } |
|
569 |