|
1 // tail.cpp |
|
2 // |
|
3 // Copyright (c) 2005 - 2010 Accenture. All rights reserved. |
|
4 // This component and the accompanying materials are made available |
|
5 // under the terms of the "Eclipse Public License v1.0" |
|
6 // which accompanies this distribution, and is available |
|
7 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 // |
|
9 // Initial Contributors: |
|
10 // Accenture - Initial contribution |
|
11 // |
|
12 |
|
13 #include "character_converter.h" |
|
14 #include "file_reader.h" |
|
15 #include "tail.h" |
|
16 |
|
17 const TInt KBlockSize = 512; |
|
18 const TText KUnicodeParagraphSeparator = 0x2029; |
|
19 const TText KUnicodeLineSeparator = 0x2028; |
|
20 const TText KCarriageReturn = 0x000d; |
|
21 const TText KLineFeed = 0x000a; |
|
22 |
|
23 |
|
24 // |
|
25 // CTrailingLineFinder. |
|
26 // |
|
27 |
|
28 class CTrailingLineFinder : public CBase, public MFileReaderObserver |
|
29 { |
|
30 public: |
|
31 static CTrailingLineFinder* NewL(TInt aBlockSize, CCharacterConverter& aCharacterConverter); |
|
32 ~CTrailingLineFinder(); |
|
33 void Find(CFileReader& aFileReader, const TDesC& aFileName, TInt aNumLines, MTrailingLineFinderObserver& aObserver); |
|
34 void Cancel(); |
|
35 const TDesC& operator[](TInt aIndex) const; |
|
36 private: |
|
37 CTrailingLineFinder(CCharacterConverter& aCharacterConverter); |
|
38 void ConstructL(TInt aBlockSize); |
|
39 void HandleFileDataL(const TDesC8& aData, TReadType aType, TBool& aContinue); |
|
40 void FindNextLine(const TDesC& aData, TInt aOffset, TPtrC& aLine, TBool& aComplete); |
|
41 void AddLineL(const TDesC& aLine, TBool aComplete); |
|
42 private: // From MFileReaderObserver. |
|
43 virtual void HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue); |
|
44 virtual void HandleFileReadError(TInt aError); |
|
45 private: |
|
46 CFileReader* iFileReader; |
|
47 CCharacterConverter& iCharacterConverter; |
|
48 MTrailingLineFinderObserver* iObserver; |
|
49 HBufC8* iBuf; |
|
50 TPtr8 iPtr; |
|
51 RPointerArray<HBufC> iTrailingLines; |
|
52 TInt iCurrentLineIndex; |
|
53 TInt iNumLinesToFind; |
|
54 TInt iNumCompleteLinesFound; |
|
55 TInt iFilePos; |
|
56 TBool iPossiblySplitCrLf; |
|
57 TBool iLastLineNotComplete; |
|
58 }; |
|
59 |
|
60 CTrailingLineFinder* CTrailingLineFinder::NewL(TInt aBlockSize, CCharacterConverter& aCharacterConverter) |
|
61 { |
|
62 CTrailingLineFinder* self = new(ELeave) CTrailingLineFinder(aCharacterConverter); |
|
63 CleanupStack::PushL(self); |
|
64 self->ConstructL(aBlockSize); |
|
65 CleanupStack::Pop(self); |
|
66 return self; |
|
67 } |
|
68 |
|
69 CTrailingLineFinder::~CTrailingLineFinder() |
|
70 { |
|
71 delete iBuf; |
|
72 iTrailingLines.ResetAndDestroy(); |
|
73 } |
|
74 |
|
75 void CTrailingLineFinder::Find(CFileReader& aFileReader, const TDesC& aFileName, TInt aNumLines, MTrailingLineFinderObserver& aObserver) |
|
76 { |
|
77 ASSERT(!iFileReader); |
|
78 iTrailingLines.ResetAndDestroy(); |
|
79 iFileReader = &aFileReader; |
|
80 iObserver = &aObserver; |
|
81 iNumLinesToFind = aNumLines; |
|
82 iNumCompleteLinesFound = 0; |
|
83 iCurrentLineIndex = -1; |
|
84 iPossiblySplitCrLf = EFalse; |
|
85 iFileReader->Read(aFileName, *this); |
|
86 } |
|
87 |
|
88 void CTrailingLineFinder::Cancel() |
|
89 { |
|
90 if (iFileReader) |
|
91 { |
|
92 iFileReader->Cancel(); |
|
93 } |
|
94 } |
|
95 |
|
96 const TDesC& CTrailingLineFinder::operator[](TInt aIndex) const |
|
97 { |
|
98 return *iTrailingLines[aIndex]; |
|
99 } |
|
100 |
|
101 void CTrailingLineFinder::HandleFileDataL(const TDesC8& aData, TReadType aType, TBool& aContinue) |
|
102 { |
|
103 iFilePos += aData.Length(); |
|
104 aContinue = ETrue; |
|
105 const TDesC* convertedData = NULL; |
|
106 |
|
107 switch (aType) |
|
108 { |
|
109 case MFileReaderObserver::EFirst: |
|
110 { |
|
111 convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::EFirst); |
|
112 break; |
|
113 } |
|
114 case MFileReaderObserver::EMiddle: |
|
115 { |
|
116 convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::EMiddle); |
|
117 break; |
|
118 } |
|
119 case MFileReaderObserver::ELast: |
|
120 { |
|
121 convertedData = &iCharacterConverter.ConvertChunkL(aData, CCharacterConverter::ELast); |
|
122 break; |
|
123 } |
|
124 default: |
|
125 { |
|
126 ASSERT(FALSE); |
|
127 } |
|
128 } |
|
129 |
|
130 if (convertedData->Length() > 0) |
|
131 { |
|
132 TInt offset = 0; |
|
133 FOREVER |
|
134 { |
|
135 TPtrC linePtr; |
|
136 TBool completeLine; |
|
137 FindNextLine(*convertedData, offset, linePtr, completeLine); |
|
138 AddLineL(linePtr, completeLine); |
|
139 if (!completeLine) |
|
140 { |
|
141 break; |
|
142 } |
|
143 offset += linePtr.Length(); |
|
144 } |
|
145 } |
|
146 |
|
147 if (aType == MFileReaderObserver::ELast) |
|
148 { |
|
149 iFileReader = NULL; |
|
150 iObserver->HandleTrailingLines(iCurrentLineIndex + 1, iFilePos); |
|
151 } |
|
152 } |
|
153 |
|
154 void CTrailingLineFinder::AddLineL(const TDesC& aLine, TBool aComplete) |
|
155 { |
|
156 if (iLastLineNotComplete) |
|
157 { |
|
158 HBufC*& buf = iTrailingLines[iCurrentLineIndex]; |
|
159 buf = buf->ReAllocL(buf->Length() + aLine.Length()); |
|
160 buf->Des() += aLine; |
|
161 } |
|
162 else |
|
163 { |
|
164 HBufC* line = aLine.AllocLC(); |
|
165 if (iNumCompleteLinesFound <= iNumLinesToFind) |
|
166 { |
|
167 ++iCurrentLineIndex; |
|
168 User::LeaveIfError(iTrailingLines.Append(line)); |
|
169 } |
|
170 else |
|
171 { |
|
172 delete iTrailingLines[0]; |
|
173 iTrailingLines.Remove(0); |
|
174 iTrailingLines.Append(line); // Will always succeed due to the above removal. |
|
175 } |
|
176 CleanupStack::Pop(line); |
|
177 } |
|
178 |
|
179 iLastLineNotComplete = !aComplete; |
|
180 if (aComplete) |
|
181 { |
|
182 ++iNumCompleteLinesFound; |
|
183 } |
|
184 } |
|
185 |
|
186 CTrailingLineFinder::CTrailingLineFinder(CCharacterConverter& aCharacterConverter) |
|
187 : iCharacterConverter(aCharacterConverter), iPtr(NULL, 0) |
|
188 { |
|
189 } |
|
190 |
|
191 void CTrailingLineFinder::ConstructL(TInt aBlockSize) |
|
192 { |
|
193 iBuf = HBufC8::NewL(aBlockSize); |
|
194 iPtr.Set(iBuf->Des()); |
|
195 } |
|
196 |
|
197 void CTrailingLineFinder::FindNextLine(const TDesC& aData, TInt aOffset, TPtrC& aLine, TBool& aComplete) |
|
198 { |
|
199 const TText* first = aData.Ptr() + aOffset; |
|
200 const TText* p = first; |
|
201 const TText* last = first + aData.Length() - aOffset - 1; |
|
202 aComplete = EFalse; |
|
203 TBool foundLineBreak = EFalse; |
|
204 while ((p <= last) && !foundLineBreak) |
|
205 { |
|
206 if (iPossiblySplitCrLf) |
|
207 { |
|
208 iPossiblySplitCrLf = EFalse; |
|
209 if (*p == KLineFeed) |
|
210 { |
|
211 aComplete = ETrue; |
|
212 foundLineBreak = ETrue; |
|
213 } |
|
214 } |
|
215 else if ((*p == KUnicodeParagraphSeparator) || (*p == KUnicodeLineSeparator) || (*p == KLineFeed)) |
|
216 { |
|
217 aComplete = ETrue; |
|
218 foundLineBreak = ETrue; |
|
219 } |
|
220 else if (*p == KCarriageReturn) |
|
221 { |
|
222 if (p == last) |
|
223 { |
|
224 iPossiblySplitCrLf = ETrue; |
|
225 } |
|
226 else if (*(p + 1) == KLineFeed) |
|
227 { |
|
228 aComplete = ETrue; |
|
229 foundLineBreak = ETrue; |
|
230 ++p; |
|
231 } |
|
232 else |
|
233 { |
|
234 aComplete = ETrue; |
|
235 foundLineBreak = ETrue; |
|
236 } |
|
237 } |
|
238 ++p; |
|
239 } |
|
240 |
|
241 aLine.Set(first, p - first); |
|
242 } |
|
243 |
|
244 void CTrailingLineFinder::HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue) |
|
245 { |
|
246 TRAPD(err, HandleFileDataL(aData, aType, aContinue)); |
|
247 if (err) |
|
248 { |
|
249 iObserver->HandleTrailingLineError(err); |
|
250 } |
|
251 } |
|
252 |
|
253 void CTrailingLineFinder::HandleFileReadError(TInt aError) |
|
254 { |
|
255 iObserver->HandleTrailingLineError(aError); |
|
256 } |
|
257 |
|
258 |
|
259 // |
|
260 // CFileWatcher. |
|
261 // |
|
262 |
|
263 class CFileWatcher : public CActive |
|
264 { |
|
265 public: |
|
266 static CFileWatcher* NewL(RFs& aFs); |
|
267 ~CFileWatcher(); |
|
268 void Start(const TDesC& aFileName, MFileChangeObserver& aObserver); |
|
269 private: |
|
270 CFileWatcher(RFs& aFs); |
|
271 void Queue(); |
|
272 private: // From CActive. |
|
273 virtual void DoCancel(); |
|
274 virtual void RunL(); |
|
275 private: |
|
276 RFs& iFs; |
|
277 TFileName iFileName; |
|
278 MFileChangeObserver* iObserver; |
|
279 }; |
|
280 |
|
281 |
|
282 CFileWatcher* CFileWatcher::NewL(RFs& aFs) |
|
283 { |
|
284 return new(ELeave) CFileWatcher(aFs); |
|
285 } |
|
286 |
|
287 CFileWatcher::~CFileWatcher() |
|
288 { |
|
289 Cancel(); |
|
290 } |
|
291 |
|
292 void CFileWatcher::Start(const TDesC& aFileName, MFileChangeObserver& aObserver) |
|
293 { |
|
294 ASSERT(!IsActive()); |
|
295 iFileName = aFileName; |
|
296 iObserver = &aObserver; |
|
297 Queue(); |
|
298 } |
|
299 |
|
300 CFileWatcher::CFileWatcher(RFs& aFs) |
|
301 : CActive(CActive::EPriorityStandard), iFs(aFs) |
|
302 { |
|
303 CActiveScheduler::Add(this); |
|
304 } |
|
305 |
|
306 void CFileWatcher::Queue() |
|
307 { |
|
308 ASSERT(!IsActive()); |
|
309 iFs.NotifyChange(ENotifyWrite, iStatus, iFileName); |
|
310 SetActive(); |
|
311 } |
|
312 |
|
313 void CFileWatcher::DoCancel() |
|
314 { |
|
315 iFs.NotifyChangeCancel(iStatus); |
|
316 } |
|
317 |
|
318 void CFileWatcher::RunL() |
|
319 { |
|
320 Queue(); |
|
321 iObserver->HandleFileChange(iFileName); |
|
322 } |
|
323 |
|
324 |
|
325 // |
|
326 // CCmdTail. |
|
327 // |
|
328 |
|
329 CCommandBase* CCmdTail::NewLC() |
|
330 { |
|
331 CCmdTail* self = new(ELeave) CCmdTail(); |
|
332 CleanupStack::PushL(self); |
|
333 self->ConstructL(); |
|
334 return self; |
|
335 } |
|
336 |
|
337 CCmdTail::~CCmdTail() |
|
338 { |
|
339 delete iCharacterConverter; |
|
340 delete iTrailingLineFinder; |
|
341 delete iFileWatcher; |
|
342 delete iFileReader; |
|
343 } |
|
344 |
|
345 CCmdTail::CCmdTail() |
|
346 : CCommandBase(EManualComplete), iNumLines(10) |
|
347 { |
|
348 } |
|
349 |
|
350 void CCmdTail::ConstructL() |
|
351 { |
|
352 BaseConstructL(); |
|
353 } |
|
354 |
|
355 void CCmdTail::WriteChunkToConsoleL(const TDesC8& aData, TReadType aType) |
|
356 { |
|
357 switch (aType) |
|
358 { |
|
359 case MFileReaderObserver::EFirst: |
|
360 { |
|
361 Write(iCharacterConverter->ConvertChunkL(aData, CCharacterConverter::EFirst)); |
|
362 break; |
|
363 } |
|
364 case MFileReaderObserver::EMiddle: // Treat "middle" and "last" to same as with tailing it's not possible to know if "last" is really last. |
|
365 case MFileReaderObserver::ELast: |
|
366 { |
|
367 Write(iCharacterConverter->ConvertChunkL(aData, CCharacterConverter::EMiddle)); |
|
368 break; |
|
369 } |
|
370 default: |
|
371 { |
|
372 ASSERT(FALSE); |
|
373 } |
|
374 } |
|
375 } |
|
376 |
|
377 const TDesC& CCmdTail::Name() const |
|
378 { |
|
379 _LIT(KName, "tail"); |
|
380 return KName; |
|
381 } |
|
382 |
|
383 void CCmdTail::DoRunL() |
|
384 { |
|
385 iCharacterConverter = CCharacterConverter::NewL(KBlockSize, FsL()); |
|
386 iTrailingLineFinder = CTrailingLineFinder::NewL(KBlockSize, *iCharacterConverter); |
|
387 iFileWatcher = CFileWatcher::NewL(Fs()); |
|
388 iFileReader = CFileReader::NewL(KBlockSize, Fs(), ETrue); |
|
389 iTrailingLineFinder->Find(*iFileReader, iFileName, iNumLines, *this); |
|
390 } |
|
391 |
|
392 void CCmdTail::OptionsL(RCommandOptionList& aOptions) |
|
393 { |
|
394 _LIT(KOptFollow, "follow"); |
|
395 _LIT(KOptNumLines, "lines"); |
|
396 aOptions.AppendBoolL(iFollow, KOptFollow); |
|
397 aOptions.AppendIntL(iNumLines, KOptNumLines); |
|
398 } |
|
399 |
|
400 void CCmdTail::ArgumentsL(RCommandArgumentList& aArguments) |
|
401 { |
|
402 _LIT(KArgFileName, "file_name"); |
|
403 aArguments.AppendFileNameL(iFileName, KArgFileName); |
|
404 } |
|
405 |
|
406 void CCmdTail::HandleTrailingLines(TInt aNumLinesFound, TInt aEndFilePos) |
|
407 { |
|
408 if (iFollow) |
|
409 { |
|
410 iFilePos = aEndFilePos; |
|
411 iFileWatcher->Start(iFileName, *this); |
|
412 } |
|
413 for (TInt i = 0; i < aNumLinesFound; ++i) |
|
414 { |
|
415 Write((*iTrailingLineFinder)[i]); |
|
416 } |
|
417 if (!iFollow) |
|
418 { |
|
419 Complete(); |
|
420 } |
|
421 } |
|
422 |
|
423 void CCmdTail::HandleTrailingLineError(TInt aError) |
|
424 { |
|
425 PrintError(aError, _L("Problem finding trailing %d lines in %S: %d"), iNumLines, &iFileName, aError); |
|
426 Complete(aError); |
|
427 } |
|
428 |
|
429 void CCmdTail::HandleFileChange(const TDesC& /*aFileName*/) |
|
430 { |
|
431 if (!iFileReader->IsActive()) |
|
432 { |
|
433 iFileReader->Read(iFileName, iFilePos, *this); |
|
434 } |
|
435 } |
|
436 |
|
437 void CCmdTail::HandleFileData(const TDesC8& aData, TReadType aType, TBool& aContinue) |
|
438 { |
|
439 aContinue = ETrue; |
|
440 iFilePos += aData.Length(); |
|
441 TRAPD(err, WriteChunkToConsoleL(aData, aType)); |
|
442 if (err) |
|
443 { |
|
444 aContinue = EFalse; |
|
445 PrintError(err, _L("Problem writing chunk of %S to console: %d"), &iFileName, err); |
|
446 } |
|
447 } |
|
448 |
|
449 void CCmdTail::HandleFileReadError(TInt aError) |
|
450 { |
|
451 PrintWarning(_L("Problem reading %S: %d"), &iFileName, aError); |
|
452 } |
|
453 |
|
454 |
|
455 #ifdef EXE_BUILD |
|
456 EXE_BOILER_PLATE(CCmdTail) |
|
457 #endif |
|
458 |