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 smartphones and has nothing
5 * whatsoever to do with the ActiveState PerlApp. */
10 #include <aknnotewrappers.h>
20 #include <AknCommonDialogs.h>
22 #ifndef __SERIES60_1X__
23 #include <CAknFileSelectionDialog.h>
30 #include "PerlApp.hrh"
31 #include "PerlApp.rsg"
33 #endif //#ifndef PerlMin
39 const TUid KPerlAppUid = {
47 _LIT(KDefaultScript, "default.pl");
49 // This is like the Symbian _LIT() but without the embedded L prefix,
50 // which enables using #defined constants (which need to carry their
53 # define _LIT_NO_L(n, s) static const TLitC<sizeof(s)/2> n={sizeof(s)/2-1,s}
54 #endif // #ifndef _LIT_NO_L
57 _LIT_NO_L(KAppName, PerlMinName);
59 _LIT(KAppName, "PerlApp");
64 _LIT_NO_L(KFlavor, PERL_SYMBIANSDK_FLAVOR);
66 "Perl %d.%d.%d, Symbian port %d.%d.%d, built for %S SDK %d.%d");
67 _LIT(KCopyrightFormat,
68 "Copyright 1987-2005 Larry Wall and others, Symbian port Copyright Nokia 2004-2005");
69 _LIT(KInboxPrefix, "\\System\\Mail\\");
70 _LIT(KScriptPrefix, "\\Perl\\");
72 _LIT8(KModulePrefix, SITELIB); // SITELIB from Perl config.h
74 typedef TBuf<256> TMessageBuffer;
75 typedef TBuf8<256> TPeekBuffer;
76 typedef TBuf8<256> TFileName8;
78 #endif // #ifndef PerlMin
80 // Usage: DEBUG_PRINTF((_L("%S"), &aStr))
82 #define DEBUG_PRINTF(s) {TMessageBuffer message; message.Format s; YesNoDialogL(message);}
85 static void DoRunScriptL(TFileName aScriptName);
87 TUid CPerlAppApplication::AppDllUid() const
94 EPerlAppCommandUnknown = 1
97 void Panic(TPerlAppPanic aReason)
99 User::Panic(KAppName, aReason);
102 void CPerlAppUi::ConstructL()
105 iAppView = CPerlAppView::NewL(ClientRect());
106 AddToStackL(iAppView);
108 CEikonEnv::Static()->DisableExitChecks(ETrue); // Symbian FAQ-0577.
111 CPerlAppUi::~CPerlAppUi()
114 iEikonEnv->RemoveFromStack(iAppView);
122 if (iDoorObserver) // Otherwise the embedding application waits forever.
123 iDoorObserver->NotifyExit(MApaEmbeddedDocObserver::EEmpty);
128 static TBool DlgOk(CAknNoteDialog* dlg)
130 return dlg && dlg->RunDlgLD() == EAknSoftkeyOk;
133 static TBool OkCancelDialogL(TDesC& aMessage)
135 CAknNoteDialog* dlg =
136 new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
137 dlg->PrepareLC(R_OK_CANCEL_DIALOG);
138 dlg->SetTextL(aMessage);
142 static TBool YesNoDialogL(TDesC& aMessage)
144 CAknNoteDialog* dlg =
145 new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
146 dlg->PrepareLC(R_YES_NO_DIALOG);
147 dlg->SetTextL(aMessage);
151 static TInt InformationNoteL(TDesC& aMessage)
153 CAknInformationNote* note = new (ELeave) CAknInformationNote;
154 return note->ExecuteLD(aMessage);
157 static TInt ConfirmationNoteL(TDesC& aMessage)
159 CAknConfirmationNote* note = new (ELeave) CAknConfirmationNote;
160 return note->ExecuteLD(aMessage);
163 static TInt WarningNoteL(TDesC& aMessage)
165 CAknWarningNote* note = new (ELeave) CAknWarningNote;
166 return note->ExecuteLD(aMessage);
169 static TInt TextQueryDialogL(const TDesC& aPrompt, TDes& aData, const TInt aMaxLength)
171 CAknTextQueryDialog* dlg =
172 new (ELeave) CAknTextQueryDialog(aData);
173 dlg->SetPromptL(aPrompt);
174 dlg->SetMaxLength(aMaxLength);
175 return dlg->ExecuteLD(R_TEXT_QUERY_DIALOG);
178 // The isXXX() come from the Perl headers.
179 #define FILENAME_IS_ABSOLUTE(n) \
180 (isALPHA(((n)[0])) && ((n)[1]) == ':' && ((n)[2]) == '\\')
182 static TBool IsInPerl(TFileName aFileName)
184 TInt offset = aFileName.FindF(KScriptPrefix);
185 return ((offset == 0 && // \foo
186 aFileName[0] == '\\')
188 (offset == 2 && // x:\foo
189 FILENAME_IS_ABSOLUTE(aFileName)));
192 static TBool IsInInbox(TFileName aFileName)
194 TInt offset = aFileName.FindF(KInboxPrefix);
195 return ((offset == 0 && // \foo
196 aFileName[0] == '\\')
198 (offset == 2 && // x:\foo
199 FILENAME_IS_ABSOLUTE(aFileName)));
202 static TBool IsPerlModule(TParsePtrC aParsed)
204 return aParsed.Ext().CompareF(_L(".pm")) == 0;
207 static TBool IsPerlScript(TParsePtrC aParsed)
209 return aParsed.Ext().CompareF(_L(".pl")) == 0;
212 static void CopyFromInboxL(RFs aFs, const TFileName& aSrc, const TFileName& aDst)
214 TBool proceed = ETrue;
215 TMessageBuffer message;
217 message.Format(_L("%S is untrusted. Install only if you trust provider."), &aDst);
218 if (OkCancelDialogL(message)) {
219 message.Format(_L("Install as %S?"), &aDst);
220 if (OkCancelDialogL(message)) {
221 if (BaflUtils::FileExists(aFs, aDst)) {
222 message.Format(_L("Replace old %S?"), &aDst);
223 if (!OkCancelDialogL(message))
228 TInt err = BaflUtils::CopyFile(aFs, aSrc, aDst);
229 if (err == KErrNone) {
230 message.Format(_L("Installed %S"), &aDst);
231 ConfirmationNoteL(message);
234 message.Format(_L("Failure %d installing %S"), err, &aDst);
235 WarningNoteL(message);
242 static TBool FindPerlPackageName(TPeekBuffer aPeekBuffer, TInt aOff, TFileName& aFn)
245 TInt m = aFn.MaxLength();
246 TInt n = aPeekBuffer.Length();
251 // The following is a little regular expression
252 // engine that matches Perl package names.
253 if (j < n && isSPACE(aPeekBuffer[j])) {
254 while (j < n && isSPACE(aPeekBuffer[j])) j++;
255 if (j < n && isALPHA(aPeekBuffer[j])) {
256 while (j < n && isALNUM(aPeekBuffer[j])) {
258 isALNUM(aPeekBuffer[j]) &&
260 aFn[i++] = aPeekBuffer[j++];
262 aPeekBuffer[j ] == ':' &&
263 aPeekBuffer[j + 1] == ':' &&
268 isALPHA(aPeekBuffer[j])) {
270 isALNUM(aPeekBuffer[j]) &&
272 aFn[i++] = aPeekBuffer[j++];
276 while (j < n && isSPACE(aPeekBuffer[j])) j++;
277 if (j < n && aPeekBuffer[j] == ';' && i + 3 < m) {
279 aFn.Append(_L(".pm"));
287 static void GuessPerlModule(TFileName& aGuess, TPeekBuffer aPeekBuffer, TParse aDrive)
289 TInt offset = aPeekBuffer.Find(_L8("package"));
290 if (offset != KErrNotFound) {
291 const TInt KPackageLen = 7;
294 if (!FindPerlPackageName(aPeekBuffer, offset + KPackageLen, q))
298 p.Copy(aDrive.Drive());
299 p.Append(KModulePrefix);
302 if (p.Length() + 1 + q.Length() < aGuess.MaxLength()) {
305 for (j = 0; j < p.Length(); j++)
308 for (j = 0; j < q.Length(); j++)
317 static TBool LooksLikePerlL(TPeekBuffer aPeekBuffer)
319 return aPeekBuffer.Left(2).Compare(_L8("#!")) == 0 &&
320 aPeekBuffer.Find(_L8("perl")) != KErrNotFound;
323 static TBool InstallStuffL(const TFileName &aSrc, TParse aDrive, TParse aFile, TPeekBuffer aPeekBuffer, RFs aFs)
326 TPtrC drive = aDrive.Drive();
327 TPtrC namext = aFile.NameAndExt();
329 aDst.Format(_L("%S%S%S"), &drive, &KScriptPrefix, &namext);
330 if (!IsPerlScript(aDst) && !LooksLikePerlL(aPeekBuffer)) {
332 if (IsPerlModule(aDst))
333 GuessPerlModule(aDst, aPeekBuffer, aDrive);
335 if (aDst.Length() > 0) {
336 CopyFromInboxL(aFs, aSrc, aDst);
343 static TBool RunStuffL(const TFileName& aScriptName, TPeekBuffer aPeekBuffer)
345 TBool isModule = EFalse;
347 if (IsInPerl(aScriptName) &&
348 (IsPerlScript(aScriptName) ||
349 (isModule = IsPerlModule(aScriptName)) ||
350 LooksLikePerlL(aPeekBuffer))) {
351 TMessageBuffer message;
354 message.Format(_L("Really run module %S?"), &aScriptName);
356 message.Format(_L("Run %S?"), &aScriptName);
357 if (YesNoDialogL(message))
358 DoRunScriptL(aScriptName);
366 void CPerlAppUi::InstallOrRunL(const TFileName& aFileName)
370 TMessageBuffer message;
372 aFile.Set(aFileName, NULL, NULL);
373 if (FILENAME_IS_ABSOLUTE(aFileName)) {
374 aDrive.Set(aFileName, NULL, NULL);
377 CEikonEnv::Static()->EikAppUi()->Application()->AppFullName();
378 aDrive.Set(appName, NULL, NULL);
381 iFs = &CEikonEnv::Static()->FsSession();
383 TInt err = f.Open(*iFs, aFileName, EFileRead);
384 if (err == KErrNone) {
385 TPeekBuffer aPeekBuffer;
386 err = f.Read(aPeekBuffer);
387 f.Close(); // Release quickly.
388 if (err == KErrNone) {
389 if (!(IsInInbox(aFileName) ?
390 InstallStuffL(aFileName, aDrive, aFile, aPeekBuffer, *iFs) :
391 RunStuffL(aFileName, aPeekBuffer))) {
392 message.Format(_L("Failed for file %S"), &aFileName);
393 WarningNoteL(message);
396 message.Format(_L("Error %d reading %S"), err, &aFileName);
397 WarningNoteL(message);
400 message.Format(_L("Error %d opening %S"), err, &aFileName);
401 WarningNoteL(message);
404 delete CEikonEnv::Static()->EikAppUi();
409 #endif // #ifndef PerlMin
411 static void DoRunScriptL(TFileName aScriptName)
413 CPerlBase* perl = CPerlBase::NewInterpreterLC();
414 TRAPD(error, perl->RunScriptL(aScriptName));
416 if (error != KErrNone) {
417 TMessageBuffer message;
418 message.Format(_L("Error %d"), error);
419 YesNoDialogL(message);
421 #endif // #ifndef PerlMin
422 CleanupStack::PopAndDestroy(perl);
427 void CPerlAppUi::OpenFileL(const TDesC& aFileName)
429 InstallOrRunL(aFileName);
433 #endif // #ifndef PerlMin
435 TBool CPerlAppUi::ProcessCommandParametersL(TApaCommand aCommand, TFileName& /* aDocumentName */, const TDesC8& /* aTail */)
437 if (aCommand == EApaCommandRun) {
438 TFileName appName = Application()->AppFullName();
440 p.Set(KDefaultScript, &appName, NULL);
444 if (aFs.Entry(p.FullName(), aEntry) == KErrNone) {
445 DoRunScriptL(p.FullName());
449 return aCommand == EApaCommandOpen ? ETrue : EFalse;
454 void CPerlAppUi::SetFs(const RFs& aFs)
459 #endif // #ifndef PerlMin
461 void CPerlAppUi::HandleCommandL(TInt aCommand)
464 TMessageBuffer message;
465 #endif // #ifndef PerlMin
470 case EAknSoftkeyExit:
474 case EPerlAppCommandAbout:
476 message.Format(KAboutFormat,
480 PERL_SYMBIANPORT_MAJOR,
481 PERL_SYMBIANPORT_MINOR,
482 PERL_SYMBIANPORT_PATCH,
484 PERL_SYMBIANSDK_MAJOR,
485 PERL_SYMBIANSDK_MINOR
487 InformationNoteL(message);
490 case EPerlAppCommandTime:
492 CPerlBase* perl = CPerlBase::NewInterpreterLC();
493 const char *const argv[] =
495 "print 'Running in ', $^O, \"\\n\", scalar localtime" };
496 perl->ParseAndRun(sizeof(argv)/sizeof(char*), (char **)argv, 0);
497 CleanupStack::PopAndDestroy(perl);
500 case EPerlAppCommandRunFile:
502 InformationNoteL(message);
503 TFileName aScriptUtf16;
504 if (AknCommonDialogs::RunSelectDlgLD(aScriptUtf16,
505 R_MEMORY_SELECTION_DIALOG))
506 DoRunScriptL(aScriptUtf16);
509 case EPerlAppCommandOneLiner:
511 _LIT(prompt, "Oneliner:");
512 if (TextQueryDialogL(prompt, iOneLiner, KPerlAppOneLinerSize)) {
513 const TUint KPerlAppUtf8Multi = 3;
514 TBuf8<KPerlAppUtf8Multi * KPerlAppOneLinerSize> utf8;
516 CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8, iOneLiner);
517 CPerlBase* perl = CPerlBase::NewInterpreterLC();
519 char **argv = (char**) malloc(argc * sizeof(char *));
520 User::LeaveIfNull(argv);
522 TCleanupItem argvCleanupItem = TCleanupItem(free, argv);
523 CleanupStack::PushL(argvCleanupItem);
524 argv[0] = (char *) "perl";
525 argv[1] = (char *) "-le";
526 argv[2] = (char *) utf8.PtrZ();
527 perl->ParseAndRun(argc, argv);
528 CleanupStack::PopAndDestroy(2, perl);
532 case EPerlAppCommandCopyright:
534 message.Format(KCopyrightFormat);
535 InformationNoteL(message);
538 #endif // #ifndef PerlMin
540 Panic(EPerlAppCommandUnknown);
545 CPerlAppView* CPerlAppView::NewL(const TRect& aRect)
547 CPerlAppView* self = CPerlAppView::NewLC(aRect);
548 CleanupStack::Pop(self);
552 CPerlAppView* CPerlAppView::NewLC(const TRect& aRect)
554 CPerlAppView* self = new (ELeave) CPerlAppView;
555 CleanupStack::PushL(self);
556 self->ConstructL(aRect);
560 void CPerlAppView::ConstructL(const TRect& aRect)
567 void CPerlAppView::Draw(const TRect& /*aRect*/) const
569 CWindowGc& gc = SystemGc();
574 CApaDocument* CPerlAppApplication::CreateDocumentL()
576 CPerlAppDocument* document = new (ELeave) CPerlAppDocument(*this);
580 CEikAppUi* CPerlAppDocument::CreateAppUiL()
582 CPerlAppUi* appui = new (ELeave) CPerlAppUi();
589 CFileStore* CPerlAppDocument::OpenFileL(TBool /* aDoOpen */, const TDesC& aFileName, RFs& aFs)
592 STATIC_CAST(CPerlAppUi*, CEikonEnv::Static()->EikAppUi());
594 appui->OpenFileL(aFileName);
598 #endif // #ifndef PerlMin
600 EXPORT_C CApaApplication* NewApplication()
602 return new CPerlAppApplication;
605 GLDEF_C TInt E32Dll(TDllReason /*aReason*/)