1 /* Copyright (c) 2004-2005 Nokia. All rights reserved. */
3 /* The PerlApp application is licensed under the same terms as Perl itself.
4 * Note that this PerlApp is for Symbian/Series 60/80 smartphones and it has
5 * nothing whatsoever to do with the ActiveState PerlApp. */
11 # include <aknnotewrappers.h>
12 # include <AknCommonDialogs.h>
13 # ifndef __SERIES60_1X__
14 # include <CAknFileSelectionDialog.h>
16 #endif /* #ifdef __SERIES60__ */
20 # include <cknflash.h>
21 # include <ckndgopn.h>
22 # include <ckndgfob.h>
23 # include <eiklabel.h>
25 #endif /* #ifdef __SERIES80__ */
29 # include <eikedwin.h>
30 # include <eiklabel.h>
31 #endif /* #ifdef __UIQ__ */
44 #ifndef PerlAppMinimal
46 #include "PerlApp.hrh"
48 #include "PerlApp.rsg"
52 #endif /* #ifdef __SERIES80__ */
54 #endif //#ifndef PerlAppMinimal
60 const TUid KPerlAppUid = {
61 #ifdef PerlAppMinimalUid
68 _LIT(KDefaultScript, "default.pl");
70 // This is like the Symbian _LIT() but without the embedded L prefix,
71 // which enables using #defined constants (which need to carry their
74 # define _LIT_NO_L(n, s) static const TLitC<sizeof(s)/2> n={sizeof(s)/2-1,s}
75 #endif // #ifndef _LIT_NO_L
77 #ifdef PerlAppMinimalName
78 _LIT_NO_L(KAppName, PerlAppMinimalName);
80 _LIT(KAppName, "PerlApp");
83 #ifndef PerlAppMinimal
85 _LIT_NO_L(KFlavor, PERL_SYMBIANSDK_FLAVOR);
87 "Perl %d.%d.%d, Symbian port %d.%d.%d, built for %S SDK %d.%d");
88 _LIT(KCopyrightFormat,
89 "Copyright 1987-2005 Larry Wall and others, Symbian port Copyright Nokia 2004-2005");
90 _LIT(KInboxPrefix, "\\System\\Mail\\");
91 _LIT(KScriptPrefix, "\\Perl\\");
93 _LIT8(KModulePrefix, SITELIB); // SITELIB from Perl config.h
95 typedef TBuf<256> TMessageBuffer;
96 typedef TBuf8<256> TPeekBuffer;
97 typedef TBuf8<256> TFileName8;
99 #endif // #ifndef PerlAppMinimal
101 // Usage: DEBUG_PRINTF((_L("%S"), &aStr))
103 #define DEBUG_PRINTF(s) {TMessageBuffer message; message.Format s; YesNoDialogL(message);}
106 static void DoRunScriptL(TFileName aScriptName);
108 TUid CPerlAppApplication::AppDllUid() const
115 EPerlAppCommandUnknown = 1
118 void Panic(TPerlAppPanic aReason)
120 User::Panic(KAppName, aReason);
123 void CPerlAppUi::ConstructL()
126 iAppView = CPerlAppView::NewL(ClientRect());
127 AddToStackL(iAppView);
129 CEikonEnv::Static()->DisableExitChecks(ETrue); // Symbian FAQ-0577.
132 CPerlAppUi::~CPerlAppUi()
135 iEikonEnv->RemoveFromStack(iAppView);
143 if (iDoorObserver) // Otherwise the embedding application waits forever.
144 iDoorObserver->NotifyExit(MApaEmbeddedDocObserver::EEmpty);
147 #ifndef PerlAppMinimal
149 static TBool OkCancelDialogL(TDesC& aMessage)
152 CAknNoteDialog* dlg =
153 new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
154 dlg->PrepareLC(R_OK_CANCEL_DIALOG);
155 dlg->SetTextL(aMessage);
156 return dlg->RunDlgLD() == EAknSoftkeyOk;
157 #endif /* #ifdef __SERIES60__ */
159 return CCknConfirmationDialog::RunDlgWithDefaultIconLD(aMessage, R_EIK_BUTTONS_CANCEL_OK);
160 #endif /* #ifdef __SERIES80__ */
162 CEikDialog* dlg = new (ELeave) CEikDialog();
163 return dlg->ExecuteLD(R_OK_CANCEL_DIALOG) == EEikBidOk;
164 #endif /* #ifdef __UIQ__ */
167 static TBool YesNoDialogL(TDesC& aMessage)
170 CAknNoteDialog* dlg =
171 new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
172 dlg->PrepareLC(R_YES_NO_DIALOG);
173 dlg->SetTextL(aMessage);
174 return dlg->RunDlgLD() == EAknSoftkeyOk;
175 #endif /* #ifdef __SERIES60__ */
177 return CCknConfirmationDialog::RunDlgWithDefaultIconLD(aMessage, R_EIK_BUTTONS_NO_YES);
178 #endif /* #ifdef __SERIES80__ */
180 CEikDialog* dlg = new (ELeave) CEikDialog();
181 return dlg->ExecuteLD(R_YES_NO_DIALOG) == EEikBidOk;
182 #endif /* #ifdef __UIQ__ */
185 static void InformationNoteL(TDesC& aMessage)
188 CAknInformationNote* note = new (ELeave) CAknInformationNote;
189 note->ExecuteLD(aMessage);
190 #endif /* #ifdef __SERIES60__ */
191 #if defined(__SERIES80__) || defined(__UIQ__)
192 CEikonEnv::Static()->InfoMsg(aMessage);
193 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
196 static TInt WarningNoteL(TDesC& aMessage)
199 CAknWarningNote* note = new (ELeave) CAknWarningNote;
200 return note->ExecuteLD(aMessage);
201 #endif /* #ifdef __SERIES60__ */
202 #if defined(__SERIES80__) || defined(__UIQ__)
203 CEikonEnv::Static()->AlertWin(aMessage);
205 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
208 #if defined(__SERIES80__) || defined(__UIQ__)
210 CPerlAppTextQueryDialog::CPerlAppTextQueryDialog(HBufC*& aBuffer) :
215 TBool CPerlAppTextQueryDialog::OkToExitL(TInt /* aKeycode */)
217 iData = static_cast<CEikEdwin*>(Control(EPerlAppTextQueryInputField))->GetTextInHBufL();
221 void CPerlAppTextQueryDialog::PreLayoutDynInitL()
224 CEikLabel* promptLabel = ControlCaption(EPerlAppTextQueryInputField);
225 promptLabel->SetTextL(iPrompt);
228 /* TODO: OfferKeyEventL() so that newline can be seen as 'OK'.
229 * Or a hotkey for the button? */
231 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
233 static TInt TextQueryDialogL(const TDesC& aTitle, const TDesC& aPrompt, TDes& aData, const TInt aMaxLength)
236 CAknTextQueryDialog* dlg =
237 new (ELeave) CAknTextQueryDialog(aData);
238 dlg->SetPromptL(aPrompt);
239 dlg->SetMaxLength(aMaxLength);
240 return dlg->ExecuteLD(R_TEXT_QUERY_DIALOG);
241 #endif /* #ifdef __SERIES60__ */
242 #if defined(__SERIES80__) || defined(__UIQ__)
244 CPerlAppTextQueryDialog* dlg =
245 new (ELeave) CPerlAppTextQueryDialog(data);
246 dlg->iTitle.Set(aTitle);
247 dlg->iPrompt.Set(aPrompt);
248 dlg->iMaxLength = aMaxLength;
249 if (dlg->ExecuteLD(R_PERL_ONELINER_DIALOG)) {
254 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
257 static TBool FileQueryDialogL(TDes& aFilename)
260 return AknCommonDialogs::RunSelectDlgLD(aFilename,
261 R_MEMORY_SELECTION_DIALOG);
262 #endif /* #ifdef __SERIES60__ */
264 if (CCknOpenFileDialog::RunDlgLD(aFilename,
265 CCknFileListDialogBase::EShowAllDrives
266 |CCknFileListDialogBase::EShowSystemFilesAndFolders
267 |CCknFileListDialogBase::EShowBothFilesAndFolders
269 TEntry aEntry; // Be paranoid and check that the file is there.
272 if (aFs.Entry(aFilename, aEntry) == KErrNone)
275 CEikonEnv::Static()->InfoMsg(_L("File not found"));
278 #endif /* #ifdef __SERIES80__ */
280 return EFalse; // No filesystem access in UIQ 2.x!
281 #endif /* #ifdef __UIQ__ */
284 // The isXXX() come from the Perl headers.
285 #define FILENAME_IS_ABSOLUTE(n) \
286 (isALPHA(((n)[0])) && ((n)[1]) == ':' && ((n)[2]) == '\\')
288 static TBool IsInPerl(TFileName aFileName)
290 TInt offset = aFileName.FindF(KScriptPrefix);
291 return ((offset == 0 && // \foo
292 aFileName[0] == '\\')
294 (offset == 2 && // x:\foo
295 FILENAME_IS_ABSOLUTE(aFileName)));
298 static TBool IsInInbox(TFileName aFileName)
300 TInt offset = aFileName.FindF(KInboxPrefix);
301 return ((offset == 0 && // \foo
302 aFileName[0] == '\\')
304 (offset == 2 && // x:\foo
305 FILENAME_IS_ABSOLUTE(aFileName)));
308 static TBool IsPerlModule(TParsePtrC aParsed)
310 return aParsed.Ext().CompareF(_L(".pm")) == 0;
313 static TBool IsPerlScript(TParsePtrC aParsed)
315 return aParsed.Ext().CompareF(_L(".pl")) == 0;
318 static void CopyFromInboxL(RFs aFs, const TFileName& aSrc, const TFileName& aDst)
320 TBool proceed = ETrue;
321 TMessageBuffer message;
323 message.Format(_L("%S is untrusted. Install only if you trust provider."), &aDst);
324 if (OkCancelDialogL(message)) {
325 message.Format(_L("Install as %S?"), &aDst);
326 if (OkCancelDialogL(message)) {
327 if (BaflUtils::FileExists(aFs, aDst)) {
328 message.Format(_L("Replace old %S?"), &aDst);
329 if (!OkCancelDialogL(message))
334 TInt err = BaflUtils::CopyFile(aFs, aSrc, aDst);
335 if (err == KErrNone) {
336 message.Format(_L("Installed %S"), &aDst);
337 InformationNoteL(message);
340 message.Format(_L("Failure %d installing %S"), err, &aDst);
341 WarningNoteL(message);
348 static TBool FindPerlPackageName(TPeekBuffer aPeekBuffer, TInt aOff, TFileName& aFn)
351 TInt m = aFn.MaxLength();
352 TInt n = aPeekBuffer.Length();
357 // The following is a little regular expression
358 // engine that matches Perl package names.
359 if (j < n && isSPACE(aPeekBuffer[j])) {
360 while (j < n && isSPACE(aPeekBuffer[j])) j++;
361 if (j < n && isALPHA(aPeekBuffer[j])) {
362 while (j < n && isALNUM(aPeekBuffer[j])) {
364 isALNUM(aPeekBuffer[j]) &&
366 aFn[i++] = aPeekBuffer[j++];
368 aPeekBuffer[j ] == ':' &&
369 aPeekBuffer[j + 1] == ':' &&
374 isALPHA(aPeekBuffer[j])) {
376 isALNUM(aPeekBuffer[j]) &&
378 aFn[i++] = aPeekBuffer[j++];
382 while (j < n && isSPACE(aPeekBuffer[j])) j++;
383 if (j < n && aPeekBuffer[j] == ';' && i + 3 < m) {
385 aFn.Append(_L(".pm"));
393 static void GuessPerlModule(TFileName& aGuess, TPeekBuffer aPeekBuffer, TParse aDrive)
395 TInt offset = aPeekBuffer.Find(_L8("package"));
396 if (offset != KErrNotFound) {
397 const TInt KPackageLen = 7;
400 if (!FindPerlPackageName(aPeekBuffer, offset + KPackageLen, q))
404 p.Copy(aDrive.Drive());
405 p.Append(KModulePrefix);
408 if (p.Length() + 1 + q.Length() < aGuess.MaxLength()) {
411 for (j = 0; j < p.Length(); j++)
414 for (j = 0; j < q.Length(); j++)
423 static TBool LooksLikePerlL(TPeekBuffer aPeekBuffer)
425 return aPeekBuffer.Left(2).Compare(_L8("#!")) == 0 &&
426 aPeekBuffer.Find(_L8("perl")) != KErrNotFound;
429 static TBool InstallStuffL(const TFileName &aSrc, TParse aDrive, TParse aFile, TPeekBuffer aPeekBuffer, RFs aFs)
432 TPtrC drive = aDrive.Drive();
433 TPtrC namext = aFile.NameAndExt();
435 aDst.Format(_L("%S%S%S"), &drive, &KScriptPrefix, &namext);
436 if (!IsPerlScript(aDst) && !LooksLikePerlL(aPeekBuffer)) {
438 if (IsPerlModule(aDst))
439 GuessPerlModule(aDst, aPeekBuffer, aDrive);
441 if (aDst.Length() > 0) {
442 CopyFromInboxL(aFs, aSrc, aDst);
449 static TBool RunStuffL(const TFileName& aScriptName, TPeekBuffer aPeekBuffer)
451 TBool isModule = EFalse;
453 if (IsInPerl(aScriptName) &&
454 (IsPerlScript(aScriptName) ||
455 (isModule = IsPerlModule(aScriptName)) ||
456 LooksLikePerlL(aPeekBuffer))) {
457 TMessageBuffer message;
460 message.Format(_L("Really run module %S?"), &aScriptName);
462 message.Format(_L("Run %S?"), &aScriptName);
463 if (YesNoDialogL(message))
464 DoRunScriptL(aScriptName);
471 void CPerlAppUi::InstallOrRunL(const TFileName& aFileName)
475 TMessageBuffer message;
477 aFile.Set(aFileName, NULL, NULL);
478 if (FILENAME_IS_ABSOLUTE(aFileName)) {
479 aDrive.Set(aFileName, NULL, NULL);
482 CEikonEnv::Static()->EikAppUi()->Application()->AppFullName();
483 aDrive.Set(appName, NULL, NULL);
486 iFs = &CEikonEnv::Static()->FsSession();
488 TInt err = f.Open(*iFs, aFileName, EFileRead);
489 if (err == KErrNone) {
490 TPeekBuffer aPeekBuffer;
491 err = f.Read(aPeekBuffer);
492 f.Close(); // Release quickly.
493 if (err == KErrNone) {
494 if (!(IsInInbox(aFileName) ?
495 InstallStuffL(aFileName, aDrive, aFile, aPeekBuffer, *iFs) :
496 RunStuffL(aFileName, aPeekBuffer))) {
497 message.Format(_L("Failed for file %S"), &aFileName);
498 WarningNoteL(message);
501 message.Format(_L("Error %d reading %S"), err, &aFileName);
502 WarningNoteL(message);
505 message.Format(_L("Error %d opening %S"), err, &aFileName);
506 WarningNoteL(message);
509 delete CEikonEnv::Static()->EikAppUi();
514 #endif // #ifndef PerlAppMinimal
516 static void DoRunScriptL(TFileName aScriptName)
518 CPerlBase* perl = CPerlBase::NewInterpreterLC();
519 TRAPD(error, perl->RunScriptL(aScriptName));
520 #ifndef PerlAppMinimal
521 if (error != KErrNone) {
522 TMessageBuffer message;
523 message.Format(_L("Error %d"), error);
524 YesNoDialogL(message);
526 #endif // #ifndef PerlAppMinimal
527 CleanupStack::PopAndDestroy(perl);
530 #ifndef PerlAppMinimal
532 void CPerlAppUi::OpenFileL(const TDesC& aFileName)
534 InstallOrRunL(aFileName);
538 #endif // #ifndef PerlAppMinimal
540 TBool CPerlAppUi::ProcessCommandParametersL(TApaCommand aCommand, TFileName& /* aDocumentName */, const TDesC8& /* aTail */)
542 if (aCommand == EApaCommandRun) {
543 TFileName appName = Application()->AppFullName();
545 p.Set(KDefaultScript, &appName, NULL);
549 if (aFs.Entry(p.FullName(), aEntry) == KErrNone) {
550 DoRunScriptL(p.FullName());
554 return aCommand == EApaCommandOpen ? ETrue : EFalse;
557 #ifndef PerlAppMinimal
559 void CPerlAppUi::SetFs(const RFs& aFs)
564 #endif // #ifndef PerlAppMinimal
566 static void DoHandleCommandL(TInt aCommand) {
567 #ifndef PerlAppMinimal
568 TMessageBuffer message;
569 #endif // #ifndef PerlAppMinimal
573 #ifndef PerlAppMinimal
574 case EPerlAppCommandAbout:
576 message.Format(KAboutFormat,
580 PERL_SYMBIANPORT_MAJOR,
581 PERL_SYMBIANPORT_MINOR,
582 PERL_SYMBIANPORT_PATCH,
584 PERL_SYMBIANSDK_MAJOR,
585 PERL_SYMBIANSDK_MINOR
587 InformationNoteL(message);
590 case EPerlAppCommandTime:
592 CPerlBase* perl = CPerlBase::NewInterpreterLC();
593 const char *const argv[] =
595 "print 'Running in ', $^O, \"\\n\", scalar localtime" };
596 perl->ParseAndRun(sizeof(argv)/sizeof(char*), (char **)argv, 0);
597 CleanupStack::PopAndDestroy(perl);
600 case EPerlAppCommandRunFile:
602 TFileName aScriptUtf16;
603 aScriptUtf16.Copy(_L("C:\\"));
604 if (FileQueryDialogL(aScriptUtf16))
605 DoRunScriptL(aScriptUtf16);
608 case EPerlAppCommandOneLiner:
611 _LIT(prompt, "Oneliner:");
612 #endif /* #ifdef __SERIES60__ */
613 #if defined(__SERIES80__) || defined(__UIQ__)
614 _LIT(prompt, "Code:"); // The title has "Oneliner" already.
615 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
617 STATIC_CAST(CPerlAppUi*, CEikonEnv::Static()->EikAppUi());
618 if (TextQueryDialogL(_L("Oneliner"),
621 KPerlAppOneLinerSize)) {
622 const TUint KPerlAppUtf8Multi = 3;
623 TBuf8<KPerlAppUtf8Multi * KPerlAppOneLinerSize> utf8;
625 CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8, cAppUi->iOneLiner);
626 CPerlBase* perl = CPerlBase::NewInterpreterLC();
628 char **argv = (char**) malloc(argc * sizeof(char *));
629 User::LeaveIfNull(argv);
631 TCleanupItem argvCleanupItem = TCleanupItem(free, argv);
632 CleanupStack::PushL(argvCleanupItem);
633 argv[0] = (char *) "perl";
634 argv[1] = (char *) "-le";
635 argv[2] = (char *) utf8.PtrZ();
636 perl->ParseAndRun(argc, argv);
637 CleanupStack::PopAndDestroy(2, perl);
641 case EPerlAppCommandCopyright:
643 message.Format(KCopyrightFormat);
644 InformationNoteL(message);
647 case EPerlAppCommandAboutCopyright:
651 m1.Format(KAboutFormat,
655 PERL_SYMBIANPORT_MAJOR,
656 PERL_SYMBIANPORT_MINOR,
657 PERL_SYMBIANPORT_PATCH,
659 PERL_SYMBIANSDK_MAJOR,
660 PERL_SYMBIANSDK_MINOR
662 InformationNoteL(m1);
663 User::After((TTimeIntervalMicroSeconds32) (1000*1000));
664 m2.Format(KCopyrightFormat);
665 InformationNoteL(m2);
668 #endif // #ifndef PerlAppMinimal
670 Panic(EPerlAppCommandUnknown);
676 void CPerlAppUi::HandleCommandL(TInt aCommand)
681 case EAknSoftkeyExit:
685 DoHandleCommandL(aCommand);
690 #endif /* #ifdef __SERIES60__ */
692 #if defined(__SERIES80__) || defined(__UIQ__)
694 void CPerlAppView::HandleCommandL(TInt aCommand) {
695 DoHandleCommandL(aCommand);
698 void CPerlAppUi::HandleCommandL(TInt aCommand) {
705 iAppView->HandleCommandL(aCommand);
710 #endif /* #if defined(__SERIES80__) || defined(__UIQ__) */
712 CPerlAppView* CPerlAppView::NewL(const TRect& aRect)
714 CPerlAppView* self = CPerlAppView::NewLC(aRect);
715 CleanupStack::Pop(self);
719 CPerlAppView* CPerlAppView::NewLC(const TRect& aRect)
721 CPerlAppView* self = new (ELeave) CPerlAppView;
722 CleanupStack::PushL(self);
723 self->ConstructL(aRect);
727 void CPerlAppView::ConstructL(const TRect& aRect)
734 CPerlAppView::~CPerlAppView()
738 void CPerlAppView::Draw(const TRect& /*aRect*/) const
740 CWindowGc& gc = SystemGc();
745 CApaDocument* CPerlAppApplication::CreateDocumentL()
747 CPerlAppDocument* document = new (ELeave) CPerlAppDocument(*this);
751 CEikAppUi* CPerlAppDocument::CreateAppUiL()
753 CPerlAppUi* appui = new (ELeave) CPerlAppUi();
758 #ifndef PerlAppMinimal
760 CFileStore* CPerlAppDocument::OpenFileL(TBool aDoOpen, const TDesC& aFileName, RFs& aFs)
763 STATIC_CAST(CPerlAppUi*, CEikonEnv::Static()->EikAppUi());
766 appui->OpenFileL(aFileName);
770 #endif // #ifndef PerlAppMinimal
772 EXPORT_C CApaApplication* NewApplication()
774 return new CPerlAppApplication;
777 GLDEF_C TInt E32Dll(TDllReason /*aReason*/)