Mark a static function as static
[p5sagit/p5-mst-13.2.git] / symbian / PerlApp.cpp
CommitLineData
27da23d5 1/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
2
3/* The PerlApp application is licensed under the same terms as Perl itself. */
4
5#include "PerlApp.h"
6
7#include <avkon.hrh>
8#include <aknnotewrappers.h>
9#include <apparc.h>
10#include <e32base.h>
11#include <e32cons.h>
12#include <eikenv.h>
13#include <bautils.h>
14#include <eikappui.h>
15#include <utf.h>
16#include <f32file.h>
17
18#include <AknCommonDialogs.h>
19
20#ifndef __SERIES60_1X__
21#include <CAknFileSelectionDialog.h>
22#endif
23
24#include <coemain.h>
25
26#include "PerlApp.hrh"
27#include "PerlApp.rsg"
28
29#include "patchlevel.h"
30#include "PerlBase.h"
31
32const TUid KPerlAppUid = { 0x102015F6 };
33
34// This is like the Symbian _LIT() but without the embedded L prefix,
35// which enables using #defined constants (which need to carry their
36// own L prefix).
37#ifndef _LIT_NO_L
38#define _LIT_NO_L(n, s) static const TLitC<sizeof(s)/2> n={sizeof(s)/2-1,s}
39#endif // #ifndef _LIT_NO_L
40
41_LIT(KAppName, "PerlApp");
42_LIT_NO_L(KFlavor, PERL_SYMBIANSDK_FLAVOR);
43_LIT(KAboutFormat,
44 "Perl %d.%d.%d, Symbian port %d.%d.%d, built for %S SDK %d.%d");
45_LIT(KCopyrightFormat,
46 "Copyright 1987-2005 Larry Wall and others, Symbian port Copyright Nokia 2004-2005");
47_LIT(KInboxPrefix, "\\System\\Mail\\");
48_LIT(KScriptPrefix, "\\Perl\\");
49
50_LIT8(KModulePrefix, SITELIB); // SITELIB from Perl config.h
51
52typedef TBuf<256> TMessageBuffer;
53typedef TBuf8<256> TPeekBuffer;
54typedef TBuf8<256> TFileName8;
55
56// Usage: DEBUG_PRINTF((_L("%S"), &aStr))
57#if 1
58#define DEBUG_PRINTF(s) {TMessageBuffer message; message.Format s; YesNoDialogL(message);}
59#endif
60
61TUid CPerlAppApplication::AppDllUid() const
62{
63 return KPerlAppUid;
64}
65
66enum TPerlAppPanic
67{
68 EPerlAppCommandUnknown = 1
69};
70
71void Panic(TPerlAppPanic aReason)
72{
73 User::Panic(KAppName, aReason);
74}
75
76void CPerlAppUi::ConstructL()
77{
78 BaseConstructL();
79 iAppView = CPerlAppView::NewL(ClientRect());
80 AddToStackL(iAppView);
81 iFs = NULL;
82 CEikonEnv::Static()->DisableExitChecks(ETrue); // Symbian FAQ-0577.
83}
84
85CPerlAppUi::~CPerlAppUi()
86{
87 if (iAppView) {
88 iEikonEnv->RemoveFromStack(iAppView);
89 delete iAppView;
90 iAppView = NULL;
91 }
92 if (iFs) {
93 delete iFs;
94 iFs = NULL;
95 }
96 if (iDoorObserver) // Otherwise the embedding application waits forever.
97 iDoorObserver->NotifyExit(MApaEmbeddedDocObserver::EEmpty);
98}
99
100static TBool DlgOk(CAknNoteDialog* dlg)
101{
102 return dlg && dlg->RunDlgLD() == EAknSoftkeyOk;
103}
104
105static TBool OkCancelDialogL(TDesC& aMessage)
106{
107 CAknNoteDialog* dlg =
108 new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
109 dlg->PrepareLC(R_OK_CANCEL_DIALOG);
110 dlg->SetTextL(aMessage);
111 return DlgOk(dlg);
112}
113
114static TBool YesNoDialogL(TDesC& aMessage)
115{
116 CAknNoteDialog* dlg =
117 new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
118 dlg->PrepareLC(R_YES_NO_DIALOG);
119 dlg->SetTextL(aMessage);
120 return DlgOk(dlg);
121}
122
123static TInt InformationNoteL(TDesC& aMessage)
124{
125 CAknInformationNote* note = new (ELeave) CAknInformationNote;
126 return note->ExecuteLD(aMessage);
127}
128
129static TInt ConfirmationNoteL(TDesC& aMessage)
130{
131 CAknConfirmationNote* note = new (ELeave) CAknConfirmationNote;
132 return note->ExecuteLD(aMessage);
133}
134
135static TInt WarningNoteL(TDesC& aMessage)
136{
137 CAknWarningNote* note = new (ELeave) CAknWarningNote;
138 return note->ExecuteLD(aMessage);
139}
140
141static TInt TextQueryDialogL(const TDesC& aPrompt, TDes& aData, const TInt aMaxLength)
142{
143 CAknTextQueryDialog* dlg =
144 new (ELeave) CAknTextQueryDialog(aData);
145 dlg->SetPromptL(aPrompt);
146 dlg->SetMaxLength(aMaxLength);
147 return dlg->ExecuteLD(R_TEXT_QUERY_DIALOG);
148}
149
150// The isXXX() come from the Perl headers.
151#define FILENAME_IS_ABSOLUTE(n) \
152 (isALPHA(((n)[0])) && ((n)[1]) == ':' && ((n)[2]) == '\\')
153
154static TBool IsInPerl(TFileName aFileName)
155{
156 TInt offset = aFileName.FindF(KScriptPrefix);
157 return ((offset == 0 && // \foo
158 aFileName[0] == '\\')
159 ||
160 (offset == 2 && // x:\foo
161 FILENAME_IS_ABSOLUTE(aFileName)));
162}
163
164static TBool IsInInbox(TFileName aFileName)
165{
166 TInt offset = aFileName.FindF(KInboxPrefix);
167 return ((offset == 0 && // \foo
168 aFileName[0] == '\\')
169 ||
170 (offset == 2 && // x:\foo
171 FILENAME_IS_ABSOLUTE(aFileName)));
172}
173
174static TBool IsPerlModule(TParsePtrC aParsed)
175{
176 return aParsed.Ext().CompareF(_L(".pm")) == 0;
177}
178
179static TBool IsPerlScript(TParsePtrC aParsed)
180{
181 return aParsed.Ext().CompareF(_L(".pl")) == 0;
182}
183
184static void CopyFromInboxL(RFs aFs, const TFileName& aSrc, const TFileName& aDst)
185{
186 TBool proceed = ETrue;
187 TMessageBuffer message;
188
189 message.Format(_L("%S is untrusted. Install only if you trust provider."), &aDst);
190 if (OkCancelDialogL(message)) {
191 message.Format(_L("Install as %S?"), &aDst);
192 if (OkCancelDialogL(message)) {
193 if (BaflUtils::FileExists(aFs, aDst)) {
194 message.Format(_L("Replace old %S?"), &aDst);
195 if (!OkCancelDialogL(message))
196 proceed = EFalse;
197 }
198 if (proceed) {
199 // Create directory?
200 TInt err = BaflUtils::CopyFile(aFs, aSrc, aDst);
201 if (err == KErrNone) {
202 message.Format(_L("Installed %S"), &aDst);
203 ConfirmationNoteL(message);
204 }
205 else {
206 message.Format(_L("Failure %d installing %S"), err, &aDst);
207 WarningNoteL(message);
208 }
209 }
210 }
211 }
212}
213
214static TBool FindPerlPackageName(TPeekBuffer aPeekBuffer, TInt aOff, TFileName& aFn)
215{
216 aFn.SetMax();
217 TInt m = aFn.MaxLength();
218 TInt n = aPeekBuffer.Length();
219 TInt i = 0;
220 TInt j = aOff;
221
222 aFn.SetMax();
223 // The following is a little regular expression
224 // engine that matches Perl package names.
225 if (j < n && isSPACE(aPeekBuffer[j])) {
226 while (j < n && isSPACE(aPeekBuffer[j])) j++;
227 if (j < n && isALPHA(aPeekBuffer[j])) {
228 while (j < n && isALNUM(aPeekBuffer[j])) {
229 while (j < n &&
230 isALNUM(aPeekBuffer[j]) &&
231 i < m)
232 aFn[i++] = aPeekBuffer[j++];
233 if (j + 1 < n &&
234 aPeekBuffer[j ] == ':' &&
235 aPeekBuffer[j + 1] == ':' &&
236 i < m) {
237 aFn[i++] = '\\';
238 j += 2;
239 if (j < n &&
240 isALPHA(aPeekBuffer[j])) {
241 while (j < n &&
242 isALNUM(aPeekBuffer[j]) &&
243 i < m)
244 aFn[i++] = aPeekBuffer[j++];
245 }
246 }
247 }
248 while (j < n && isSPACE(aPeekBuffer[j])) j++;
249 if (j < n && aPeekBuffer[j] == ';' && i + 3 < m) {
250 aFn.SetLength(i);
251 aFn.Append(_L(".pm"));
252 return ETrue;
253 }
254 }
255 }
256 return EFalse;
257}
258
259static void GuessPerlModule(TFileName& aGuess, TPeekBuffer aPeekBuffer, TParse aDrive)
260{
261 TInt offset = aPeekBuffer.Find(_L8("package"));
262 if (offset != KErrNotFound) {
263 const TInt KPackageLen = 7;
264 TFileName q;
265
266 if (!FindPerlPackageName(aPeekBuffer, offset + KPackageLen, q))
267 return;
268
269 TFileName8 p;
270 p.Copy(aDrive.Drive());
271 p.Append(KModulePrefix);
272
273 aGuess.SetMax();
274 if (p.Length() + 1 + q.Length() < aGuess.MaxLength()) {
275 TInt i = 0, j;
276
277 for (j = 0; j < p.Length(); j++)
278 aGuess[i++] = p[j];
279 aGuess[i++] = '\\';
280 for (j = 0; j < q.Length(); j++)
281 aGuess[i++] = q[j];
282 aGuess.SetLength(i);
283 }
284 else
285 aGuess.SetLength(0);
286 }
287}
288
289static TBool LooksLikePerlL(TPeekBuffer aPeekBuffer)
290{
291 return aPeekBuffer.Left(2).Compare(_L8("#!")) == 0 &&
292 aPeekBuffer.Find(_L8("perl")) != KErrNotFound;
293}
294
295static TBool InstallStuffL(const TFileName &aSrc, TParse aDrive, TParse aFile, TPeekBuffer aPeekBuffer, RFs aFs)
296{
297 TFileName aDst;
298 TPtrC drive = aDrive.Drive();
299 TPtrC namext = aFile.NameAndExt();
300
301 aDst.Format(_L("%S%S%S"), &drive, &KScriptPrefix, &namext);
302 if (!IsPerlScript(aDst) && !LooksLikePerlL(aPeekBuffer)) {
303 aDst.SetLength(0);
304 if (IsPerlModule(aDst))
305 GuessPerlModule(aDst, aPeekBuffer, aDrive);
306 }
307 if (aDst.Length() > 0) {
308 CopyFromInboxL(aFs, aSrc, aDst);
309 return ETrue;
310 }
311
312 return EFalse;
313}
314
315static void DoRunScriptL(TFileName aScriptName)
316{
317 CPerlBase* perl = CPerlBase::NewInterpreterLC();
318 TRAPD(error, perl->RunScriptL(aScriptName));
319 if (error != KErrNone) {
320 TMessageBuffer message;
321 message.Format(_L("Error %d"), error);
322 YesNoDialogL(message);
323 }
324 CleanupStack::PopAndDestroy(perl);
325}
326
327static TBool RunStuffL(const TFileName& aScriptName, TPeekBuffer aPeekBuffer)
328{
329 TBool isModule = EFalse;
330
331 if (IsInPerl(aScriptName) &&
332 (IsPerlScript(aScriptName) ||
333 (isModule = IsPerlModule(aScriptName)) ||
334 LooksLikePerlL(aPeekBuffer))) {
335 TMessageBuffer message;
336
337 if (isModule)
338 message.Format(_L("Really run module %S?"), &aScriptName);
339 else
340 message.Format(_L("Run %S?"), &aScriptName);
341 if (YesNoDialogL(message))
342 DoRunScriptL(aScriptName);
343
344 return ETrue;
345 }
346
347 return EFalse;
348}
349
350void CPerlAppUi::InstallOrRunL(const TFileName& aFileName)
351{
352 TParse aFile;
353 TParse aDrive;
354 TMessageBuffer message;
355
356 aFile.Set(aFileName, NULL, NULL);
357 if (FILENAME_IS_ABSOLUTE(aFileName)) {
358 aDrive.Set(aFileName, NULL, NULL);
359 } else {
360 TFileName appName =
361 CEikonEnv::Static()->EikAppUi()->Application()->AppFullName();
362 aDrive.Set(appName, NULL, NULL);
363 }
364 if (!iFs)
365 iFs = &CEikonEnv::Static()->FsSession();
366 RFile f;
367 TInt err = f.Open(*iFs, aFileName, EFileRead);
368 if (err == KErrNone) {
369 TPeekBuffer aPeekBuffer;
370 err = f.Read(aPeekBuffer);
371 f.Close(); // Release quickly.
372 if (err == KErrNone) {
373 if (!(IsInInbox(aFileName) ?
374 InstallStuffL(aFileName, aDrive, aFile, aPeekBuffer, *iFs) :
375 RunStuffL(aFileName, aPeekBuffer))) {
376 message.Format(_L("Failed for file %S"), &aFileName);
377 WarningNoteL(message);
378 }
379 } else {
380 message.Format(_L("Error %d reading %S"), err, &aFileName);
381 WarningNoteL(message);
382 }
383 } else {
384 message.Format(_L("Error %d opening %S"), err, &aFileName);
385 WarningNoteL(message);
386 }
387 if (iDoorObserver)
388 delete CEikonEnv::Static()->EikAppUi();
389 else
390 Exit();
391}
392
393void CPerlAppUi::OpenFileL(const TDesC& aFileName)
394{
395 InstallOrRunL(aFileName);
396 return;
397}
398
399TBool CPerlAppUi::ProcessCommandParametersL(TApaCommand aCommand, TFileName& /* aDocumentName */, const TDesC8& /* aTail */)
400{
401 return aCommand == EApaCommandOpen ? ETrue : EFalse;
402}
403
404void CPerlAppUi::SetFs(const RFs& aFs)
405{
406 iFs = (RFs*) &aFs;
407}
408
409void CPerlAppUi::HandleCommandL(TInt aCommand)
410{
411 TMessageBuffer message;
412
413 switch(aCommand)
414 {
415 case EEikCmdExit:
416 case EAknSoftkeyExit:
417 Exit();
418 break;
419 case EPerlAppCommandAbout:
420 {
421 message.Format(KAboutFormat,
422 PERL_REVISION,
423 PERL_VERSION,
424 PERL_SUBVERSION,
425 PERL_SYMBIANPORT_MAJOR,
426 PERL_SYMBIANPORT_MINOR,
427 PERL_SYMBIANPORT_PATCH,
428 &KFlavor,
429 PERL_SYMBIANSDK_MAJOR,
430 PERL_SYMBIANSDK_MINOR
431 );
432 InformationNoteL(message);
433 }
434 break;
435 case EPerlAppCommandTime:
436 {
437 CPerlBase* perl = CPerlBase::NewInterpreterLC();
438 const char *const argv[] =
439 { "perl", "-le",
440 "print 'Running in ', $^O, \"\\n\", scalar localtime" };
441 perl->ParseAndRun(sizeof(argv)/sizeof(char*), (char **)argv, 0);
442 CleanupStack::PopAndDestroy(perl);
443 }
444 break;
445 case EPerlAppCommandRunFile:
446 {
447 InformationNoteL(message);
448 TFileName aScriptUtf16;
449 if (AknCommonDialogs::RunSelectDlgLD(aScriptUtf16,
450 R_MEMORY_SELECTION_DIALOG))
451 DoRunScriptL(aScriptUtf16);
452 }
453 break;
454 case EPerlAppCommandOneLiner:
455 {
456 _LIT(prompt, "Oneliner:");
457 if (TextQueryDialogL(prompt, iOneLiner, KPerlAppOneLinerSize)) {
458 const TUint KPerlAppUtf8Multi = 3;
459 TBuf8<KPerlAppUtf8Multi * KPerlAppOneLinerSize> utf8;
460
461 CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8, iOneLiner);
462 CPerlBase* perl = CPerlBase::NewInterpreterLC();
463 int argc = 3;
464 char **argv = (char**) malloc(argc * sizeof(char *));
465 User::LeaveIfNull(argv);
466
467 TCleanupItem argvCleanupItem = TCleanupItem(free, argv);
468 CleanupStack::PushL(argvCleanupItem);
469 argv[0] = (char *) "perl";
470 argv[1] = (char *) "-le";
471 argv[2] = (char *) utf8.PtrZ();
472 perl->ParseAndRun(argc, argv);
473 CleanupStack::PopAndDestroy(2, perl);
474 }
475 }
476 break;
477 case EPerlAppCommandCopyright:
478 {
479 message.Format(KCopyrightFormat);
480 InformationNoteL(message);
481 }
482 break;
483
484 default:
485 Panic(EPerlAppCommandUnknown);
486 break;
487 }
488}
489
490CPerlAppView* CPerlAppView::NewL(const TRect& aRect)
491{
492 CPerlAppView* self = CPerlAppView::NewLC(aRect);
493 CleanupStack::Pop(self);
494 return self;
495}
496
497CPerlAppView* CPerlAppView::NewLC(const TRect& aRect)
498{
499 CPerlAppView* self = new (ELeave) CPerlAppView;
500 CleanupStack::PushL(self);
501 self->ConstructL(aRect);
502 return self;
503}
504
505void CPerlAppView::ConstructL(const TRect& aRect)
506{
507 CreateWindowL();
508 SetRect(aRect);
509 ActivateL();
510}
511
512void CPerlAppView::Draw(const TRect& /*aRect*/) const
513{
514 CWindowGc& gc = SystemGc();
515 TRect rect = Rect();
516 gc.Clear(rect);
517}
518
519CApaDocument* CPerlAppApplication::CreateDocumentL()
520{
521 CPerlAppDocument* document = new (ELeave) CPerlAppDocument(*this);
522 return document;
523}
524
525CEikAppUi* CPerlAppDocument::CreateAppUiL()
526{
527 CPerlAppUi* appui = new (ELeave) CPerlAppUi();
528 return appui;
529}
530
531CFileStore* CPerlAppDocument::OpenFileL(TBool /* aDoOpen */, const TDesC& aFileName, RFs& aFs)
532{
533 CPerlAppUi* appui =
534 STATIC_CAST(CPerlAppUi*, CEikonEnv::Static()->EikAppUi());
535 appui->SetFs(aFs);
536 appui->OpenFileL(aFileName);
537 return NULL;
538}
539
540EXPORT_C CApaApplication* NewApplication()
541{
542 return new CPerlAppApplication;
543}
544
545GLDEF_C TInt E32Dll(TDllReason /*aReason*/)
546{
547 return KErrNone;
548}
549