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. */
7 /* This source code can be compiled into "PerlApp" which is the simple
8 * launchpad application/demonstrator, or into "PerlMin", which is the
9 * minimal Perl-on-Series-60 application. Define the cpp symbols
10 * PerlMin (a boolean), PerlMinUid (the Symbian application uid in
11 * the 0x... format), and PerlMinName (a C wide string, with the L prefix)
12 * to compile as "PerlMin". */
18 # define PerlMinUid 0x0beefadd
19 # define PerlMinName L"PerlMin"
24 # error PerlMin defined but PerlMinUid undefined
27 # error PerlMin defined but PerlMinName undefined
32 #include <aknnotewrappers.h>
42 #include <AknCommonDialogs.h>
44 #ifndef __SERIES60_1X__
45 #include <CAknFileSelectionDialog.h>
52 #include "PerlApp.hrh"
53 #include "PerlApp.rsg"
55 #endif // #ifndef PerlMin
61 const TUid KPerlAppUid = {
69 _LIT(KDefaultScript, "default.pl");
71 // This is like the Symbian _LIT() but without the embedded L prefix,
72 // which enables using #defined constants (which need to carry their
75 # define _LIT_NO_L(n, s) static const TLitC<sizeof(s)/2> n={sizeof(s)/2-1,s}
76 #endif // #ifndef _LIT_NO_L
79 _LIT_NO_L(KAppName, PerlMinName);
81 _LIT(KAppName, "PerlApp");
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
94 #endif // #ifndef PerlMin
96 typedef TBuf<256> TMessageBuffer;
97 typedef TBuf8<256> TPeekBuffer;
98 typedef TBuf8<256> TFileName8;
100 // Usage: DEBUG_PRINTF((_L("%S"), &aStr))
102 #define DEBUG_PRINTF(s) {TMessageBuffer message; message.Format s; YesNoDialogL(message);}
105 TUid CPerlAppApplication::AppDllUid() const
112 EPerlAppCommandUnknown = 1
115 void Panic(TPerlAppPanic aReason)
117 User::Panic(KAppName, aReason);
120 void CPerlAppUi::ConstructL()
123 iAppView = CPerlAppView::NewL(ClientRect());
124 AddToStackL(iAppView);
126 CEikonEnv::Static()->DisableExitChecks(ETrue); // Symbian FAQ-0577.
129 CPerlAppUi::~CPerlAppUi()
132 iEikonEnv->RemoveFromStack(iAppView);
140 if (iDoorObserver) // Otherwise the embedding application waits forever.
141 iDoorObserver->NotifyExit(MApaEmbeddedDocObserver::EEmpty);
144 static void DoRunScriptL(TFileName aScriptName);
148 static TBool DlgOk(CAknNoteDialog* dlg)
150 return dlg && dlg->RunDlgLD() == EAknSoftkeyOk;
153 static TBool OkCancelDialogL(TDesC& aMessage)
155 CAknNoteDialog* dlg =
156 new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
157 dlg->PrepareLC(R_OK_CANCEL_DIALOG);
158 dlg->SetTextL(aMessage);
162 static TBool YesNoDialogL(TDesC& aMessage)
164 CAknNoteDialog* dlg =
165 new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
166 dlg->PrepareLC(R_YES_NO_DIALOG);
167 dlg->SetTextL(aMessage);
171 static TInt InformationNoteL(TDesC& aMessage)
173 CAknInformationNote* note = new (ELeave) CAknInformationNote;
174 return note->ExecuteLD(aMessage);
177 static TInt ConfirmationNoteL(TDesC& aMessage)
179 CAknConfirmationNote* note = new (ELeave) CAknConfirmationNote;
180 return note->ExecuteLD(aMessage);
183 static TInt WarningNoteL(TDesC& aMessage)
185 CAknWarningNote* note = new (ELeave) CAknWarningNote;
186 return note->ExecuteLD(aMessage);
189 static TInt TextQueryDialogL(const TDesC& aPrompt, TDes& aData, const TInt aMaxLength)
191 CAknTextQueryDialog* dlg =
192 new (ELeave) CAknTextQueryDialog(aData);
193 dlg->SetPromptL(aPrompt);
194 dlg->SetMaxLength(aMaxLength);
195 return dlg->ExecuteLD(R_TEXT_QUERY_DIALOG);
198 // The isXXX() come from the Perl headers.
199 #define FILENAME_IS_ABSOLUTE(n) \
200 (isALPHA(((n)[0])) && ((n)[1]) == ':' && ((n)[2]) == '\\')
202 static TBool IsInPerl(TFileName aFileName)
204 TInt offset = aFileName.FindF(KScriptPrefix);
205 return ((offset == 0 && // \foo
206 aFileName[0] == '\\')
208 (offset == 2 && // x:\foo
209 FILENAME_IS_ABSOLUTE(aFileName)));
212 static TBool IsInInbox(TFileName aFileName)
214 TInt offset = aFileName.FindF(KInboxPrefix);
215 return ((offset == 0 && // \foo
216 aFileName[0] == '\\')
218 (offset == 2 && // x:\foo
219 FILENAME_IS_ABSOLUTE(aFileName)));
222 static TBool IsPerlModule(TParsePtrC aParsed)
224 return aParsed.Ext().CompareF(_L(".pm")) == 0;
227 static TBool IsPerlScript(TParsePtrC aParsed)
229 return aParsed.Ext().CompareF(_L(".pl")) == 0;
232 static void CopyFromInboxL(RFs aFs, const TFileName& aSrc, const TFileName& aDst)
234 TBool proceed = ETrue;
235 TMessageBuffer message;
237 message.Format(_L("%S is untrusted. Install only if you trust provider."), &aDst);
238 if (OkCancelDialogL(message)) {
239 message.Format(_L("Install as %S?"), &aDst);
240 if (OkCancelDialogL(message)) {
241 if (BaflUtils::FileExists(aFs, aDst)) {
242 message.Format(_L("Replace old %S?"), &aDst);
243 if (!OkCancelDialogL(message))
248 TInt err = BaflUtils::CopyFile(aFs, aSrc, aDst);
249 if (err == KErrNone) {
250 message.Format(_L("Installed %S"), &aDst);
251 ConfirmationNoteL(message);
254 message.Format(_L("Failure %d installing %S"), err, &aDst);
255 WarningNoteL(message);
262 static TBool FindPerlPackageName(TPeekBuffer aPeekBuffer, TInt aOff, TFileName& aFn)
265 TInt m = aFn.MaxLength();
266 TInt n = aPeekBuffer.Length();
271 // The following is a little regular expression
272 // engine that matches Perl package names.
273 if (j < n && isSPACE(aPeekBuffer[j])) {
274 while (j < n && isSPACE(aPeekBuffer[j])) j++;
275 if (j < n && isALPHA(aPeekBuffer[j])) {
276 while (j < n && isALNUM(aPeekBuffer[j])) {
278 isALNUM(aPeekBuffer[j]) &&
280 aFn[i++] = aPeekBuffer[j++];
282 aPeekBuffer[j ] == ':' &&
283 aPeekBuffer[j + 1] == ':' &&
288 isALPHA(aPeekBuffer[j])) {
290 isALNUM(aPeekBuffer[j]) &&
292 aFn[i++] = aPeekBuffer[j++];
296 while (j < n && isSPACE(aPeekBuffer[j])) j++;
297 if (j < n && aPeekBuffer[j] == ';' && i + 3 < m) {
299 aFn.Append(_L(".pm"));
307 static void GuessPerlModule(TFileName& aGuess, TPeekBuffer aPeekBuffer, TParse aDrive)
309 TInt offset = aPeekBuffer.Find(_L8("package"));
310 if (offset != KErrNotFound) {
311 const TInt KPackageLen = 7;
314 if (!FindPerlPackageName(aPeekBuffer, offset + KPackageLen, q))
318 p.Copy(aDrive.Drive());
319 p.Append(KModulePrefix);
322 if (p.Length() + 1 + q.Length() < aGuess.MaxLength()) {
325 for (j = 0; j < p.Length(); j++)
328 for (j = 0; j < q.Length(); j++)
337 static TBool LooksLikePerlL(TPeekBuffer aPeekBuffer)
339 return aPeekBuffer.Left(2).Compare(_L8("#!")) == 0 &&
340 aPeekBuffer.Find(_L8("perl")) != KErrNotFound;
343 static TBool InstallStuffL(const TFileName &aSrc, TParse aDrive, TParse aFile, TPeekBuffer aPeekBuffer, RFs aFs)
346 TPtrC drive = aDrive.Drive();
347 TPtrC namext = aFile.NameAndExt();
349 aDst.Format(_L("%S%S%S"), &drive, &KScriptPrefix, &namext);
350 if (!IsPerlScript(aDst) && !LooksLikePerlL(aPeekBuffer)) {
352 if (IsPerlModule(aDst))
353 GuessPerlModule(aDst, aPeekBuffer, aDrive);
355 if (aDst.Length() > 0) {
356 CopyFromInboxL(aFs, aSrc, aDst);
363 static TBool RunStuffL(const TFileName& aScriptName, TPeekBuffer aPeekBuffer)
365 TBool isModule = EFalse;
367 if (IsInPerl(aScriptName) &&
368 (IsPerlScript(aScriptName) ||
369 (isModule = IsPerlModule(aScriptName)) ||
370 LooksLikePerlL(aPeekBuffer))) {
371 TMessageBuffer message;
374 message.Format(_L("Really run module %S?"), &aScriptName);
376 message.Format(_L("Run %S?"), &aScriptName);
377 if (YesNoDialogL(message))
378 DoRunScriptL(aScriptName);
386 void CPerlAppUi::InstallOrRunL(const TFileName& aFileName)
390 TMessageBuffer message;
392 aFile.Set(aFileName, NULL, NULL);
393 if (FILENAME_IS_ABSOLUTE(aFileName)) {
394 aDrive.Set(aFileName, NULL, NULL);
397 CEikonEnv::Static()->EikAppUi()->Application()->AppFullName();
398 aDrive.Set(appName, NULL, NULL);
401 iFs = &CEikonEnv::Static()->FsSession();
403 TInt err = f.Open(*iFs, aFileName, EFileRead);
404 if (err == KErrNone) {
405 TPeekBuffer aPeekBuffer;
406 err = f.Read(aPeekBuffer);
407 f.Close(); // Release quickly.
408 if (err == KErrNone) {
409 if (!(IsInInbox(aFileName) ?
410 InstallStuffL(aFileName, aDrive, aFile, aPeekBuffer, *iFs) :
411 RunStuffL(aFileName, aPeekBuffer))) {
412 message.Format(_L("Failed for file %S"), &aFileName);
413 WarningNoteL(message);
416 message.Format(_L("Error %d reading %S"), err, &aFileName);
417 WarningNoteL(message);
420 message.Format(_L("Error %d opening %S"), err, &aFileName);
421 WarningNoteL(message);
424 delete CEikonEnv::Static()->EikAppUi();
429 #endif // #ifndef PerlMin
431 static void DoRunScriptL(TFileName aScriptName)
433 CPerlBase* perl = CPerlBase::NewInterpreterLC();
434 TRAPD(error, perl->RunScriptL(aScriptName));
436 if (error != KErrNone) {
437 TMessageBuffer message;
438 message.Format(_L("Error %d"), error);
439 YesNoDialogL(message);
442 CleanupStack::PopAndDestroy(perl);
445 void CPerlAppUi::OpenFileL(const TDesC& aFileName)
448 InstallOrRunL(aFileName);
450 DoRunScriptL(aFileName);
455 TBool CPerlAppUi::ProcessCommandParametersL(TApaCommand aCommand, TFileName& /* aDocumentName */, const TDesC8& /* aTail */)
457 if (aCommand == EApaCommandRun) {
458 TFileName appName = Application()->AppFullName();
460 p.Set(KDefaultScript, &appName, NULL);
461 DoRunScriptL(p.FullName());
464 return aCommand == EApaCommandOpen ? ETrue : EFalse;
467 void CPerlAppUi::SetFs(const RFs& aFs)
472 void CPerlAppUi::HandleCommandL(TInt aCommand)
475 TMessageBuffer message;
476 #endif // #ifndef PerlMin
481 case EAknSoftkeyExit:
485 case EPerlAppCommandAbout:
487 message.Format(KAboutFormat,
491 PERL_SYMBIANPORT_MAJOR,
492 PERL_SYMBIANPORT_MINOR,
493 PERL_SYMBIANPORT_PATCH,
495 PERL_SYMBIANSDK_MAJOR,
496 PERL_SYMBIANSDK_MINOR
498 InformationNoteL(message);
501 case EPerlAppCommandTime:
503 CPerlBase* perl = CPerlBase::NewInterpreterLC();
504 const char *const argv[] =
506 "print 'Running in ', $^O, \"\\n\", scalar localtime" };
507 perl->ParseAndRun(sizeof(argv)/sizeof(char*), (char **)argv, 0);
508 CleanupStack::PopAndDestroy(perl);
511 case EPerlAppCommandRunFile:
513 InformationNoteL(message);
514 TFileName aScriptUtf16;
515 if (AknCommonDialogs::RunSelectDlgLD(aScriptUtf16,
516 R_MEMORY_SELECTION_DIALOG))
517 DoRunScriptL(aScriptUtf16);
520 case EPerlAppCommandOneLiner:
522 _LIT(prompt, "Oneliner:");
523 if (TextQueryDialogL(prompt, iOneLiner, KPerlAppOneLinerSize)) {
524 const TUint KPerlAppUtf8Multi = 3;
525 TBuf8<KPerlAppUtf8Multi * KPerlAppOneLinerSize> utf8;
527 CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8, iOneLiner);
528 CPerlBase* perl = CPerlBase::NewInterpreterLC();
530 char **argv = (char**) malloc(argc * sizeof(char *));
531 User::LeaveIfNull(argv);
533 TCleanupItem argvCleanupItem = TCleanupItem(free, argv);
534 CleanupStack::PushL(argvCleanupItem);
535 argv[0] = (char *) "perl";
536 argv[1] = (char *) "-le";
537 argv[2] = (char *) utf8.PtrZ();
538 perl->ParseAndRun(argc, argv);
539 CleanupStack::PopAndDestroy(2, perl);
543 case EPerlAppCommandCopyright:
545 message.Format(KCopyrightFormat);
546 InformationNoteL(message);
549 #endif // #ifndef PerlMin
551 Panic(EPerlAppCommandUnknown);
556 CPerlAppView* CPerlAppView::NewL(const TRect& aRect)
558 CPerlAppView* self = CPerlAppView::NewLC(aRect);
559 CleanupStack::Pop(self);
563 CPerlAppView* CPerlAppView::NewLC(const TRect& aRect)
565 CPerlAppView* self = new (ELeave) CPerlAppView;
566 CleanupStack::PushL(self);
567 self->ConstructL(aRect);
571 void CPerlAppView::ConstructL(const TRect& aRect)
578 void CPerlAppView::Draw(const TRect& /*aRect*/) const
580 CWindowGc& gc = SystemGc();
585 CApaDocument* CPerlAppApplication::CreateDocumentL()
587 CPerlAppDocument* document = new (ELeave) CPerlAppDocument(*this);
591 CEikAppUi* CPerlAppDocument::CreateAppUiL()
593 CPerlAppUi* appui = new (ELeave) CPerlAppUi();
597 CFileStore* CPerlAppDocument::OpenFileL(TBool /* aDoOpen */, const TDesC& aFileName, RFs& aFs)
600 STATIC_CAST(CPerlAppUi*, CEikonEnv::Static()->EikAppUi());
602 appui->OpenFileL(aFileName);
606 EXPORT_C CApaApplication* NewApplication()
608 return new CPerlAppApplication;
611 GLDEF_C TInt E32Dll(TDllReason /*aReason*/)