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