summaryrefslogtreecommitdiff
path: root/symbian
diff options
context:
space:
mode:
authorJarkko Hietaniemi <jhi@iki.fi>2005-04-18 16:18:30 +0300
committerRafael Garcia-Suarez <rgarciasuarez@gmail.com>2005-04-21 15:38:30 +0000
commit27da23d53ccce622bc51822f59df8def79b4df95 (patch)
tree1202440e0fbf7a2fc1bb54993d11cda7b245f1b4 /symbian
parentec0624293b57ae07d6b2c32bae099d4f163e7e07 (diff)
downloadperl-27da23d53ccce622bc51822f59df8def79b4df95.tar.gz
Symbian port of Perl
Message-ID: <B356D8F434D20B40A8CEDAEC305A1F2453D653@esebe105.NOE.Nokia.com> p4raw-id: //depot/perl@24271
Diffstat (limited to 'symbian')
-rw-r--r--symbian/PerlApp.cpp549
-rw-r--r--symbian/PerlApp.h60
-rw-r--r--symbian/PerlApp.hrh17
-rw-r--r--symbian/PerlApp.rss141
-rw-r--r--symbian/PerlAppAif.rss21
-rw-r--r--symbian/PerlBase.cpp409
-rw-r--r--symbian/PerlBase.h118
-rw-r--r--symbian/PerlBase.pod202
-rw-r--r--symbian/PerlRecog.cpp57
-rw-r--r--symbian/PerlRecog.mmp9
-rw-r--r--symbian/README20
-rw-r--r--symbian/TODO150
-rw-r--r--symbian/bld.inf4
-rw-r--r--symbian/config.pl768
-rw-r--r--symbian/config.sh768
-rw-r--r--symbian/cwd.pl6
-rw-r--r--symbian/demo_pl128
-rw-r--r--symbian/install.cfg108
-rw-r--r--symbian/makesis.pl185
-rw-r--r--symbian/port.pl6
-rw-r--r--symbian/sanity.pl28
-rw-r--r--symbian/sdk.pl48
-rw-r--r--symbian/symbian_dll.cpp20
-rw-r--r--symbian/symbian_proto.h72
-rw-r--r--symbian/symbian_stubs.c112
-rw-r--r--symbian/symbian_stubs.h22
-rw-r--r--symbian/symbian_utils.cpp299
-rw-r--r--symbian/symbianish.h209
-rw-r--r--symbian/uid.pl1
-rw-r--r--symbian/version.pl22
-rw-r--r--symbian/xsbuild.pl861
31 files changed, 5420 insertions, 0 deletions
diff --git a/symbian/PerlApp.cpp b/symbian/PerlApp.cpp
new file mode 100644
index 0000000000..319a59118c
--- /dev/null
+++ b/symbian/PerlApp.cpp
@@ -0,0 +1,549 @@
+/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
+
+/* The PerlApp application is licensed under the same terms as Perl itself. */
+
+#include "PerlApp.h"
+
+#include <avkon.hrh>
+#include <aknnotewrappers.h>
+#include <apparc.h>
+#include <e32base.h>
+#include <e32cons.h>
+#include <eikenv.h>
+#include <bautils.h>
+#include <eikappui.h>
+#include <utf.h>
+#include <f32file.h>
+
+#include <AknCommonDialogs.h>
+
+#ifndef __SERIES60_1X__
+#include <CAknFileSelectionDialog.h>
+#endif
+
+#include <coemain.h>
+
+#include "PerlApp.hrh"
+#include "PerlApp.rsg"
+
+#include "patchlevel.h"
+#include "PerlBase.h"
+
+const TUid KPerlAppUid = { 0x102015F6 };
+
+// This is like the Symbian _LIT() but without the embedded L prefix,
+// which enables using #defined constants (which need to carry their
+// own L prefix).
+#ifndef _LIT_NO_L
+#define _LIT_NO_L(n, s) static const TLitC<sizeof(s)/2> n={sizeof(s)/2-1,s}
+#endif // #ifndef _LIT_NO_L
+
+_LIT(KAppName, "PerlApp");
+_LIT_NO_L(KFlavor, PERL_SYMBIANSDK_FLAVOR);
+_LIT(KAboutFormat,
+ "Perl %d.%d.%d, Symbian port %d.%d.%d, built for %S SDK %d.%d");
+_LIT(KCopyrightFormat,
+ "Copyright 1987-2005 Larry Wall and others, Symbian port Copyright Nokia 2004-2005");
+_LIT(KInboxPrefix, "\\System\\Mail\\");
+_LIT(KScriptPrefix, "\\Perl\\");
+
+_LIT8(KModulePrefix, SITELIB); // SITELIB from Perl config.h
+
+typedef TBuf<256> TMessageBuffer;
+typedef TBuf8<256> TPeekBuffer;
+typedef TBuf8<256> TFileName8;
+
+// Usage: DEBUG_PRINTF((_L("%S"), &aStr))
+#if 1
+#define DEBUG_PRINTF(s) {TMessageBuffer message; message.Format s; YesNoDialogL(message);}
+#endif
+
+TUid CPerlAppApplication::AppDllUid() const
+{
+ return KPerlAppUid;
+}
+
+enum TPerlAppPanic
+{
+ EPerlAppCommandUnknown = 1
+};
+
+void Panic(TPerlAppPanic aReason)
+{
+ User::Panic(KAppName, aReason);
+}
+
+void CPerlAppUi::ConstructL()
+{
+ BaseConstructL();
+ iAppView = CPerlAppView::NewL(ClientRect());
+ AddToStackL(iAppView);
+ iFs = NULL;
+ CEikonEnv::Static()->DisableExitChecks(ETrue); // Symbian FAQ-0577.
+}
+
+CPerlAppUi::~CPerlAppUi()
+{
+ if (iAppView) {
+ iEikonEnv->RemoveFromStack(iAppView);
+ delete iAppView;
+ iAppView = NULL;
+ }
+ if (iFs) {
+ delete iFs;
+ iFs = NULL;
+ }
+ if (iDoorObserver) // Otherwise the embedding application waits forever.
+ iDoorObserver->NotifyExit(MApaEmbeddedDocObserver::EEmpty);
+}
+
+static TBool DlgOk(CAknNoteDialog* dlg)
+{
+ return dlg && dlg->RunDlgLD() == EAknSoftkeyOk;
+}
+
+static TBool OkCancelDialogL(TDesC& aMessage)
+{
+ CAknNoteDialog* dlg =
+ new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
+ dlg->PrepareLC(R_OK_CANCEL_DIALOG);
+ dlg->SetTextL(aMessage);
+ return DlgOk(dlg);
+}
+
+static TBool YesNoDialogL(TDesC& aMessage)
+{
+ CAknNoteDialog* dlg =
+ new (ELeave) CAknNoteDialog(CAknNoteDialog::EConfirmationTone);
+ dlg->PrepareLC(R_YES_NO_DIALOG);
+ dlg->SetTextL(aMessage);
+ return DlgOk(dlg);
+}
+
+static TInt InformationNoteL(TDesC& aMessage)
+{
+ CAknInformationNote* note = new (ELeave) CAknInformationNote;
+ return note->ExecuteLD(aMessage);
+}
+
+static TInt ConfirmationNoteL(TDesC& aMessage)
+{
+ CAknConfirmationNote* note = new (ELeave) CAknConfirmationNote;
+ return note->ExecuteLD(aMessage);
+}
+
+static TInt WarningNoteL(TDesC& aMessage)
+{
+ CAknWarningNote* note = new (ELeave) CAknWarningNote;
+ return note->ExecuteLD(aMessage);
+}
+
+static TInt TextQueryDialogL(const TDesC& aPrompt, TDes& aData, const TInt aMaxLength)
+{
+ CAknTextQueryDialog* dlg =
+ new (ELeave) CAknTextQueryDialog(aData);
+ dlg->SetPromptL(aPrompt);
+ dlg->SetMaxLength(aMaxLength);
+ return dlg->ExecuteLD(R_TEXT_QUERY_DIALOG);
+}
+
+// The isXXX() come from the Perl headers.
+#define FILENAME_IS_ABSOLUTE(n) \
+ (isALPHA(((n)[0])) && ((n)[1]) == ':' && ((n)[2]) == '\\')
+
+static TBool IsInPerl(TFileName aFileName)
+{
+ TInt offset = aFileName.FindF(KScriptPrefix);
+ return ((offset == 0 && // \foo
+ aFileName[0] == '\\')
+ ||
+ (offset == 2 && // x:\foo
+ FILENAME_IS_ABSOLUTE(aFileName)));
+}
+
+static TBool IsInInbox(TFileName aFileName)
+{
+ TInt offset = aFileName.FindF(KInboxPrefix);
+ return ((offset == 0 && // \foo
+ aFileName[0] == '\\')
+ ||
+ (offset == 2 && // x:\foo
+ FILENAME_IS_ABSOLUTE(aFileName)));
+}
+
+static TBool IsPerlModule(TParsePtrC aParsed)
+{
+ return aParsed.Ext().CompareF(_L(".pm")) == 0;
+}
+
+static TBool IsPerlScript(TParsePtrC aParsed)
+{
+ return aParsed.Ext().CompareF(_L(".pl")) == 0;
+}
+
+static void CopyFromInboxL(RFs aFs, const TFileName& aSrc, const TFileName& aDst)
+{
+ TBool proceed = ETrue;
+ TMessageBuffer message;
+
+ message.Format(_L("%S is untrusted. Install only if you trust provider."), &aDst);
+ if (OkCancelDialogL(message)) {
+ message.Format(_L("Install as %S?"), &aDst);
+ if (OkCancelDialogL(message)) {
+ if (BaflUtils::FileExists(aFs, aDst)) {
+ message.Format(_L("Replace old %S?"), &aDst);
+ if (!OkCancelDialogL(message))
+ proceed = EFalse;
+ }
+ if (proceed) {
+ // Create directory?
+ TInt err = BaflUtils::CopyFile(aFs, aSrc, aDst);
+ if (err == KErrNone) {
+ message.Format(_L("Installed %S"), &aDst);
+ ConfirmationNoteL(message);
+ }
+ else {
+ message.Format(_L("Failure %d installing %S"), err, &aDst);
+ WarningNoteL(message);
+ }
+ }
+ }
+ }
+}
+
+static TBool FindPerlPackageName(TPeekBuffer aPeekBuffer, TInt aOff, TFileName& aFn)
+{
+ aFn.SetMax();
+ TInt m = aFn.MaxLength();
+ TInt n = aPeekBuffer.Length();
+ TInt i = 0;
+ TInt j = aOff;
+
+ aFn.SetMax();
+ // The following is a little regular expression
+ // engine that matches Perl package names.
+ if (j < n && isSPACE(aPeekBuffer[j])) {
+ while (j < n && isSPACE(aPeekBuffer[j])) j++;
+ if (j < n && isALPHA(aPeekBuffer[j])) {
+ while (j < n && isALNUM(aPeekBuffer[j])) {
+ while (j < n &&
+ isALNUM(aPeekBuffer[j]) &&
+ i < m)
+ aFn[i++] = aPeekBuffer[j++];
+ if (j + 1 < n &&
+ aPeekBuffer[j ] == ':' &&
+ aPeekBuffer[j + 1] == ':' &&
+ i < m) {
+ aFn[i++] = '\\';
+ j += 2;
+ if (j < n &&
+ isALPHA(aPeekBuffer[j])) {
+ while (j < n &&
+ isALNUM(aPeekBuffer[j]) &&
+ i < m)
+ aFn[i++] = aPeekBuffer[j++];
+ }
+ }
+ }
+ while (j < n && isSPACE(aPeekBuffer[j])) j++;
+ if (j < n && aPeekBuffer[j] == ';' && i + 3 < m) {
+ aFn.SetLength(i);
+ aFn.Append(_L(".pm"));
+ return ETrue;
+ }
+ }
+ }
+ return EFalse;
+}
+
+static void GuessPerlModule(TFileName& aGuess, TPeekBuffer aPeekBuffer, TParse aDrive)
+{
+ TInt offset = aPeekBuffer.Find(_L8("package"));
+ if (offset != KErrNotFound) {
+ const TInt KPackageLen = 7;
+ TFileName q;
+
+ if (!FindPerlPackageName(aPeekBuffer, offset + KPackageLen, q))
+ return;
+
+ TFileName8 p;
+ p.Copy(aDrive.Drive());
+ p.Append(KModulePrefix);
+
+ aGuess.SetMax();
+ if (p.Length() + 1 + q.Length() < aGuess.MaxLength()) {
+ TInt i = 0, j;
+
+ for (j = 0; j < p.Length(); j++)
+ aGuess[i++] = p[j];
+ aGuess[i++] = '\\';
+ for (j = 0; j < q.Length(); j++)
+ aGuess[i++] = q[j];
+ aGuess.SetLength(i);
+ }
+ else
+ aGuess.SetLength(0);
+ }
+}
+
+static TBool LooksLikePerlL(TPeekBuffer aPeekBuffer)
+{
+ return aPeekBuffer.Left(2).Compare(_L8("#!")) == 0 &&
+ aPeekBuffer.Find(_L8("perl")) != KErrNotFound;
+}
+
+static TBool InstallStuffL(const TFileName &aSrc, TParse aDrive, TParse aFile, TPeekBuffer aPeekBuffer, RFs aFs)
+{
+ TFileName aDst;
+ TPtrC drive = aDrive.Drive();
+ TPtrC namext = aFile.NameAndExt();
+
+ aDst.Format(_L("%S%S%S"), &drive, &KScriptPrefix, &namext);
+ if (!IsPerlScript(aDst) && !LooksLikePerlL(aPeekBuffer)) {
+ aDst.SetLength(0);
+ if (IsPerlModule(aDst))
+ GuessPerlModule(aDst, aPeekBuffer, aDrive);
+ }
+ if (aDst.Length() > 0) {
+ CopyFromInboxL(aFs, aSrc, aDst);
+ return ETrue;
+ }
+
+ return EFalse;
+}
+
+static void DoRunScriptL(TFileName aScriptName)
+{
+ CPerlBase* perl = CPerlBase::NewInterpreterLC();
+ TRAPD(error, perl->RunScriptL(aScriptName));
+ if (error != KErrNone) {
+ TMessageBuffer message;
+ message.Format(_L("Error %d"), error);
+ YesNoDialogL(message);
+ }
+ CleanupStack::PopAndDestroy(perl);
+}
+
+static TBool RunStuffL(const TFileName& aScriptName, TPeekBuffer aPeekBuffer)
+{
+ TBool isModule = EFalse;
+
+ if (IsInPerl(aScriptName) &&
+ (IsPerlScript(aScriptName) ||
+ (isModule = IsPerlModule(aScriptName)) ||
+ LooksLikePerlL(aPeekBuffer))) {
+ TMessageBuffer message;
+
+ if (isModule)
+ message.Format(_L("Really run module %S?"), &aScriptName);
+ else
+ message.Format(_L("Run %S?"), &aScriptName);
+ if (YesNoDialogL(message))
+ DoRunScriptL(aScriptName);
+
+ return ETrue;
+ }
+
+ return EFalse;
+}
+
+void CPerlAppUi::InstallOrRunL(const TFileName& aFileName)
+{
+ TParse aFile;
+ TParse aDrive;
+ TMessageBuffer message;
+
+ aFile.Set(aFileName, NULL, NULL);
+ if (FILENAME_IS_ABSOLUTE(aFileName)) {
+ aDrive.Set(aFileName, NULL, NULL);
+ } else {
+ TFileName appName =
+ CEikonEnv::Static()->EikAppUi()->Application()->AppFullName();
+ aDrive.Set(appName, NULL, NULL);
+ }
+ if (!iFs)
+ iFs = &CEikonEnv::Static()->FsSession();
+ RFile f;
+ TInt err = f.Open(*iFs, aFileName, EFileRead);
+ if (err == KErrNone) {
+ TPeekBuffer aPeekBuffer;
+ err = f.Read(aPeekBuffer);
+ f.Close(); // Release quickly.
+ if (err == KErrNone) {
+ if (!(IsInInbox(aFileName) ?
+ InstallStuffL(aFileName, aDrive, aFile, aPeekBuffer, *iFs) :
+ RunStuffL(aFileName, aPeekBuffer))) {
+ message.Format(_L("Failed for file %S"), &aFileName);
+ WarningNoteL(message);
+ }
+ } else {
+ message.Format(_L("Error %d reading %S"), err, &aFileName);
+ WarningNoteL(message);
+ }
+ } else {
+ message.Format(_L("Error %d opening %S"), err, &aFileName);
+ WarningNoteL(message);
+ }
+ if (iDoorObserver)
+ delete CEikonEnv::Static()->EikAppUi();
+ else
+ Exit();
+}
+
+void CPerlAppUi::OpenFileL(const TDesC& aFileName)
+{
+ InstallOrRunL(aFileName);
+ return;
+}
+
+TBool CPerlAppUi::ProcessCommandParametersL(TApaCommand aCommand, TFileName& /* aDocumentName */, const TDesC8& /* aTail */)
+{
+ return aCommand == EApaCommandOpen ? ETrue : EFalse;
+}
+
+void CPerlAppUi::SetFs(const RFs& aFs)
+{
+ iFs = (RFs*) &aFs;
+}
+
+void CPerlAppUi::HandleCommandL(TInt aCommand)
+{
+ TMessageBuffer message;
+
+ switch(aCommand)
+ {
+ case EEikCmdExit:
+ case EAknSoftkeyExit:
+ Exit();
+ break;
+ case EPerlAppCommandAbout:
+ {
+ message.Format(KAboutFormat,
+ PERL_REVISION,
+ PERL_VERSION,
+ PERL_SUBVERSION,
+ PERL_SYMBIANPORT_MAJOR,
+ PERL_SYMBIANPORT_MINOR,
+ PERL_SYMBIANPORT_PATCH,
+ &KFlavor,
+ PERL_SYMBIANSDK_MAJOR,
+ PERL_SYMBIANSDK_MINOR
+ );
+ InformationNoteL(message);
+ }
+ break;
+ case EPerlAppCommandTime:
+ {
+ CPerlBase* perl = CPerlBase::NewInterpreterLC();
+ const char *const argv[] =
+ { "perl", "-le",
+ "print 'Running in ', $^O, \"\\n\", scalar localtime" };
+ perl->ParseAndRun(sizeof(argv)/sizeof(char*), (char **)argv, 0);
+ CleanupStack::PopAndDestroy(perl);
+ }
+ break;
+ case EPerlAppCommandRunFile:
+ {
+ InformationNoteL(message);
+ TFileName aScriptUtf16;
+ if (AknCommonDialogs::RunSelectDlgLD(aScriptUtf16,
+ R_MEMORY_SELECTION_DIALOG))
+ DoRunScriptL(aScriptUtf16);
+ }
+ break;
+ case EPerlAppCommandOneLiner:
+ {
+ _LIT(prompt, "Oneliner:");
+ if (TextQueryDialogL(prompt, iOneLiner, KPerlAppOneLinerSize)) {
+ const TUint KPerlAppUtf8Multi = 3;
+ TBuf8<KPerlAppUtf8Multi * KPerlAppOneLinerSize> utf8;
+
+ CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8, iOneLiner);
+ CPerlBase* perl = CPerlBase::NewInterpreterLC();
+ int argc = 3;
+ char **argv = (char**) malloc(argc * sizeof(char *));
+ User::LeaveIfNull(argv);
+
+ TCleanupItem argvCleanupItem = TCleanupItem(free, argv);
+ CleanupStack::PushL(argvCleanupItem);
+ argv[0] = (char *) "perl";
+ argv[1] = (char *) "-le";
+ argv[2] = (char *) utf8.PtrZ();
+ perl->ParseAndRun(argc, argv);
+ CleanupStack::PopAndDestroy(2, perl);
+ }
+ }
+ break;
+ case EPerlAppCommandCopyright:
+ {
+ message.Format(KCopyrightFormat);
+ InformationNoteL(message);
+ }
+ break;
+
+ default:
+ Panic(EPerlAppCommandUnknown);
+ break;
+ }
+}
+
+CPerlAppView* CPerlAppView::NewL(const TRect& aRect)
+{
+ CPerlAppView* self = CPerlAppView::NewLC(aRect);
+ CleanupStack::Pop(self);
+ return self;
+}
+
+CPerlAppView* CPerlAppView::NewLC(const TRect& aRect)
+{
+ CPerlAppView* self = new (ELeave) CPerlAppView;
+ CleanupStack::PushL(self);
+ self->ConstructL(aRect);
+ return self;
+}
+
+void CPerlAppView::ConstructL(const TRect& aRect)
+{
+ CreateWindowL();
+ SetRect(aRect);
+ ActivateL();
+}
+
+void CPerlAppView::Draw(const TRect& /*aRect*/) const
+{
+ CWindowGc& gc = SystemGc();
+ TRect rect = Rect();
+ gc.Clear(rect);
+}
+
+CApaDocument* CPerlAppApplication::CreateDocumentL()
+{
+ CPerlAppDocument* document = new (ELeave) CPerlAppDocument(*this);
+ return document;
+}
+
+CEikAppUi* CPerlAppDocument::CreateAppUiL()
+{
+ CPerlAppUi* appui = new (ELeave) CPerlAppUi();
+ return appui;
+}
+
+CFileStore* CPerlAppDocument::OpenFileL(TBool /* aDoOpen */, const TDesC& aFileName, RFs& aFs)
+{
+ CPerlAppUi* appui =
+ STATIC_CAST(CPerlAppUi*, CEikonEnv::Static()->EikAppUi());
+ appui->SetFs(aFs);
+ appui->OpenFileL(aFileName);
+ return NULL;
+}
+
+EXPORT_C CApaApplication* NewApplication()
+{
+ return new CPerlAppApplication;
+}
+
+GLDEF_C TInt E32Dll(TDllReason /*aReason*/)
+{
+ return KErrNone;
+}
+
diff --git a/symbian/PerlApp.h b/symbian/PerlApp.h
new file mode 100644
index 0000000000..37a02f2502
--- /dev/null
+++ b/symbian/PerlApp.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
+
+/* The PerlApp application is licensed under the same terms as Perl itself. */
+
+#ifndef __PerlApp_h__
+#define __PerlApp_h__
+
+#include <aknapp.h>
+#include <aknappui.h>
+#include <akndoc.h>
+#include <coecntrl.h>
+#include <f32file.h>
+
+class CPerlAppDocument : public CAknDocument
+{
+ public:
+ CPerlAppDocument(CEikApplication& aApp):CAknDocument(aApp) {;}
+ CFileStore* OpenFileL(TBool aDoOpen, const TDesC& aFilename, RFs& aFs);
+ private: // from CEikDocument
+ CEikAppUi* CreateAppUiL();
+};
+
+class CPerlAppApplication : public CAknApplication
+{
+ private:
+ CApaDocument* CreateDocumentL();
+ TUid AppDllUid() const;
+};
+
+const TUint KPerlAppOneLinerSize = 80;
+
+class CPerlAppView;
+
+class CPerlAppUi : public CAknAppUi
+{
+ public:
+ void ConstructL();
+ ~CPerlAppUi();
+ void HandleCommandL(TInt aCommand);
+ void OpenFileL(const TDesC& aFileName);
+ TBool ProcessCommandParametersL(TApaCommand aCommand, TFileName& aDocumentName, const TDesC8& aTail);
+ void InstallOrRunL(const TFileName& aFileName);
+ void SetFs(const RFs& aFs);
+ private:
+ CPerlAppView* iAppView;
+ RFs* iFs;
+ TBuf<KPerlAppOneLinerSize> iOneLiner;
+};
+
+class CPerlAppView : public CCoeControl
+{
+ public:
+ static CPerlAppView* NewL(const TRect& aRect);
+ static CPerlAppView* NewLC(const TRect& aRect);
+ void Draw(const TRect& aRect) const;
+ private:
+ void ConstructL(const TRect& aRect);
+};
+
+#endif // __PerlApp_h__
diff --git a/symbian/PerlApp.hrh b/symbian/PerlApp.hrh
new file mode 100644
index 0000000000..3b0f23d79a
--- /dev/null
+++ b/symbian/PerlApp.hrh
@@ -0,0 +1,17 @@
+/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
+
+/* The PerlApp application is licensed under the same terms as Perl itself. */
+
+#ifndef __PerlApp_HRH__
+#define __PerlApp_HRH__
+
+enum TPerlIds
+{
+ EPerlAppCommandAbout = 1024, // start value must not be 0
+ EPerlAppCommandTime = 1025,
+ EPerlAppCommandRunFile = 1026,
+ EPerlAppCommandOneLiner = 1027,
+ EPerlAppCommandCopyright = 1028 // no comma here
+};
+
+#endif // __PerlApp_HRH__
diff --git a/symbian/PerlApp.rss b/symbian/PerlApp.rss
new file mode 100644
index 0000000000..c352c528db
--- /dev/null
+++ b/symbian/PerlApp.rss
@@ -0,0 +1,141 @@
+/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
+
+/* The PerlApp application is licensed under the same terms as Perl itself. */
+
+NAME PERL
+
+#include <eikon.rh>
+#include <avkon.rh>
+#include <avkon.rsg>
+
+#include "PerlApp.hrh"
+
+RESOURCE RSS_SIGNATURE
+{
+}
+
+RESOURCE TBUF r_default_document_name
+{
+ buf = "";
+}
+
+RESOURCE EIK_APP_INFO
+{
+ menubar = r_Perl_menubar;
+ cba = R_AVKON_SOFTKEYS_OPTIONS_EXIT;
+}
+
+
+RESOURCE MENU_BAR r_Perl_menubar
+{
+ titles = {
+ MENU_TITLE
+ {
+ menu_pane = r_Perl_menu;
+ }
+ };
+}
+
+
+RESOURCE MENU_PANE r_Perl_menu
+{
+ items = {
+ MENU_ITEM {
+ command = EPerlAppCommandAbout;
+ txt = "About";
+ },
+ MENU_ITEM {
+ command = EPerlAppCommandTime;
+ txt = "Time";
+ },
+ MENU_ITEM {
+ command = EPerlAppCommandRunFile;
+ txt = "Run";
+ },
+ MENU_ITEM {
+ command = EPerlAppCommandOneLiner;
+ txt = "Oneliner";
+ },
+ MENU_ITEM {
+ command = EPerlAppCommandCopyright;
+ txt = "Copyright";
+ }
+ };
+}
+
+RESOURCE DIALOG r_ok_cancel_dialog
+{
+ flags = EEikDialogFlagWait | EEikDialogFlagCbaButtons;
+ buttons = R_AVKON_SOFTKEYS_OK_CANCEL;
+ items = {
+ DLG_LINE
+ {
+ type = EAknCtNote;
+ id = EGeneralNote;
+ control = AVKON_NOTE
+ {
+ layout = EGeneralLayout;
+ };
+ }
+ };
+}
+
+RESOURCE DIALOG r_yes_no_dialog
+{
+ flags = EEikDialogFlagWait | EEikDialogFlagCbaButtons;
+ buttons = R_AVKON_SOFTKEYS_YES_NO;
+ items = {
+ DLG_LINE
+ {
+ type = EAknCtNote;
+ id = EGeneralNote;
+ control = AVKON_NOTE
+ {
+ layout = EGeneralLayout;
+ };
+ }
+ };
+}
+
+RESOURCE DIALOG r_text_query_dialog
+{
+ flags = EGeneralQueryFlags;
+ buttons = R_AVKON_SOFTKEYS_OK_CANCEL;
+ items = {
+ DLG_LINE
+ {
+ type = EAknCtQuery;
+ id = EGeneralQuery;
+ control = AVKON_DATA_QUERY
+ {
+ layout = EDataLayout;
+ control = EDWIN {};
+ };
+ }
+ };
+}
+
+RESOURCE AVKON_LIST_QUERY r_list_query_dialog
+{
+ flags = EGeneralQueryFlags;
+ softkeys = R_AVKON_SOFTKEYS_OK_CANCEL;
+ items = {
+ DLG_LINE
+ {
+ type = EAknCtListQueryControl;
+ id = EListQueryControl;
+ control = AVKON_LIST_QUERY_CONTROL
+ {
+ listtype = EAknCtSinglePopupMenuListBox;
+ };
+ }
+ };
+}
+
+#include <CommonDialogs.hrh>
+#include <CommonDialogs.rh>
+
+RESOURCE MEMORYSELECTIONDIALOG r_memory_selection_dialog
+{
+}
+
diff --git a/symbian/PerlAppAif.rss b/symbian/PerlAppAif.rss
new file mode 100644
index 0000000000..fa4d42b0e1
--- /dev/null
+++ b/symbian/PerlAppAif.rss
@@ -0,0 +1,21 @@
+/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
+
+/* The PerlApp application is licensed under the same terms as Perl itself. */
+
+#include <aiftool.rh>
+
+RESOURCE AIF_DATA
+{
+ app_uid = 0x102015F6;
+ embeddability = KAppEmbeddable;
+ hidden = KAppNotHidden;
+ launch = KAppLaunchInForeground;
+ newfile = KAppDoesNotSupportNewFile;
+ datatype_list = {
+ DATATYPE
+ {
+ priority = EDataTypePriorityNormal;
+ type = "x-application/x-perl";
+ }
+ };
+ }
diff --git a/symbian/PerlBase.cpp b/symbian/PerlBase.cpp
new file mode 100644
index 0000000000..31fe012f80
--- /dev/null
+++ b/symbian/PerlBase.cpp
@@ -0,0 +1,409 @@
+/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
+
+/* The CPerlBase class is licensed under the same terms as Perl itself. */
+
+/* See PerlBase.pod for documentation. */
+
+#define PERLBASE_CPP
+
+#include <e32cons.h>
+#include <e32keys.h>
+#include <utf.h>
+
+#include "PerlBase.h"
+
+const TUint KPerlConsoleBufferMaxTChars = 0x0200;
+const TUint KPerlConsoleNoPos = 0xffff;
+
+CPerlBase::CPerlBase()
+{
+}
+
+EXPORT_C void CPerlBase::Destruct()
+{
+ iState = EPerlDestroying;
+ if (iConsole) {
+ iConsole->Printf(_L("[Any key to continue]"));
+ iConsole->Getch();
+ }
+ if (iPerl) {
+ (void)perl_destruct(iPerl);
+ perl_free(iPerl);
+ iPerl = NULL;
+ PERL_SYS_TERM();
+ }
+ if (iConsole) {
+ delete iConsole;
+ iConsole = NULL;
+ }
+ if (iConsoleBuffer) {
+ free(iConsoleBuffer);
+ iConsoleBuffer = NULL;
+ }
+#ifdef PERL_GLOBAL_STRUCT
+ if (iVars) {
+ PerlInterpreter* my_perl = NULL;
+ free_global_struct(iVars);
+ iVars = NULL;
+ }
+#endif
+}
+
+CPerlBase::~CPerlBase()
+{
+ Destruct();
+}
+
+EXPORT_C CPerlBase* CPerlBase::NewInterpreterL(TBool aCloseStdlib,
+ void (*aStdioInitFunc)(void*),
+ void *aStdioInitCookie)
+{
+ CPerlBase* self =
+ CPerlBase::NewInterpreterLC(aCloseStdlib,
+ aStdioInitFunc,
+ aStdioInitCookie);
+ CleanupStack::Pop(self);
+ return self;
+}
+
+EXPORT_C CPerlBase* CPerlBase::NewInterpreterLC(TBool aCloseStdlib,
+ void (*aStdioInitFunc)(void*),
+ void *aStdioInitCookie)
+{
+ CPerlBase* self = new (ELeave) CPerlBase;
+ CleanupStack::PushL(self);
+ self->iCloseStdlib = aCloseStdlib;
+ self->iStdioInitFunc = aStdioInitFunc;
+ self->iStdioInitCookie = aStdioInitCookie;
+ self->ConstructL();
+ PERL_APPCTX_SET(self);
+ return self;
+}
+
+static int _console_stdin(void* cookie, char* buf, int n)
+{
+ return ((CPerlBase*)cookie)->ConsoleRead(0, buf, n);
+}
+
+static int _console_stdout(void* cookie, const char* buf, int n)
+{
+ return ((CPerlBase*)cookie)->ConsoleWrite(1, buf, n);
+}
+
+static int _console_stderr(void* cookie, const char* buf, int n)
+{
+ return ((CPerlBase*)cookie)->ConsoleWrite(2, buf, n);
+}
+
+void CPerlBase::StdioRewire(void *arg) {
+ _REENT->_sf[0]._cookie = (void*)this;
+ _REENT->_sf[0]._read = &_console_stdin;
+ _REENT->_sf[0]._write = 0;
+ _REENT->_sf[0]._seek = 0;
+ _REENT->_sf[0]._close = 0;
+
+ _REENT->_sf[1]._cookie = (void*)this;
+ _REENT->_sf[1]._read = 0;
+ _REENT->_sf[1]._write = &_console_stdout;
+ _REENT->_sf[1]._seek = 0;
+ _REENT->_sf[1]._close = 0;
+
+ _REENT->_sf[2]._cookie = (void*)this;
+ _REENT->_sf[2]._read = 0;
+ _REENT->_sf[2]._write = &_console_stderr;
+ _REENT->_sf[2]._seek = 0;
+ _REENT->_sf[2]._close = 0;
+}
+
+void CPerlBase::ConstructL()
+{
+ iState = EPerlNone;
+#ifdef PERL_GLOBAL_STRUCT
+ PerlInterpreter *my_perl = 0;
+ iVars = init_global_struct();
+ User::LeaveIfNull(iVars);
+#endif
+ iPerl = perl_alloc();
+ User::LeaveIfNull(iPerl);
+ iState = EPerlAllocated;
+ perl_construct(iPerl); // returns void
+ if (!iStdioInitFunc) {
+ iConsole =
+ Console::NewL(_L("Perl Console"),
+ TSize(KConsFullScreen, KConsFullScreen));
+ iConsoleBuffer =
+ (TUint16*)malloc(sizeof(TUint) *
+ KPerlConsoleBufferMaxTChars);
+ User::LeaveIfNull(iConsoleBuffer);
+ iConsoleUsed = 0;
+#ifndef USE_PERLIO
+ iStdioInitFunc = &StdioRewire;
+#endif
+ }
+ if (iStdioInitFunc)
+ iStdioInitFunc(iStdioInitCookie);
+ iReadFunc = NULL;
+ iWriteFunc = NULL;
+ iState = EPerlConstructed;
+}
+
+EXPORT_C PerlInterpreter* CPerlBase::GetInterpreter()
+{
+ return (PerlInterpreter*) iPerl;
+}
+
+#ifdef PERL_MINIPERL
+static void boot_DynaLoader(pTHX_ CV* cv) { }
+#else
+EXTERN_C void boot_DynaLoader(pTHX_ CV* cv);
+#endif
+
+static void xs_init(pTHX)
+{
+ dXSUB_SYS;
+ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
+}
+
+EXPORT_C TInt CPerlBase::RunScriptL(const TDesC& aFileName,
+ int argc,
+ char **argv,
+ char *envp[]) {
+ TBuf8<KMaxFileName> scriptUtf8;
+ TInt error;
+ error = CnvUtfConverter::ConvertFromUnicodeToUtf8(scriptUtf8, aFileName);
+ User::LeaveIfError(error);
+ char *filename = (char*)scriptUtf8.PtrZ();
+ struct stat st;
+ if (stat(filename, &st) == -1)
+ return KErrNotFound;
+ if (argc < 2)
+ return KErrGeneral; /* Anything better? */
+ char **Argv = (char**)malloc(argc * sizeof(char*));
+ User::LeaveIfNull(Argv);
+ TCleanupItem ArgvCleanupItem = TCleanupItem(free, Argv);
+ CleanupStack::PushL(ArgvCleanupItem);
+ Argv[0] = "perl";
+ if (argv && argc > 2)
+ for (int i = 2; i < argc - 1; i++)
+ Argv[i] = argv[i];
+ Argv[argc - 1] = filename;
+ error = this->ParseAndRun(argc, Argv, envp);
+ CleanupStack::PopAndDestroy(Argv);
+ Argv = 0;
+ return error == 0 ? KErrNone : KErrGeneral;
+}
+
+
+EXPORT_C int CPerlBase::Parse(int argc, char *argv[], char *envp[])
+{
+ if (iState == EPerlConstructed) {
+ const char* const NullArgv[] = { "perl", "-e", "0" };
+ if (argc == 0 || argv == 0) {
+ argc = 3;
+ argv = (char**) NullArgv;
+ }
+ PERL_SYS_INIT(&argc, &argv);
+ int parsed = perl_parse(iPerl, xs_init, argc, argv, envp);
+ if (parsed == 0)
+ iState = EPerlParsed;
+ return parsed;
+ } else
+ return -1;
+}
+
+EXPORT_C void CPerlBase::SetupExit()
+{
+ if (iState == EPerlParsed) {
+ diTHX;
+ PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
+ // PL_perl_destruct level of 2 would be nice but
+ // it causes "Unbalanced scopes" for some reason.
+ PL_perl_destruct_level = 1;
+ }
+}
+
+EXPORT_C int CPerlBase::Run()
+{
+ if (iState == EPerlParsed) {
+ SetupExit();
+ iState = EPerlRunning;
+ int ran = perl_run(iPerl);
+ iState = (ran == 0) ? EPerlSuccess : EPerlFailure;
+ return ran;
+ } else
+ return -1;
+}
+
+EXPORT_C int CPerlBase::ParseAndRun(int argc, char *argv[], char *envp[])
+{
+ int parsed = Parse(argc, argv, envp);
+ int ran = (parsed == 0) ? Run() : -1;
+ return ran;
+}
+
+int CPerlBase::ConsoleReadLine()
+{
+ if (!iConsole)
+ return -EIO;
+
+ TUint currX = KPerlConsoleNoPos;
+ TUint currY = KPerlConsoleNoPos;
+ TUint prevX = KPerlConsoleNoPos;
+ TUint prevY = KPerlConsoleNoPos;
+ TUint maxX = KPerlConsoleNoPos;
+ TUint offset = 0;
+
+ for (;;) {
+ TKeyCode code = iConsole->Getch();
+
+ if (code == EKeyLineFeed || code == EKeyEnter) {
+ if (offset < KPerlConsoleBufferMaxTChars) {
+ iConsoleBuffer[offset++] = '\n';
+ iConsole->Printf(_L("\n"));
+ iConsoleBuffer[offset++] = 0;
+ }
+ break;
+ }
+ else {
+ TBool doBackward = EFalse;
+ TBool doBackspace = EFalse;
+
+ prevX = currX;
+ prevY = currY;
+ if (code == EKeyBackspace) {
+ if (offset > 0) {
+ iConsoleBuffer[--offset] = 0;
+ doBackward = ETrue;
+ doBackspace = ETrue;
+ }
+ }
+ else if (offset < KPerlConsoleBufferMaxTChars) {
+ TChar ch = TChar(code);
+
+ if (ch.IsPrint()) {
+ iConsoleBuffer[offset++] = (unsigned short)code;
+ iConsole->Printf(_L("%c"), code);
+ }
+ }
+ currX = iConsole->WhereX();
+ currY = iConsole->WhereY();
+ if (maxX == KPerlConsoleNoPos && prevX != KPerlConsoleNoPos &&
+ prevY != KPerlConsoleNoPos && currY == prevY + 1)
+ maxX = prevX;
+ if (doBackward) {
+ if (currX > 0)
+ iConsole->SetPos(currX - 1);
+ else if (currY > 0)
+ iConsole->SetPos(maxX, currY - 1);
+ if (doBackspace) {
+ TUint nowX = iConsole->WhereX();
+ TUint nowY = iConsole->WhereY();
+ iConsole->Printf(_L(" ")); /* scrub */
+ iConsole->SetPos(nowX, nowY);
+ }
+ }
+ }
+ }
+
+ return offset;
+}
+
+int CPerlBase::ConsoleRead(const int fd, char* buf, int n)
+{
+ if (iReadFunc)
+ return iReadFunc(fd, buf, n);
+
+ if (!iConsole) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (n < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (n == 0)
+ return 0;
+
+ TBuf8<4 * KPerlConsoleBufferMaxTChars> aBufferUtf8;
+ TBuf16<KPerlConsoleBufferMaxTChars> aBufferUtf16;
+ int length = ConsoleReadLine();
+ int i;
+
+ iConsoleUsed += length;
+
+ aBufferUtf16.SetLength(length);
+ for (i = 0; i < length; i++)
+ aBufferUtf16[i] = iConsoleBuffer[i];
+ aBufferUtf8.SetLength(4 * length);
+
+ CnvUtfConverter::ConvertFromUnicodeToUtf8(aBufferUtf8, aBufferUtf16);
+
+ char *pUtf8 = (char*)aBufferUtf8.PtrZ();
+ int nUtf8 = aBufferUtf8.Size();
+ if (nUtf8 > n)
+ nUtf8 = n; /* Potential data loss. */
+#ifdef PERL_SYMBIAN_CONSOLE_UTF8
+ for (i = 0; i < nUtf8; i++)
+ buf[i] = pUtf8[i];
+#else
+ dTHX;
+ for (i = 0; i < nUtf8; i+= UTF8SKIP(pUtf8 + i)) {
+ unsigned long u = utf8_to_uvchr((U8*)(pUtf8 + i), 0);
+ if (u > 0xFF) {
+ iConsole->Printf(_L("(keycode > 0xFF)\n"));
+ buf[i] = 0;
+ return -1;
+ }
+ buf[i] = u;
+ }
+#endif
+ if (nUtf8 < n)
+ buf[nUtf8] = 0;
+ return nUtf8;
+}
+
+int CPerlBase::ConsoleWrite(const int fd, const char* buf, int n)
+{
+ if (iWriteFunc)
+ return iWriteFunc(fd, buf, n);
+
+ if (!iConsole) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (n < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (n == 0)
+ return 0;
+
+ int wrote = 0;
+#ifdef PERL_SYMBIAN_CONSOLE_UTF8
+ dTHX;
+ if (is_utf8_string((U8*)buf, n)) {
+ for (int i = 0; i < n; i += UTF8SKIP(buf + i)) {
+ TChar u = utf8_to_uvchr((U8*)(buf + i), 0);
+ iConsole->Printf(_L("%c"), u);
+ wrote++;
+ }
+ } else {
+ iConsole->Printf(_L("(malformed utf8: "));
+ for (int i = 0; i < n; i++)
+ iConsole->Printf(_L("%02x "), buf[i]);
+ iConsole->Printf(_L(")\n"));
+ }
+#else
+ for (int i = 0; i < n; i++) {
+ iConsole->Printf(_L("%c"), buf[i]);
+ }
+ wrote = n;
+#endif
+ iConsoleUsed += wrote;
+ return n;
+}
+
diff --git a/symbian/PerlBase.h b/symbian/PerlBase.h
new file mode 100644
index 0000000000..f6765fbed2
--- /dev/null
+++ b/symbian/PerlBase.h
@@ -0,0 +1,118 @@
+/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
+
+/* The CPerlBase class is licensed under the same terms as Perl itself. */
+
+/* See PerlBase.pod for documentation. */
+
+#ifndef __PerlBase_h__
+#define __PerlBase_h__
+
+#include <e32base.h>
+
+#if !defined(PERL_MINIPERL) && !defined(PERL_PERL)
+# ifndef PERL_IMPLICIT_CONTEXT
+# define PERL_IMPLICIT_CONTEXT
+# endif
+# ifndef PERL_MULTIPLICITY
+# define PERL_MULTIPLICITY
+# endif
+# ifndef PERL_GLOBAL_STRUCT
+# define PERL_GLOBAL_STRUCT
+# endif
+# ifndef PERL_GLOBAL_STRUCT_PRIVATE
+# define PERL_GLOBAL_STRUCT_PRIVATE
+# endif
+#endif
+
+#include "EXTERN.h"
+#include "perl.h"
+
+typedef enum {
+ EPerlNone,
+ EPerlAllocated,
+ EPerlConstructed,
+ EPerlParsed,
+ EPerlRunning,
+ EPerlTerminated,
+ EPerlPaused,
+ EPerlSuccess,
+ EPerlFailure,
+ EPerlDestroying
+} TPerlState;
+
+class PerlConsole;
+
+class CPerlBase : public CBase
+{
+ public:
+ CPerlBase();
+ IMPORT_C virtual ~CPerlBase();
+ IMPORT_C static CPerlBase* NewInterpreterL(TBool iCloseStdlib = ETrue,
+ void (*aStdioInitFunc)(void*) = NULL,
+ void *aStdioInitCookie = NULL);
+ IMPORT_C static CPerlBase* NewInterpreterLC(TBool iCloseStdlib = ETrue,
+ void (*aStdioInitFunc)(void*) = NULL,
+ void *aStdioInitCookie = NULL);
+ IMPORT_C TInt RunScriptL(const TDesC& aFileName, int argc = 2, char **argv = NULL, char *envp[] = NULL);
+ IMPORT_C int Parse(int argc = 0, char *argv[] = NULL, char *envp[] = NULL);
+ IMPORT_C void SetupExit();
+ IMPORT_C int Run();
+ IMPORT_C int ParseAndRun(int argc = 0, char *argv[] = 0, char *envp[] = 0);
+ IMPORT_C void Destruct();
+
+ IMPORT_C PerlInterpreter* GetInterpreter();
+
+ // These two really should be private but when not using PERLIO
+ // certain C callback functions of STDLIB need to be able to call
+ // these. In general, all the console related functionality is
+ // intentionally hidden and underdocumented.
+ int ConsoleRead(const int fd, char* buf, int n);
+ int ConsoleWrite(const int fd, const char* buf, int n);
+
+ // Having these public does not feel right, but maybe someone needs
+ // to do creative things with them.
+ int (*iReadFunc)(const int fd, char *buf, int n);
+ int (*iWriteFunc)(const int fd, const char *buf, int n);
+
+ protected:
+ PerlInterpreter* iPerl;
+#ifdef PERL_GLOBAL_STRUCT
+ struct perl_vars* iVars;
+#else
+ void* iAppCtx;
+#endif
+ TPerlState iState;
+
+ private:
+
+ void ConstructL();
+ CConsoleBase* iConsole; /* The screen. */
+ TUint16* iConsoleBuffer; /* The UTF-16 characters. */
+ TUint iConsoleUsed; /* How many in iConsoleBuffer. */
+ TBool iCloseStdlib; /* Close STDLIB on exit? */
+
+ void (*iStdioInitFunc)(void *);
+ void* iStdioInitCookie;
+
+ int ConsoleReadLine();
+ void StdioRewire(void*);
+};
+
+#define diTHX PerlInterpreter* my_perl = iPerl
+#define diVAR struct perl_vars* my_vars = iVars
+
+#ifdef PERL_GLOBAL_STRUCT
+# define PERL_APPCTX_SET(c) ((c)->iVars->Gappctx = (c))
+#else
+# define PERL_APPCTX_SET(c) (PL_appctx = (c))
+#endif
+
+#undef Copy
+#undef CopyD /* For symmetry, not for Symbian reasons. */
+#undef New
+#define PerlCopy(s,d,n,t) (MEM_WRAP_CHECK(n,t), (void)memcpy((char*)(d),(char*)(s), (n) * sizeof(t)))
+#define PerlCopyD(s,d,n,t) (MEM_WRAP_CHECK(n,t), memcpy((char*)(d),(char*)(s), (n) * sizeof(t)))
+#define PerlNew(x,v,n,t) (v = (MEM_WRAP_CHECK(n,t), (t*)safemalloc((MEM_SIZE)((n)*sizeof(t)))))
+
+#endif /* #ifndef __PerlBase_h__ */
+
diff --git a/symbian/PerlBase.pod b/symbian/PerlBase.pod
new file mode 100644
index 0000000000..265e2d6d8b
--- /dev/null
+++ b/symbian/PerlBase.pod
@@ -0,0 +1,202 @@
+=head1 NAME
+
+CPerlBase - a base class encapsulating a Perl interpreter
+
+=head1 SYNOPSIS
+
+ // in your App.mmp
+ USERINCLUDE \symbian\perl\x.y.z\include
+ LIBRARY perlXYZ.lib
+
+ // in your App
+ #include "PerlBase.h" // includes also EXTERN.h and perl.h
+ CPerlBase* perl = CPerlBase::NewInterpreterLC();
+ ...
+ delete perl;
+
+=head1 DESCRIPTION
+
+CPerlBase is a simple Symbian C++ class that wraps a Perl
+interpreter; its creation, use, and destroying. To understand
+what this is doing, and how to use the interpreter, a fair knowledge
+of L<perlapi>, L<perlguts>, and L<perlembed> is recommended.
+
+One useful thing CPerlBase does compared with just using the raw
+Perl C API is that it redirects the "std streams" (STDOUT et alia)
+to a text console implementation which while being very basic
+is marginally more usable than the Symbian basic text console.
+
+=head2 The Basics
+
+=over 4
+
+=item *
+
+CPerlBase* NewInterpreterL();
+
+The constructor that does not keep the object in the Symbian "cleanup stack".
+perl_alloc() and perl_construct() are called behind the curtains.
+
+Accepts the same arguments as NewInterpreterLC().
+
+=item *
+
+CPerlBase* NewInterpreterLC();
+
+The constructor that keeps the object in the Symbian "cleanup stack".
+perl_alloc() and perl_construct() are called behind the curtains.
+
+Can have three arguments:
+
+=over 8
+
+=item *
+
+TBool aCloseStdlib = ETrue
+
+Should a CPerlBase close the Symbian POSIX STDLIB when closing down.
+Good for one-shot script execution, probably less good for longer term
+embedded interpreter.
+
+=item *
+
+void (*aStdioInitFunc)(void*) = NULL
+
+If set, called with aStdioInitCookie, and the default console is
+not created. You may want to set the iReadFunc() and iWriteFunc().
+
+=item *
+
+void *aStdioInitCookie = NULL
+
+Used as the argument for aStdioInitFunc().
+
+=back
+
+=item *
+
+void Destroy();
+
+The destructor of the interpreter. The class destructor calls
+first this and then the Symbian CloseSTDLIB().
+
+perl_destruct(), perl_free(), and PERL_SYS_TERM() are called
+behind the curtains.
+
+=back
+
+=head2 Utility functions
+
+=over 4
+
+=item *
+
+int Parse(int argc = 0, char *argv[] = 0, char *envp[] = 0);
+
+Prepare an interpreter for executing by parsing input as if a C main()
+had been called. For example to parse a script, use argc of 2 and argv
+of { "perl", script_name }.
+
+All arguments are optional: in case either argc or argv are zero,
+argc of 3 and argv of { "perl", "-e", "0" } is assumed.
+
+PERL_SYS_INIT() and perl_parse() are called behind the curtains.
+
+Note that a call to Parse() is required before Run().
+
+Returns zero if parsing was successful, non-zero if not (and the stderr
+will get the error).
+
+=item *
+
+int Run()
+
+Start executing an interpeter. A Parse() must have been called before
+a Run(): use 3 and { "", "-e", 0 } if you do not have an argv.
+
+Note that a call to Parse() is required before Run().
+
+perl_run() is called behind the curtains.
+
+Returns zero if execution was successful, non-zero if not (and the stderr
+will get the error).
+
+=item *
+
+int ParseAndRun(int argc, char *argv[], char *envp[]);
+
+Combined Parse() and Run(). The Run() is not run if the Parse() fails.
+
+Returns zero if parsing and execution were successful, non-zero if not.
+
+=item *
+
+TInt RunScriptL(TDesC& aFileName, int argc, char **argv, char *envp[])
+
+Like ParseAndRun() but works for Symbian filenames (UTF-16LE).
+The UTF-8 version of aFileName is always argv[argc-1], and argv[0]
+is always "perl".
+
+=head2 Macros
+
+=over 4
+
+=item *
+
+diTHX
+
+Set up my_perl from the current object (like dTHX).
+
+=item *
+
+diVAR
+
+Set up my_vars from the current object (like dVAR).
+
+=back
+
+=head2 Extending CPerlBase (subclassing, deriving from)
+
+Note that it probably isn't worth the trouble to try to wrap the
+whole, rather large, Perl C API into a C++ API. Just use the C API.
+
+The protected members of the class are:
+
+=over 4
+
+=item *
+
+PerlInterpreter* iPerl
+
+The Perl interpreter.
+
+=item *
+
+struct perl_vars* iVars
+
+The global variables of the interpreter.
+
+=item *
+
+TPerlState iState
+
+The state of the Perl interpreter. TPerlState is one of EPerlNone,
+EPerlAllocated, EPerlConstructed, EPerlParsed, EPerlRunning,
+EPerlTerminated, EPerlPaused (these two are currently unused
+but in the future they might be used to indicate that the interpreter
+was stopped either non-resumably or resumably for some reason),
+EPerlSuccess (perl_run() succeeded), EPerlFailure (perl_run() failed),
+EPerlDestroying.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (c) 2004-2005 Nokia. All rights reserved.
+
+=head1 LICENSE
+
+The CPerlBase class is licensed under the same terms as Perl itself.
+
+=cut
+
diff --git a/symbian/PerlRecog.cpp b/symbian/PerlRecog.cpp
new file mode 100644
index 0000000000..d2db54491b
--- /dev/null
+++ b/symbian/PerlRecog.cpp
@@ -0,0 +1,57 @@
+/* Copyright (c) 2004-2005 Nokia. All rights reserved. */
+
+/* The PerlRecog application is licensed under the same terms as Perl itself. */
+
+#include <apmrec.h>
+#include <apmstd.h>
+#include <f32file.h>
+
+const TUid KUidPerlRecog = { 0x102015F7 };
+_LIT8(KPerlMimeType, "x-application/x-perl");
+_LIT8(KPerlSig, "#!/usr/bin/perl");
+const TInt KPerlSigLen = 15;
+
+class CApaPerlRecognizer : public CApaDataRecognizerType {
+ public:
+ CApaPerlRecognizer():CApaDataRecognizerType(KUidPerlRecog, EHigh) {
+ iCountDataTypes = 1;
+ }
+ virtual TUint PreferredBufSize() { return KPerlSigLen; }
+ virtual TDataType SupportedDataTypeL(TInt /* aIndex */) const {
+ return TDataType(KPerlMimeType);
+ }
+ private:
+ virtual void DoRecognizeL(const TDesC& aName, const TDesC8& aBuffer);
+};
+
+void CApaPerlRecognizer::DoRecognizeL(const TDesC& aName, const TDesC8& aBuffer)
+{
+ iConfidence = ENotRecognized;
+
+ if (aBuffer.Length() >= KPerlSigLen &&
+ aBuffer.Left(KPerlSigLen).Compare(KPerlSig) == 0) {
+ iConfidence = ECertain;
+ iDataType = TDataType(KPerlMimeType);
+ } else {
+ TParsePtrC p(aName);
+
+ if ((p.Ext().CompareF(_L(".pl")) == 0) ||
+ (p.Ext().CompareF(_L(".pm")) == 0)) {
+ iConfidence = ECertain;
+ iDataType = TDataType(KPerlMimeType);
+ }
+ }
+}
+
+EXPORT_C CApaDataRecognizerType* CreateRecognizer()
+{
+ return new CApaPerlRecognizer;
+}
+
+GLDEF_C TInt E32Dll(TDllReason /* aReason */)
+{
+ return KErrNone;
+}
+
+
+
diff --git a/symbian/PerlRecog.mmp b/symbian/PerlRecog.mmp
new file mode 100644
index 0000000000..6850103b5b
--- /dev/null
+++ b/symbian/PerlRecog.mmp
@@ -0,0 +1,9 @@
+TARGET PerlRecog.mdl
+TARGETTYPE mdl
+UID 0x10003A19 0x102015F7
+TARGETPATH \system\recogs
+SOURCE PerlRecog.cpp
+USERINCLUDE .
+SYSTEMINCLUDE \epoc32\include
+LIBRARY euser.lib efsrv.lib apmime.lib
+
diff --git a/symbian/README b/symbian/README
new file mode 100644
index 0000000000..95ed303851
--- /dev/null
+++ b/symbian/README
@@ -0,0 +1,20 @@
+The PerlApp* files are a demonstration application for the CPerlBase
+class, which is defined and implemented by the PerlBase* files.
+The rest of the files are part of the Symbian base port.
+
+All files are Copyright (c) Nokia, 2004-2005, all rights reserved,
+and licensed under the same terms as Perl itself.
+
+Once the 'sdkinstall' make target has been run in the top level,
+the PerlApp can be built using the standard Symbian way:
+
+ bldmake bldfiles
+ abld build wins udeb
+ abld build thumb urel
+
+and then packaged into a SIS by:
+
+ makesis PerlApp.pkg
+
+--
+
diff --git a/symbian/TODO b/symbian/TODO
new file mode 100644
index 0000000000..78dcd24630
--- /dev/null
+++ b/symbian/TODO
@@ -0,0 +1,150 @@
+=head1 BASE PORT
+
+=head2 Console
+
+- The Console only does "ASCII" input: e.g. pressing the "2"
+ key five times, "aaaaa", does not produce "ä" ("a diaeresis"),
+ but instead the "2" key rotates through "abc2abc2...".
+ This is a pity because the Console is actually capable of full
+ Unicode input and output (if you have the fonts, that is). You
+ can verify this by entering e.g. the euro character, which is
+ U+20AC, well beyond U+00FF. I don't know why the full repertoire
+ of the keyboard is not available.
+- Enhance the console? (line editing, full x-y movement, history)
+- The role of the console needs to be rethought: the best way
+ would be to have the console visible in the same screen as
+ the GUI elements (an "embedded console"?)
+
+=head2 Core Language
+
+- the $^E does not work
+- select() does not work (not our fault)
+- starting external application: what now (0.1.0) works is:
+ - system("app");
+ - system("app&");
+ - and those with arguments:
+ - system("app arg1 arg2")
+ - system("app arg1 arg2 &")
+ but remember that a Symbian process does get only argv[0]
+ and argv[1]: all the arguments of the application are passed
+ in as a single argument ("arg1 arg2" in the above)
+ What does not work:
+ - piped open, in either direction
+ - qx/backtick/`
+ - fork/wait (these unlikely to ever work as in POSIX)
+ - IO redirection or filename globbing in system()
+ (since there is no POSIX shell beneath)
+ What might work in future:
+ - exec() might be made to work
+ - Symbian::spawn("cmd args") returning a process id (what does Win32 do?)
+ - Symbian::waitpid($spawned_pid)
+
+=head2 Platform
+
+- in S60 1.2 (at least in 3650 Nokia 3650 v3.11) setjmp/longjmp is
+ fragile (see Symbian FAQ-0929), intensive debugging and fix needed
+- in S60 2.x (at least in Nokia 6630 v4.03.11) launching scripts via
+ FExplorer does not open up the console
+
+=head2 Unicode
+
+- Symbian has Unicode filenames, and Unicode all over the place.
+- Encode and the use of Symbian Unicode in general
+ tie into the overall usefulness of PerlIO.
+
+=head2 Portability
+
+- Slash versus Backslash: where does one need to use "\\"?
+ writing Perl applications, where can one get away with using "/" ?
+
+=head2 Build
+
+- make xsbuild.pl much more robust (for building external extensions)
+- MakeMaker? Pure PM, PM + XS?
+- currently the PerlApp UID is in both config.pl (hardwired) and
+ in makesis.pl (computed), this is quite error prone
+- Enable building also under Cygwin?
+
+=head1 PACKAGING
+
+- subdivide perlext.sis?
+- pm-stripper: strip pod and comments, while inserting the appropriate
+ #line commands to keep linenumbers in sync. Shaves off easily 50%
+ of the code, making install packages smaller.
+- Get MakeMaker to create SIS packages? In non-Win32?
+- Symbian has APIs for opening .zip files
+- Investigate Autrijus Tang's PAR format
+ http://www.autrijus.org/par-intro/
+- "makeplsis" to wrap a script.pl or dir/script.pl as a stand-alone
+ application (and SIS): unshift the "application home" to @INC and
+ chdir to that, then run the script.pl (renamed as default.pl)
+
+=head1 PerlBase
+
+- review for proper Symbian coding practices
+
+=head1 PerlApp
+
+- In "Run" see how one could show also the file extensions.
+- when autostarting also offer to display the file (via Notes?)
+ instead of installing/running it?
+- Allow passing command line options to scripts being run?
+- Add "OneLiner" menu item? (-e, -M) (requires a UI form)
+- Terminate/Pause menu entries?
+- review for proper Symbian coding practices
+
+=head1 CORE LIBRARIES
+
+- Fix Devel::PPPort (worth it?) (Note that there is D::PPP 3.x out by now)
+- Fix Encode to not to have writeable data: seems to be tricky indeed
+ because of copious global non-const data.
+- Verify that the modified File::Spec::Win32 does work in Symbian.
+ (File::Spec::Epoc does not seem to be relevant?)
+- What does Cwd really do since the concept of cwd is a bit fuzzy in Symbian.
+- What should Sys::Hostname return? GPRS? BT? WLAN?
+- ByteLoader problem: byterun.c does not see VERSION and XS_VERSION.
+- POSIX problem: STDLIB POSIX is not that POSIX.
+
+=head1 REGRESSION SUITE
+
+- how to run the standard test suite on a Symbian device?
+
+=head1 CPAN LIBRARIES
+
+- Include/Package more modules (or work harder on getting CPAN.pm working?)
+ (but note that lib/**/*.pm is 3.5 megabytes, probably not worth including
+ all of it, even after pm-stripping):
+ - libnet
+ - Bundle::CPAN
+ - Archive::Tar
+ - Compress::Zlib (zlib?) (there is builtin gz support)
+ - Term::ReadKey (useless?)
+ - Term::ReadLine (useless?)
+ - Bundle::LWP
+ - URI
+ - HTML::TagSet
+ - HTML::Parser
+ - HTML::Entities
+ - HTML::HeadParser
+ - LWP
+ - Crypt::SSLeay? (ssl?)
+ - IO::Zlib? (zlib?)
+ - IMAP?
+ - Net::Telnet?
+ - Archive::Zip?
+ - Mail::Send?
+ - Date::Calc?
+ - XML? XML::Simple? (expat?) (there is builtin xml support)
+ - RSS?
+ - DBI
+ - DBD::SQLite? (sqlite?)
+ - SOAP? XML-RPC?
+
+=head1 FUTURE POSSIBILITIES
+
+- Remote console (Bluetooth/IR)
+- S60 GUI support
+- S60 PDA support
+- Phone APIs
+- S80
+- UIQ
diff --git a/symbian/bld.inf b/symbian/bld.inf
new file mode 100644
index 0000000000..c4489677f0
--- /dev/null
+++ b/symbian/bld.inf
@@ -0,0 +1,4 @@
+PRJ_MMPFILES
+PerlApp.mmp
+PerlRecog.mmp
+
diff --git a/symbian/config.pl b/symbian/config.pl
new file mode 100644
index 0000000000..e2cd2c682a
--- /dev/null
+++ b/symbian/config.pl
@@ -0,0 +1,768 @@
+#!/usr/bin/perl -w
+
+# Copyright (c) 2004-2005 Nokia. All rights reserved.
+
+use strict;
+use lib "symbian";
+
+print "Configuring...\n";
+print "Configuring with: Perl version $] ($^X)\n";
+
+do "sanity.pl";
+
+my %VERSION = %{ do "version.pl" };
+
+printf "Configuring for: Perl version $VERSION{REVISION}.%03d%03d\n",
+ $VERSION{VERSION}, $VERSION{SUBVERSION};
+
+my $VERSION = "$VERSION{REVISION}$VERSION{VERSION}$VERSION{SUBVERSION}";
+my $R_V_SV = "$VERSION{REVISION}.$VERSION{VERSION}.$VERSION{SUBVERSION}";
+
+my $SDK = do "sdk.pl";
+my %PORT = %{ do "port.pl" };
+
+my ( $SYMBIAN_VERSION, $SDK_VERSION ) = ( $SDK =~ m!\\Symbian\\(.+?)\\(.+)$! );
+
+if ($SDK eq 'C:\Symbian\Series60_1_2_CW') {
+ ( $SYMBIAN_VERSION, $SDK_VERSION ) = qw(6.1 1.2);
+}
+
+my $WIN = $ENV{WIN} ; # 'wins', 'winscw' (from sdk.pl)
+my $ARM = 'thumb'; # 'thumb', 'armi'
+my $S60SDK = $ENV{S60SDK}; # qw(1.2 2.0 2.1 2.6) (from sdk.pl)
+
+my $UREL = $ENV{UREL}; # from sdk.pl
+$UREL =~ s/-ARM-/$ARM/;
+my $UARM = $ENV{UARM}; # from sdk.pl
+
+die "$0: SDK not recognized\n"
+ if !defined($SYMBIAN_VERSION) || !defined($SDK_VERSION) || !defined($S60SDK);
+
+die "$0: does not know which Windows compiler to use\n"
+ unless defined $WIN;
+
+print "Symbian $SYMBIAN_VERSION SDK $S60SDK ($WIN) installed at $SDK\n";
+
+my $CWD = do "cwd.pl";
+print "Build directory $CWD\n";
+
+die "$0: '+' in cwd does not work with SDK 1.2\n"
+ if $S60SDK eq '1.2' && $CWD =~ /\+/;
+
+my @unclean;
+my @mmp;
+
+sub create_mmp {
+ my ( $target, $type, @x ) = @_;
+ my $miniperl = $target eq 'miniperl';
+ my $perl = $target eq 'perl';
+ my $mmp = "$target.mmp";
+ my $targetpath = $miniperl
+ || $perl ? "TARGETPATH\t\\System\\Apps\\Perl" : "";
+ if ( open( my $fh, ">$mmp" ) ) {
+ print "\t$mmp\n";
+ push @mmp, $mmp;
+ push @unclean, $mmp;
+ print $fh <<__EOF__;
+TARGET $target.$type
+TARGETTYPE $type
+$targetpath
+EPOCHEAPSIZE 1024 8388608
+EPOCSTACKSIZE 65536
+EXPORTUNFROZEN
+SRCDBG
+__EOF__
+ print $fh "MACRO\t__SERIES60_1X__\n" if $S60SDK =~ /^1\./;
+ print $fh "MACRO\t__SERIES60_2X__\n" if $S60SDK =~ /^2\./;
+ my ( @c, %c );
+ @c = map { glob } qw(*.c); # Find the .c files.
+ @c = map { lc } @c; # Lowercase the names.
+ @c = grep { !/malloc\.c/ } @c; # Use the system malloc.
+ @c = grep { !/main\.c/ } @c; # main.c must be explicit.
+ push @c, map { lc } @x;
+ @c = map { s:^\.\./::; $_ } @c; # Remove the leading ../
+ @c = map { $c{$_}++ } @c; # Uniquefy.
+ @c = sort keys %c; # Beautify.
+
+ for (@c) {
+ print $fh "SOURCE\t\t$_\n";
+ }
+ print $fh <<__EOF__;
+SOURCEPATH $CWD
+USERINCLUDE $CWD
+USERINCLUDE $CWD\\ext\\DynaLoader
+USERINCLUDE $CWD\\symbian
+SYSTEMINCLUDE \\epoc32\\include\\libc
+SYSTEMINCLUDE \\epoc32\\include
+LIBRARY euser.lib
+LIBRARY estlib.lib
+__EOF__
+ if ( $miniperl || $perl || $type eq 'dll' ) {
+ print $fh <<__EOF__;
+LIBRARY charconv.lib
+LIBRARY commonengine.lib
+LIBRARY hal.lib
+LIBRARY estor.lib
+__EOF__
+ }
+ if ( $type eq 'exe' ) {
+ print $fh <<__EOF__;
+STATICLIBRARY ecrt0.lib
+__EOF__
+ }
+ if ($miniperl) {
+ print $fh <<__EOF__;
+MACRO PERL_MINIPERL
+__EOF__
+ }
+ if ($perl) {
+ print $fh <<__EOF__;
+MACRO PERL_PERL
+__EOF__
+ }
+ print $fh <<__EOF__;
+MACRO PERL_CORE
+MACRO MULTIPLICITY
+MACRO PERL_IMPLICIT_CONTEXT
+__EOF__
+ unless ( $miniperl || $perl ) {
+ print $fh <<__EOF__;
+MACRO PERL_GLOBAL_STRUCT
+MACRO PERL_GLOBAL_STRUCT_PRIVATE
+__EOF__
+ }
+ close $fh;
+ }
+ else {
+ warn "$0: failed to open $mmp for writing: $!\n";
+ }
+}
+
+sub create_bld_inf {
+ if ( open( BLD_INF, ">bld.inf" ) ) {
+ print "\tbld.inf\n";
+ push @unclean, "bld.inf";
+ print BLD_INF <<__EOF__;
+PRJ_PLATFORMS
+${WIN} ${ARM}
+PRJ_MMPFILES
+__EOF__
+ for (@mmp) { print BLD_INF $_, "\n" }
+ close BLD_INF;
+ }
+ else {
+ warn "$0: failed to open bld.inf for writing: $!\n";
+ }
+}
+
+my %config;
+
+sub load_config_sh {
+ if ( open( CONFIG_SH, "symbian/config.sh" ) ) {
+ while (<CONFIG_SH>) {
+ if (/^(\w+)=['"]?(.*?)["']?$/) {
+ my ( $var, $val ) = ( $1, $2 );
+ $val =~ s/x.y.z/$R_V_SV/gi;
+ $val =~ s/thumb/$ARM/gi;
+ $val = "'$SYMBIAN_VERSION'" if $var eq 'osvers';
+ $val = "'$SDK_VERSION'" if $var eq 'sdkvers';
+ $config{$var} = $val;
+ }
+ }
+ close CONFIG_SH;
+ }
+ else {
+ warn "$0: failed to open symbian\\config.sh for reading: $!\n";
+ }
+}
+
+sub create_config_h {
+ load_config_sh();
+ if ( open( CONFIG_H, ">config.h" ) ) {
+ print "\tconfig.h\n";
+ push @unclean, "config.h";
+ if ( open( CONFIG_H_SH, "config_h.SH" ) ) {
+ while (<CONFIG_H_SH>) {
+ last if /\#ifndef _config_h_/;
+ }
+ print CONFIG_H <<__EOF__;
+/*
+ * Package name : perl
+ * Source directory : .
+ * Configuration time:
+ * Configured by :
+ * Target system : symbian
+ */
+
+#ifndef _config_h_
+__EOF__
+ while (<CONFIG_H_SH>) {
+ last if /!GROK!THIS/;
+ s/\$(\w+)/exists $config{$1} ? $config{$1} : ""/eg;
+ s/^#undef\s+(\S+).+/#undef $1/g;
+ s:\Q/**/::;
+ print CONFIG_H;
+ }
+ close CONFIG_H_SH;
+ }
+ else {
+ warn "$0: failed to open ../config_h.SH for reading: $!\n";
+ }
+ close CONFIG_H;
+ }
+ else {
+ warn "$0: failed to open config.h for writing: $!\n";
+ }
+}
+
+sub create_DynaLoader_cpp {
+ print "\text\\DynaLoader\\DynaLoader.cpp\n";
+ system(
+q[perl -Ilib lib\ExtUtils\xsubpp ext\DynaLoader\dl_symbian.xs >ext\DynaLoader\DynaLoader.cpp]
+ ) == 0
+ or die "$0: creating DynaLoader.cpp failed: $!\n";
+ push @unclean, 'ext\DynaLoader\DynaLoader.cpp';
+
+}
+
+sub create_symbian_port_h {
+ print "\tsymbian\\symbian_port.h\n";
+ if ( open( SYMBIAN_PORT_H, ">symbian/symbian_port.h" ) ) {
+ $S60SDK =~ /^(\d+)\.(\d+)$/;
+ my ($sdkmajor, $sdkminor) = ($1, $2);
+ print SYMBIAN_PORT_H <<__EOF__;
+/* Copyright (c) 2004-2005, Nokia. All rights reserved. */
+
+#ifndef __symbian_port_h__
+#define __symbian_port_h__
+
+#define PERL_SYMBIANPORT_MAJOR $PORT{dll}->{MAJOR}
+#define PERL_SYMBIANPORT_MINOR $PORT{dll}->{MINOR}
+#define PERL_SYMBIANPORT_PATCH $PORT{dll}->{PATCH}
+
+#define PERL_SYMBIANSDK_FLAVOR L"Series 60"
+#define PERL_SYMBIANSDK_MAJOR $sdkmajor
+#define PERL_SYMBIANSDK_MINOR $sdkminor
+
+#endif /* #ifndef __symbian_port_h__ */
+__EOF__
+ close(SYMBIAN_PORT_H);
+ push @unclean, 'symbian\symbian_port.h';
+ }
+ else {
+ warn "$0: failed to open symbian/symbian_port.h for writing: $!\n";
+ }
+}
+
+sub create_perlmain_c {
+ print "\tperlmain.c\n";
+ system(
+q[perl -ne "print qq[ char *file = __FILE__;\n] if /dXSUB_SYS/;print;print qq[ newXS(\"DynaLoader::boot_DynaLoader\", boot_DynaLoader, file);\n] if /dXSUB_SYS/;print qq[EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);\n] if /Do not delete this line/" miniperlmain.c > perlmain.c]
+ ) == 0
+ or die "$0: Creating perlmain.c failed: $!\n";
+ push @unclean, 'perlmain.c';
+}
+
+sub create_PerlApp_pkg {
+ print "\tsymbian\\PerlApp.pkg\n";
+ if ( open( PERLAPP_PKG, ">symbian\\PerlApp.pkg" ) ) {
+ my $APPS = $UREL;
+ if ($S60SDK ne '1.2' || $SDK =~ m/_CW$/) { # Do only if not in 1.2 VC.
+ $APPS =~ s!\\epoc32\\release\\(.+)\\$UARM$!\\epoc32\\data\\z\\system\\apps\\PerlApp!i;
+ }
+ print PERLAPP_PKG <<__EOF__;
+; !!!!!! DO NOT EDIT THIS FILE !!!!!!
+; This file is built by symbian\\config.pl.
+; Any changes made here will be lost!
+;
+; PerlApp.pkg
+;
+; Note that the demo_pl needs to be run to create the demo .pl scripts.
+;
+; Languages
+&EN;
+
+; Standard SIS file header
+#{"PerlApp"},(0x102015F6),0,1,0
+
+; Supports Series 60 v0.9
+(0x101F6F88), 0, 0, 0, {"Series60ProductID"}
+
+; Files
+"$UREL\\PerlApp.APP"-"!:\\system\\apps\\PerlApp\\PerlApp.app"
+"$UREL\\PerlRecog.mdl"-"!:\\system\\recogs\\PerlRecog.mdl"
+"$APPS\\PerlApp.rsc"-"!:\\system\\apps\\PerlApp\\PerlApp.rsc"
+"$APPS\\PerlApp.aif"-"!:\\system\\apps\\PerlApp\\PerlApp.aif"
+__EOF__
+ if ( open( DEMOS, "perl symbian\\demo_pl list |" ) ) {
+ while (<DEMOS>) {
+ chomp;
+ print PERLAPP_PKG qq["$_"-"!:\\Perl\\$_"\n];
+ }
+ close(DEMOS);
+ }
+ close(PERLAPP_PKG);
+ }
+ else {
+ die "$0: symbian\\PerlApp.pkg: $!\n";
+ }
+ push @unclean, 'symbian\PerlApp.pkg';
+}
+
+print "Creating...\n";
+create_mmp(
+ 'miniperl', 'exe',
+ 'miniperlmain.c', 'symbian\symbian_stubs.c',
+ 'symbian\PerlBase.cpp', 'symbian\symbian_utils.cpp',
+);
+create_mmp(
+ "perl", 'exe',
+ 'perlmain.c', 'symbian\symbian_stubs.c',
+ 'symbian\symbian_utils.cpp', 'symbian\PerlBase.cpp',
+ 'ext\DynaLoader\DynaLoader.cpp',
+);
+
+create_mmp(
+ "perl$VERSION", 'dll',
+ 'symbian\symbian_dll.cpp', 'symbian\symbian_stubs.c',
+ 'symbian\symbian_utils.cpp', 'symbian\PerlBase.cpp',
+ 'ext\DynaLoader\DynaLoader.cpp',
+);
+
+create_bld_inf();
+create_config_h();
+create_perlmain_c();
+create_symbian_port_h();
+create_DynaLoader_cpp();
+create_PerlApp_pkg();
+
+if ( open( PERLAPP_MMP, ">symbian/PerlApp.mmp" ) ) {
+ my @MACRO;
+ push @MACRO, '__SERIES60_1X__' if $S60SDK =~ /^1\./;
+ push @MACRO, '__SERIES60_2X__' if $S60SDK =~ /^2\./;
+ print PERLAPP_MMP <<__EOF__;
+// !!!!!! DO NOT EDIT THIS FILE !!!!!!
+// This file is built by symbian\\config.pl.
+// Any changes made here will be lost!
+TARGET PerlApp.app
+TARGETTYPE app
+UID 0x100039CE 0x102015F6
+TARGETPATH \\system\\apps\\PerlApp
+SRCDBG
+EXPORTUNFROZEN
+SOURCEPATH .
+SOURCE PerlApp.cpp
+
+RESOURCE PerlApp.rss
+
+USERINCLUDE .
+USERINCLUDE ..
+USERINCLUDE \\symbian\\perl\\$R_V_SV\\include
+
+SYSTEMINCLUDE \\epoc32\\include
+SYSTEMINCLUDE \\epoc32\\include\\libc
+
+LIBRARY apparc.lib
+LIBRARY avkon.lib
+LIBRARY bafl.lib
+LIBRARY charconv.lib
+LIBRARY commondialogs.lib
+LIBRARY cone.lib
+LIBRARY efsrv.lib
+LIBRARY eikcore.lib
+LIBRARY estlib.lib
+LIBRARY euser.lib
+LIBRARY perl$VERSION.lib
+
+AIF PerlApp.aif . PerlAppAif.rss
+__EOF__
+ if (@MACRO) {
+ for my $macro (@MACRO) {
+ print PERLAPP_MMP <<__EOF__;
+MACRO $macro
+__EOF__
+ }
+ }
+ close(PERLAPP_MMP);
+ push @unclean, 'symbian\PerlApp.mmp';
+}
+else {
+ warn "$0: failed to create symbian\\PerlApp.mmp";
+}
+
+if ( open( MAKEFILE, ">Makefile" ) ) {
+ my $perl = "perl$VERSION";
+ my $windef1 = "$SDK\\Epoc32\\Build$CWD\\$perl\\$WIN\\$perl.def";
+ my $windef2 = "..\\BWINS\\${perl}u.def";
+ my $armdef1 = "$SDK\\Epoc32\\Build$CWD\\$perl\\$ARM\\$perl.def";
+ my $armdef2 = "..\\BMARM\\${perl}u.def";
+ print "\tMakefile\n";
+ print MAKEFILE <<__EOF__;
+help:
+ \@echo === Perl for Symbian ===
+ \@echo Useful targets:
+ \@echo all win arm clean
+ \@echo perldll.sis perlext.sis perlsdk.zip
+
+WIN = ${WIN}
+ARM = ${ARM}
+
+all: build
+
+build: rename_makedef build_win build_arm
+
+@unclean: symbian\\config.pl
+ perl symbian\\config.pl
+
+build_win: abld.bat win_perl.mf win_miniperl.mf win_${VERSION}.mf perldll_win
+
+build_vc6: abld.bat win_perl.mf win_miniperl.mf win_${VERSION}.mf vc6.mf perldll_win
+
+build_arm: abld.bat perl_arm miniperl_arm arm_${VERSION}.mf perldll_arm
+
+miniperl_win: miniperl.mmp abld.bat win_miniperl.mf rename_makedef
+ abld build \$(WIN) udeb miniperl
+
+miniperl_arm: miniperl.mmp abld.bat arm_miniperl.mf rename_makedef
+ abld build \$(ARM) $UARM miniperl
+
+miniperl: miniperl_win miniperl_arm
+
+perl: perl_win perl_arm
+
+perl_win: perl.mmp abld.bat win_perl.mf rename_makedef
+ abld build \$(WIN) perl
+
+perl_arm: perl.mmp abld.bat arm_perl.mf rename_makedef
+ abld build \$(ARM) $UARM perl
+
+perldll_win: perl${VERSION}_win freeze_win perl${VERSION}_win
+
+perl${VERSION}_win: perl$VERSION.mmp abld.bat rename_makedef
+ abld build \$(WIN) perl$VERSION
+
+perldll_arm: perl${VERSION}_arm freeze_arm perl${VERSION}_arm
+
+perl${VERSION}_arm: perl$VERSION.mmp arm_${VERSION}.mf abld.bat rename_makedef
+ abld build \$(ARM) $UARM perl$VERSION
+
+perldll perl$VERSION: perldll_win perldll_arm
+
+win: miniperl_win perl_win perldll_win
+
+arm: miniperl_arm perl_arm perldll_arm
+
+rename_makedef:
+ -ren makedef.pl nomakedef.pl
+
+# Symbian SDK has a makedef.pl of its own,
+# and we don't need Perl's.
+rerename_makedef:
+ -ren nomakedef.pl makedef.pl
+
+abld.bat abld: bld.inf
+ bldmake bldfiles
+
+makefiles: win.mf arm.mf vc6.mf
+
+vc6: win.mf vc6.mf build_vc6
+
+win_miniperl.mf: abld.bat symbian\\config.pl
+ abld makefile \$(WIN) miniperl
+ echo > win_miniperl.mf
+
+win_perl.mf: abld.bat symbian\\config.pl
+ abld makefile \$(WIN) perl
+ echo > win_perl.mf
+
+win_${VERSION}.mf: abld.bat symbian\\config.pl
+ abld makefile \$(WIN) perl${VERSION}
+ echo > win_${VERSION}.mf
+
+symbian\\win.mf:
+ cd symbian; make win.mf
+
+win.mf: win_miniperl.mf win_perl.mf win_${VERSION}.mf symbian\\win.mf
+
+arm_miniperl.mf: abld.bat symbian\\config.pl
+ abld makefile \$(ARM) miniperl
+ echo > arm_miniperl.mf
+
+arm_perl.mf: abld.bat symbian\\config.pl
+ abld makefile \$(ARM) perl
+ echo > arm_perl.mf
+
+arm_${VERSION}.mf: abld.bat symbian\\config.pl
+ abld makefile \$(ARM) perl${VERSION}
+ echo > arm_${VERSION}.mf
+
+arm.mf: arm_miniperl.mf arm_perl.mf arm_${VERSION}.mf
+
+vc6.mf: abld.bat symbian\\config.pl
+ abld makefile vc6
+ echo > vc6.mf
+
+PM = lib\\Config.pm lib\\Cross.pm lib\\lib.pm ext\\DynaLoader\\DynaLoader.pm ext\\DynaLoader\\XSLoader.pm ext\\Errno\\Errno.pm
+POD = lib\\Config.pod
+
+pm: \$(PM)
+
+XLIB = -Ixlib\\symbian
+
+XSBOPT = --win=\$(WIN) --arm=\$(ARM)
+
+lib\\Config.pm:
+ copy symbian\\config.sh config.sh
+ perl -pi.bak -e "s:x\\.y\\.z+:$R_V_SV:g" config.sh
+ perl \$(XLIB) configpm --cross=symbian
+ copy xlib\\symbian\\Config.pm lib\\Config.pm
+ perl -pi.bak -e "s:x\\.y\\.z:$R_V_SV:g" lib\\Config.pm
+ perl -pi.bak -e "s:5\\.\\d+\\.\\d+:$R_V_SV:g" lib\\Config.pm
+ -perl -pi.bak -e "s:x\\.y\\.z:$R_V_SV:g" xlib\\symbian\\Config_heavy.pl
+
+lib\\lib.pm:
+ perl lib\\lib_pm.PL
+
+ext\\DynaLoader\\DynaLoader.pm:
+ -del /f ext\\DynaLoader\\DynaLoader.pm
+ perl -Ixlib\\symbian ext\\DynaLoader\\DynaLoader_pm.PL
+ perl -pi.bak -e "s/__END__//" DynaLoader.pm
+ copy /y DynaLoader.pm ext\\DynaLoader\\DynaLoader.pm
+ -del /f DynaLoader.pm DynaLoader.pm.bak
+
+ext\\DynaLoader\\XSLoader.pm:
+ perl \$(XLIB) symbian\\xsbuild.pl \$(XSBOPT) XSLoader
+
+ext\\Errno\\Errno.pm:
+ perl \$(XLIB) symbian\\xsbuild.pl \$(XSBOPT) Errno
+
+miniperlexe.sis: miniperl_arm symbian\\makesis.pl
+ perl \$(XLIB) symbian\\makesis.pl miniperl
+
+perlexe.sis: perl_arm symbian\\makesis.pl
+ perl \$(XLIB) symbian\\makesis.pl perl
+
+
+allsis: all miniperlexe.sis perlexe.sis perldll.sis perllib.sis perlext.sis perlapp.sis
+
+perldll.sis perl$VERSION.sis: perldll_arm pm symbian\\makesis.pl
+ perl \$(XLIB) symbian\\makesis.pl perl${VERSION}dll
+
+perllib.sis: \$(PM)
+ perl \$(XLIB) symbian\\makesis.pl perl${VERSION}lib
+
+perlext.sis: perldll_arm buildext_sis
+ perl symbian\\makesis.pl perl${VERSION}ext
+
+EXT = Cwd Data::Dumper Devel::Peek Digest::MD5 Errno Fcntl File::Glob Filter::Util::Call IO List::Util MIME::Base64 PerlIO::scalar PerlIO::via SDBM_File Socket Storable Time::HiRes XSLoader attrs
+
+buildext: perldll symbian\\xsbuild.pl
+ perl \$(XLIB) symbian\\xsbuild.pl \$(XSBOPT) \$(EXT)
+
+buildext_sis: perldll.sis symbian\\xsbuild.pl
+ perl \$(XLIB) symbian\\xsbuild.pl \$(XSBOPT) --sis \$(EXT)
+
+cleanext: symbian\\xsbuild.pl
+ perl \$(XLIB) symbian\\xsbuild.pl \$(XSBOPT) --clean \$(EXT)
+
+distcleanext: symbian\\xsbuild.pl
+ perl \$(XLIB) symbian\\xsbuild.pl \$(XSBOPT) --distclean \$(EXT)
+
+sis makesis: miniperl perl perldll pm buildext perlapp.sis
+ perl \$(XLIB) symbian\\makesis.pl
+
+APIDIR = \\Symbian\\perl\\$R_V_SV
+
+sdkinstall:
+ -mkdir \\Symbian\\perl
+ -mkdir \\Symbian\\perl\\$R_V_SV
+ -mkdir \$(APIDIR)\\include
+ -mkdir \$(APIDIR)\\include\\symbian
+ -mkdir \$(APIDIR)\\lib
+ -mkdir \$(APIDIR)\\lib\\ExtUtils
+ -mkdir \$(APIDIR)\\pod
+ -mkdir \$(APIDIR)\\bin
+ -mkdir \$(BINDIR)
+ copy /y *.h \$(APIDIR)\\include
+ - copy /y *.inc \$(APIDIR)\\include
+ copy /y lib\\ExtUtils\\xsubpp \$(APIDIR)\\lib\\ExtUtils
+ copy /y lib\\ExtUtils\\typemap \$(APIDIR)\\lib\\ExtUtils
+ copy /y symbian\\xsbuild.pl \$(APIDIR)\\bin
+ copy /y symbian\\PerlBase.h \$(APIDIR)\\include
+ copy /y symbian\\symbian*.h \$(APIDIR)\\include\\symbian
+ copy /y symbian\\PerlBase.pod \$(APIDIR)\\pod
+
+RELDIR = $SDK\\epoc32\\release
+RELWIN = \$(RELDIR)\\\$(WIN)\\udeb
+RELARM = \$(RELDIR)\\\$(ARM)\\$UARM
+
+perlsdk.zip: perldll sdkinstall
+ zip -r perl${VERSION}sdk.zip \$(RELWIN)\\perl$VERSION.* \$(RELARM)\\perl$VERSION.* \$(APIDIR)
+ \@echo perl${VERSION}sdk.zip created.
+
+perlapp: sdkinstall perlapp_win perlapp_arm
+
+perlapp_win: config.h
+ cd symbian; make perlapp_win
+
+perlapp_arm: config.h
+ cd symbian; make perlapp_arm
+
+perlapp_demo_extract:
+ cd symbian; make perlapp_demo_extract
+
+perlapp.sis: perlapp_arm
+ cd symbian; make perlapp.sis
+
+perlapp.zip:
+ cd symbian; zip perlapp.zip PerlApp.* PerlRecog.* PerlBase.* demo_pl
+
+zip: perlsdk.zip perlapp.zip
+
+freeze: freeze_win freeze_arm
+
+freeze_win:
+ abld freeze \$(WIN) perl$VERSION
+
+freeze_arm:
+ abld freeze \$(ARM) perl$VERSION
+
+defrost: defrost_win defrost_arm
+
+defrost_win:
+ -del /f $windef1
+ -del /f $windef2
+
+defrost_arm:
+ -del /f $armdef1
+ -del /f $armdef2
+
+clean_win: abld.bat
+ abld clean \$(WIN)
+
+clean_arm: abld.bat
+ abld clean \$(ARM)
+
+clean: clean_win clean_arm rerename_makedef
+ -del /f \$(PM)
+ -del /f \$(POD)
+ -del /f lib\\Config.pm.bak
+ -del /f xlib\\symbian\\Config_heavy.pl
+ -rmdir /s /q xlib
+ -del /f config.sh
+ -del /f DynaLoader.pm ext\\DynaLoader\\DynaLoader.pm
+ -del /f ext\\DynaLoader\\Makefile
+ -del /f ext\\SDBM_File\\sdbm\\Makefile
+ -del /f symbian\\*.lst
+ -del /f abld.bat @unclean *.pkg *.sis *.zip
+ -del /f symbian\\abld.bat symbian\\*.sis symbian\\*.zip
+ -del /f symbian\\perl5*.pkg symbian\\miniperl.pkg
+ -del arm_*.mf win_*.mf vc6*.mf
+ -perl symbian\\xsbuild.pl \$(XSBOPT) --clean \$(EXT)
+ -rmdir /s /q perl${VERSION}_Data
+ -cd symbian; make clean
+
+reallyclean: abld.bat
+ abld reallyclean
+
+distclean: defrost reallyclean clean
+ -perl symbian\\xsbuild.pl \$(XSBOPT) --distclean \$(EXT)
+ -del /f config.h config.sh.bak symbian\\symbian_port.h
+ -del /f Makefile symbian\\PerlApp.mmp
+ -del /f BMARM\\*.def
+ -del /f *.cwlink *.resources *.pref
+ -del /f perl${VERSION}.xml perl${VERSION}.mcp uid.cpp
+ -rmdir /s /q BMARM
+ cd symbian; make distclean
+ -del /f symbian\\Makefile
+__EOF__
+ close MAKEFILE;
+}
+else {
+ warn "$0: failed to create Makefile: $!\n";
+}
+
+if ( open( MAKEFILE, ">symbian/Makefile")) {
+ my $wrap = $S60SDK eq '1.2' && $SDK !~ /_CW$/;
+ my $ABLD = $wrap ? 'perl b.pl': 'abld';
+ print "\tsymbian/Makefile\n";
+ print MAKEFILE <<__EOF__;
+WIN = $WIN
+ARM = $ARM
+ABLD = $ABLD
+
+abld.bat:
+ bldmake bldfiles
+
+perlapp_win: abld.bat ..\\config.h PerlApp.h PerlApp.cpp
+ bldmake bldfiles
+ \$(ABLD) build \$(WIN) udeb
+
+perlapp_arm: ..\\config.h PerlApp.h PerlApp.cpp
+ bldmake bldfiles
+ \$(ABLD) build \$(ARM) $UARM
+
+win.mf:
+ bldmake bldfiles
+ abld makefile vc6
+
+perlapp_demo_extract:
+ perl demo_pl extract
+
+perlapp.sis: perlapp_arm perlapp_demo_extract
+ -del /f perlapp.SIS
+ makesis perlapp.pkg
+ copy /y perlapp.SIS ..\\perlapp.SIS
+
+clean:
+ -perl demo_pl cleanup
+ -del /f perlapp.sis
+ -del /f b.pl
+
+distclean: clean
+ -del /f *.cwlink *.resources *.pref
+ -del /f PerlApp.xml PerlApp.mcp uid.cpp
+ -rmdir /s /q PerlApp_Data
+ -del /f abld.bat
+__EOF__
+ close(MAKEFILE);
+ if ($wrap) {
+ if ( open( B_PL, ">symbian/b.pl")) {
+ print B_PL <<'__EOF__';
+# abld.pl wrapper.
+
+# nmake doesn't like MFLAGS and MAKEFLAGS being set to -w and w.
+delete $ENV{MFLAGS};
+delete $ENV{MAKEFLAGS};
+
+system("abld @ARGV");
+__EOF__
+ close(B_PL);
+ } else {
+ warn "$0: failed to create symbian/b.pl: $!\n";
+ }
+ }
+} else {
+ warn "$0: failed to create symbian/Makefile: $!\n";
+}
+
+print "Deleting...\n";
+for my $config (
+ # Do not delete config.h here.
+ "config.sh",
+ "lib\\Config.pm",
+ "xlib\\symbian\\Config.pm",
+ "xlib\\symbian\\Config_heavy.pl",
+ ) {
+ print "\t$config\n";
+ unlink($config);
+}
+
+print <<__EOM__;
+Configuring done.
+Now you can run:
+ make all
+ make allsis
+__EOM__
+
+1; # Happy End.
diff --git a/symbian/config.sh b/symbian/config.sh
new file mode 100644
index 0000000000..1c1fa01b99
--- /dev/null
+++ b/symbian/config.sh
@@ -0,0 +1,768 @@
+#!\\bin\\sh
+PERL_CONFIG_SH='true'
+_a='.a'
+_o='.o'
+afs='false'
+afsroot='/afs'
+alignbytes='4'
+apiversion='5.005'
+ar=':'
+archlib='\\system\\libs\\perl\\x.y.z\\thumb-symbian'
+archlibexp='\\system\\libs\\perl\\x.y.z\\thumb-symbian'
+archname='thumb-symbian'
+asctime_r_proto='0'
+bin='\\system\\apps\\perl'
+binexp='\\system\\apps\\perl'
+bincompat5005='n'
+byteorder='1234'
+castflags='0'
+cc='gcc'
+cccdlflags=''
+ccdlflags=''
+charsize='1'
+clocktype='clock_t'
+cpp_stuff='42'
+cppminus='-'
+cpprun='gcc -E'
+cppstdin='gcc -E'
+crypt_r_proto='0'
+ctermid_r_proto='0'
+ctime_r_proto='0'
+d_Gconvert='sprintf((b),"%.*g",(n),(x))'
+d_PRIEUldbl='undef'
+d_PRIFUldbl='undef'
+d_PRIGUldbl='undef'
+d_PRIXU64='undef'
+d_PRId64='undef'
+d_PRIeldbl='undef'
+d_PRIfldbl='undef'
+d_PRIgldbl='undef'
+d_PRIi64='undef'
+d_PRIo64='undef'
+d_PRIu64='undef'
+d_PRIx64='undef'
+d_SCNfldbl='undef'
+d__fwalk='undef'
+d_access='undef'
+d_accessx='undef'
+d_aintl='undef'
+d_alarm='undef'
+d_archlib='define'
+d_asctime_r='undef'
+d_atolf='undef'
+d_atoll='undef'
+d_attribut='undef'
+d_bcmp='undef'
+d_bcopy='undef'
+d_bsd='undef'
+d_bsdgetpgrp='undef'
+d_bsdsetpgrp='undef'
+d_bzero='undef'
+d_casti32='undef'
+d_castneg='undef'
+d_charvspr='undef'
+d_chown='undef'
+d_chroot='undef'
+d_chsize='undef'
+d_class='undef'
+d_closedir='undef'
+d_cmsghdr_s='undef'
+d_const='define'
+d_copysignl='undef'
+d_crypt='undef'
+d_crypt_r='undef'
+d_csh='undef'
+d_ctermid_r='undef'
+d_ctime_r='undef'
+d_cuserid='undef'
+d_dbl_dig='undef'
+d_dbminitproto='undef'
+d_difftime='undef'
+d_dirfd='undef'
+d_dirnamlen='define'
+d_dlerror='undef'
+d_dlopen='undef'
+d_dlsymun='undef'
+d_dosuid='undef'
+d_drand48_r='undef'
+d_drand48proto='undef'
+d_dup2='undef'
+d_eaccess='undef'
+d_endgrent='undef'
+d_endgrent_r='undef'
+d_endhent='undef'
+d_endhostent_r='undef'
+d_endnent='undef'
+d_endnetent_r='undef'
+d_endpent='undef'
+d_endprotoent_r='undef'
+d_endpwent='undef'
+d_endpwent_r='undef'
+d_endsent='undef'
+d_endservent_r='undef'
+d_eofnblk='undef'
+d_eunice='undef'
+d_faststdio='undef'
+d_fchdir='undef'
+d_fchmod='undef'
+d_fchown='undef'
+d_fcntl='undef'
+d_fcntl_can_lock='undef'
+d_fd_macros='undef'
+d_fd_set='undef'
+d_fds_bits='undef'
+d_fgetpos='undef'
+d_finite='undef'
+d_finitel='undef'
+d_flexfnam='undef'
+d_flock='undef'
+d_flockproto='undef'
+d_fork='undef'
+d_fp_class='undef'
+d_fpathconf='undef'
+d_fpclass='undef'
+d_fpclassify='undef'
+d_fpclassl='undef'
+d_fpos64_t='undef'
+d_frexpl='undef'
+d_fs_data_s='undef'
+d_fseeko='undef'
+d_fsetpos='define'
+d_fstatfs='undef'
+d_fstatvfs='undef'
+d_fsync='undef'
+d_ftello='undef'
+d_ftime='undef'
+d_getcwd='define'
+d_getespwnam='undef'
+d_getfsstat='undef'
+d_getgrent='undef'
+d_getgrent_r='undef'
+d_getgrgid_r='undef'
+d_getgrnam_r='undef'
+d_getgrps='undef'
+d_gethbyaddr='define'
+d_gethbyname='define'
+d_gethent='undef'
+d_gethname='define'
+d_gethostbyaddr_r='undef'
+d_gethostbyname_r='undef'
+d_gethostent_r='undef'
+d_gethostprotos='define'
+d_getitimer='undef'
+d_getlogin='undef'
+d_getlogin_r='undef'
+d_getmnt='undef'
+d_getmntent='undef'
+d_getnbyaddr='undef'
+d_getnbyname='undef'
+d_getnent='undef'
+d_getnetbyaddr_r='undef'
+d_getnetbyname_r='undef'
+d_getnetent_r='undef'
+d_getnetprotos='undef'
+d_getpagsz='undef'
+d_getpbyname='define'
+d_getpbynumber='define'
+d_getpent='undef'
+d_getpgid='undef'
+d_getpgrp2='undef'
+d_getpgrp='undef'
+d_getppid='undef'
+d_getprior='undef'
+d_getprotobyname_r='undef'
+d_getprotobynumber_r='undef'
+d_getprotoent_r='undef'
+d_getprotoprotos='define'
+d_getprpwnam='undef'
+d_getpwent='undef'
+d_getpwent_r='undef'
+d_getpwnam_r='undef'
+d_getpwuid_r='undef'
+d_getsbyname='define'
+d_getsbyport='define'
+d_getsent='undef'
+d_getservbyname_r='undef'
+d_getservbyport_r='undef'
+d_getservent_r='undef'
+d_getservprotos='define'
+d_getspent='undef'
+d_getspnam='undef'
+d_getspnam_r='undef'
+d_gettimeod='define'
+d_gmtime_r='undef'
+d_gnulibc='undef'
+d_grpasswd='undef'
+d_hasmntopt='undef'
+d_htonl='define'
+d_ilogbl='undef'
+d_index='undef'
+d_inetaton='undef'
+d_int64_t='undef'
+d_isascii='undef'
+d_isfinite='undef'
+d_isinf='undef'
+d_isnan='undef'
+d_isnanl='undef'
+d_killpg='undef'
+d_lchown='undef'
+d_ldbl_dig='undef'
+d_libm_lib_version='undef'
+d_link='undef'
+d_localtime_r='undef'
+d_locconv='undef'
+d_lockf='undef'
+d_longdbl='undef'
+d_longlong='undef'
+d_lseekproto='undef'
+d_lstat='undef'
+d_madvise='undef'
+d_mblen='undef'
+d_mbstowcs='undef'
+d_mbtowc='undef'
+d_memchr='define'
+d_memcmp='define'
+d_memcpy='define'
+d_memmove='define'
+d_memset='define'
+d_mkdir='define'
+d_mkdtemp='undef'
+d_mkfifo='undef'
+d_mkstemp='undef'
+d_mkstemps='undef'
+d_mktime='undef'
+d_mmap='undef'
+d_modfl='undef'
+d_modfl_pow32_bug='undef'
+d_modflproto='undef'
+d_mprotect='undef'
+d_msg='undef'
+d_msg_ctrunc='undef'
+d_msg_dontroute='undef'
+d_msg_oob='undef'
+d_msg_peek='undef'
+d_msg_proxy='undef'
+d_msgctl='undef'
+d_msgget='undef'
+d_msghdr_s='undef'
+d_msgrcv='undef'
+d_msgsnd='undef'
+d_msync='undef'
+d_munmap='undef'
+d_mymalloc='undef'
+d_nice='undef'
+d_nl_langinfo='undef'
+d_nv_preserves_uv='undef'
+d_off64_t='undef'
+d_old_pthread_create_joinable='undef'
+d_oldpthreads='undef'
+d_oldsock='undef'
+d_open3='undef'
+d_pathconf='undef'
+d_pause='undef'
+d_perl_otherlibdirs='undef'
+d_phostname='undef'
+d_pipe='undef'
+d_poll='undef'
+d_portable='undef'
+d_procselfexe='undef'
+d_pthread_atfork='undef'
+d_pthread_attr_setscope='undef'
+d_pthread_yield='undef'
+d_pwage='undef'
+d_pwchange='undef'
+d_pwclass='undef'
+d_pwcomment='undef'
+d_pwexpire='undef'
+d_pwgecos='undef'
+d_pwpasswd='undef'
+d_pwquota='undef'
+d_qgcvt='undef'
+d_quad='undef'
+d_random_r='undef'
+d_readdir64_r='undef'
+d_readdir='define'
+d_readdir_r='undef'
+d_readlink='undef'
+d_readv='undef'
+d_recvmsg='undef'
+d_rename='define'
+d_rewinddir='define'
+d_rmdir='define'
+d_safebcpy='undef'
+d_safemcpy='undef'
+d_sanemcmp='undef'
+d_sbrkproto='undef'
+d_scalbnl='undef'
+d_sched_yield='undef'
+d_scm_rights='undef'
+d_seekdir='define'
+d_select='undef'
+d_sem='undef'
+d_semctl='undef'
+d_semctl_semid_ds='undef'
+d_semctl_semun='undef'
+d_semget='undef'
+d_semop='undef'
+d_sendmsg='undef'
+d_setegid='undef'
+d_seteuid='undef'
+d_setgrent='undef'
+d_setgrent_r='undef'
+d_setgrps='undef'
+d_sethent='undef'
+d_sethostent_r='undef'
+d_setitimer='undef'
+d_setlinebuf='undef'
+d_setlocale='undef'
+d_setlocale_r='undef'
+d_setnent='undef'
+d_setnetent_r='undef'
+d_setpent='undef'
+d_setpgid='undef'
+d_setpgrp2='undef'
+d_setpgrp='undef'
+d_setprior='undef'
+d_setproctitle='undef'
+d_setprotoent_r='undef'
+d_setpwent='undef'
+d_setpwent_r='undef'
+d_setregid='undef'
+d_setresgid='undef'
+d_setresuid='undef'
+d_setreuid='undef'
+d_setrgid='undef'
+d_setruid='undef'
+d_setsent='undef'
+d_setservent_r='undef'
+d_setsid='undef'
+d_setvbuf='define'
+d_sfio='undef'
+d_shm='undef'
+d_shmat='undef'
+d_shmatprototype='undef'
+d_shmctl='undef'
+d_shmdt='undef'
+d_shmget='undef'
+d_sigaction='undef'
+d_sigprocmask='undef'
+d_sigsetjmp='undef'
+d_sitecustomize='undef'
+d_sockatmark='undef'
+d_sockatmarkproto='undef'
+d_socket='define'
+d_socklen_t='undef'
+d_sockpair='undef'
+d_socks5_init='undef'
+d_sqrtl='undef'
+d_srand48_r='undef'
+d_srandom_r='undef'
+d_sresgproto='undef'
+d_sresuproto='undef'
+d_statblks='undef'
+d_statfs_f_flags='undef'
+d_statfs_s='undef'
+d_statvfs='undef'
+d_stdio_cnt_lval='undef'
+d_stdio_ptr_lval='undef'
+d_stdio_ptr_lval_nochange_cnt='undef'
+d_stdio_ptr_lval_sets_cnt='undef'
+d_stdio_stream_array='undef'
+d_stdiobase='undef'
+d_stdstdio='undef'
+d_strchr='define'
+d_strcoll='undef'
+d_strctcpy='undef'
+d_strerrm='strerror(e)'
+d_strerror='define'
+d_strerror_r='undef'
+d_strftime='undef'
+d_strlcat='undef'
+d_strlcpy='undef'
+d_strtod='define'
+d_strtol='define'
+d_strtold='undef'
+d_strtoll='undef'
+d_strtoq='undef'
+d_strtoul='define'
+d_strtoull='undef'
+d_strtouq='undef'
+d_strxfrm='undef'
+d_suidsafe='undef'
+d_symlink='undef'
+d_syscall='undef'
+d_syscallproto='undef'
+d_sysconf='undef'
+d_sysernlst=''
+d_syserrlst='undef'
+d_system='define'
+d_tcgetpgrp='undef'
+d_tcsetpgrp='undef'
+d_telldir='define'
+d_telldirproto='define'
+d_time='define'
+d_times='define'
+d_tm_tm_gmtoff='undef'
+d_tm_tm_zone='undef'
+d_tmpnam_r='undef'
+d_truncate='undef'
+d_ttyname_r='undef'
+d_tzname='undef'
+d_u32align='define'
+d_ualarm='undef'
+d_umask='undef'
+d_uname='undef'
+d_union_semun='undef'
+d_unordered='undef'
+d_sitecustomize='undef'
+d_usleep='define'
+d_usleepproto='undef'
+d_ustat='undef'
+d_vendorarch='undef'
+d_vendorbin='undef'
+d_vendorlib='undef'
+d_vfork='undef'
+d_void_closedir='undef'
+d_voidsig='undef'
+d_voidtty=''
+d_volatile='define'
+d_vprintf='define'
+d_wait4='undef'
+d_waitpid='undef'
+d_wcstombs='undef'
+d_wctomb='undef'
+d_writev='undef'
+d_xenix='undef'
+db_hashtype='u_int32_t'
+db_prefixtype='size_t'
+defvoidused=1
+direntrytype='struct dirent'
+dlext='dll'
+dlsrc='dl_symbian.xs'
+doublesize='8'
+drand01="((rand() & 0x7FFF) / (double) ((unsigned long)1 << 15))"
+drand48_r_proto='0'
+eagain='EAGAIN'
+ebcdic='undef'
+endgrent_r_proto='0'
+endhostent_r_proto='0'
+endnetent_r_proto='0'
+endprotoent_r_proto='0'
+endpwent_r_proto='0'
+endservent_r_proto='0'
+eunicefix=':'
+exe_ext='.exe'
+fflushNULL='undef'
+fflushall='undef'
+firstmakefile='makefile'
+fpossize='4'
+fpostype=fpos_t
+freetype=void
+full_ar=':'
+getgrent_r_proto='0'
+getgrgid_r_proto='0'
+getgrnam_r_proto='0'
+gethostbyaddr_r_proto='0'
+gethostbyname_r_proto='0'
+gethostent_r_proto='0'
+getlogin_r_proto='0'
+getnetbyaddr_r_proto='0'
+getnetbyname_r_proto='0'
+getnetent_r_proto='0'
+getprotobyname_r_proto='0'
+getprotobynumber_r_proto='0'
+getprotoent_r_proto='0'
+getpwent_r_proto='0'
+getpwnam_r_proto='0'
+getpwuid_r_proto='0'
+getservbyname_r_proto='0'
+getservbyport_r_proto='0'
+getservent_r_proto='0'
+getspnam_r_proto='0'
+gidformat='"lu"'
+gidsign='1'
+gidsize='4'
+gidtype=int
+gmtime_r_proto='0'
+groupstype=int
+h_fcntl='false'
+h_sysfile='true'
+i16size='2'
+i16type='short'
+i32size='4'
+i32type='long'
+i64size='8'
+i64type='int64_t'
+i8size='1'
+i8type='char'
+i_arpainet='undef'
+i_bsdioctl=''
+i_crypt='undef'
+i_db='undef'
+i_dbm='undef'
+i_dirent='define'
+i_dld='undef'
+i_dlfcn='undef'
+i_fcntl='define'
+i_float='undef'
+i_fp='undef'
+i_fp_class='undef'
+i_gdbm='undef'
+i_grp='undef'
+i_ieeefp='undef'
+i_inttypes='undef'
+i_langinfo='undef'
+i_libutil='undef'
+i_limits='define'
+i_locale='define'
+i_machcthr='undef'
+i_malloc='undef'
+i_math='define'
+i_memory='undef'
+i_mntent='undef'
+i_ndbm='undef'
+i_netdb='define'
+i_neterrno='undef'
+i_netinettcp='undef'
+i_niin='define'
+i_poll='undef'
+i_prot='undef'
+i_pthread='undef'
+i_pwd='define'
+i_rpcsvcdbm='undef'
+i_sfio='undef'
+i_sgtty='undef'
+i_shadow='undef'
+i_socks='undef'
+i_stdarg='define'
+i_stddef='undef'
+i_stdlib='define'
+i_string='define'
+i_sunmath='undef'
+i_sysaccess='undef'
+i_sysdir='undef'
+i_sysfile='undef'
+i_sysfilio='undef'
+i_sysin='undef'
+i_sysioctl='define'
+i_syslog='undef'
+i_sysmman='undef'
+i_sysmode='undef'
+i_sysmount='undef'
+i_sysndir='undef'
+i_sysparam='undef'
+i_sysresrc='undef'
+i_syssecrt='undef'
+i_sysselct='undef'
+i_syssockio='undef'
+i_sysstat='define'
+i_sysstatfs='undef'
+i_sysstatvfs='undef'
+i_systime='define'
+i_systimek='undef'
+i_systimes='define'
+i_systypes='define'
+i_sysuio='undef'
+i_sysun='undef'
+i_sysutsname='undef'
+i_sysvfs='undef'
+i_syswait='undef'
+i_termio='undef'
+i_termios='undef'
+i_time='define'
+i_unistd='define'
+i_ustat='undef'
+i_utime='undef'
+i_values='undef'
+i_varargs='undef'
+i_varhdr='stdarg.h'
+i_vfork='undef'
+ignore_versioned_solibs='y'
+inc_version_list='0'
+inc_version_list_init='0'
+installprefix='\\system'
+installprefixexp='\\system'
+installsitearch='\\system\\libs\\perl\\siteperl\\x.y.z\\thumb-symbian'
+installsitelib='\\system\\libs\\perl\\siteperl\\x.y.z'
+installstyle='lib\\perl5'
+installusrbinperl='undef'
+intsize='4'
+ivdformat='"ld"'
+ivsize='4'
+ivtype='long'
+lib_ext='.a'
+lddlflags=''
+ld=':'
+ldflags=''
+libc='stdlib'
+libm_lib_version='0'
+libperl='libperl.a'
+localtime_r_proto='0'
+longdblsize=8
+longlongsize=8
+longsize='4'
+lseeksize=4
+lseektype=int
+make='make'
+malloctype='int*'
+malloctype='void *'
+modetype='mode_t'
+modetype=int
+multiarch='undef'
+myarchname='thumb-symbian'
+myuname='symbian'
+need_va_copy='undef'
+netdb_hlen_type='int'
+netdb_host_type='const char *'
+netdb_name_type='const char *'
+netdb_net_type='unsigned long'
+nroff='nroff'
+nv_preserves_uv_bits='0'
+nveformat='"e"'
+nvfformat='"f"'
+nvgformat='"g"'
+nvsize='8'
+nvtype='double'
+o_nonblock='O_NONBLOCK'
+obj_ext='.o'
+old_pthread_create_joinable=''
+optimize='-O2'
+orderlib='false'
+osname='symbian'
+osvers='7.0s'
+otherlibdirs=''
+path_sep=';';
+phostname='hostname'
+pidtype='int'
+pm_apiversion='5.005'
+privlib='\\system\\libs\\perl\\x.y.z'
+privlibexp='\\system\\libs\\perl\\x.y.z'
+procselfexe=''
+prototype='undef'
+ptrsize='4'
+quadkind='4'
+quadtype='int64_t'
+randbits='48'
+randfunc='drand48'
+random_r_proto='0'
+randseedtype='int'
+ranlib=':'
+rd_nodata='-1'
+readdir64_r_proto='0'
+readdir_r_proto='0'
+sPRIEUldbl='"llE"'
+sPRIFUldbl='"llF"'
+sPRIGUldbl='"llG"'
+sPRIXU64='"LX"'
+sPRId64='"Ld"'
+sPRIeldbl=''
+sPRIfldbl=''
+sPRIgldbl=''
+sPRIi64='"Li"'
+sPRIo64='"Lo"'
+sPRIu64='"Lu"'
+sPRIx64='"Lx"'
+sSCNfldbl=''
+sched_yield='sched_yield()'
+scriptdir='\\system\\apps\\perl'
+scriptdirexp='\\system\\apps\\perl'
+sdkvers=''
+seedfunc='srand'
+selectminbits='32'
+selecttype=int
+setgrent_r_proto='0'
+sethostent_r_proto='0'
+setlocale_r_proto='0'
+setnetent_r_proto='0'
+setprotoent_r_proto='0'
+setpwent_r_proto='0'
+setservent_r_proto='0'
+shmattype='void *'
+shortsize=2
+sig_name_init='0'
+sig_num_init='0'
+sig_size='1'
+signal_t=void
+sitearch='\\system\\libs\\perl\\siteperl\\x.y.z\\thumb-symbian'
+sitearchexp='\\system\\libs\\perl\\siteperl\\x.y.z\\thumb-symbian'
+sitelib='\\system\\libs\\perl\\siteperl\\x.y.z'
+sitelib_stem='\\system\\libs\\perl'
+sitelibexp='\\system\\libs\\perl\\siteperl\\x.y.z'
+siteprefix='\\system'
+siteprefixexp='\\system'
+sizesize=4
+sizetype=size_t
+so='o'
+socksizetype='unsigned int'
+srand48_r_proto='0'
+srandom_r_proto='0'
+ssizetype=int
+stdchar=char
+stdio_base='((fp)->_IO_read_base)'
+stdio_bufsiz='((fp)->_IO_read_end - (fp)->_IO_read_base)'
+stdio_cnt='((fp)->_IO_read_end - (fp)->_IO_read_ptr)'
+stdio_filbuf=''
+stdio_ptr='((fp)->_IO_read_ptr)'
+stdio_stream_array=''
+strerror_r_proto='0'
+targetarch='thumb-symbian'
+timetype=time_t
+tmpnam_r_proto='0'
+touch='touch'
+ttyname_r_proto='0'
+u16size='2'
+u16type='unsigned short'
+u32size='4'
+u32type='unsigned long'
+u64size='8'
+u64type='uint64_t'
+u8size='1'
+u8type='unsigned char'
+uidformat='"lu"'
+uidsign='1'
+uidsize='4'
+uidtype=int
+uquadtype='uint64_t'
+use5005threads='undef'
+use64bitall='undef'
+use64bitint='undef'
+usecrosscompile='define'
+usedl='undef'
+usefaststdio='undef'
+useithreads='undef'
+uselargefiles='undef'
+uselongdouble='undef'
+usemallocwrap='define'
+usemorebits='undef'
+usemultiplicity='undef'
+usemymalloc='n'
+usenm='false'
+useopcode='true'
+useperlio='define'
+useposix='true'
+usereentrant='undef'
+userelocatableinc='undef'
+usesfio='false'
+useshrplib='false'
+usesitecustomize='undef'
+usesocks='undef'
+usethreads='undef'
+usevendorprefix='n'
+usevfork='false'
+uvXUformat='"lX"'
+uvoformat='"lo"'
+uvsize='4'
+uvtype='unsigned long'
+uvuformat='"lu"'
+vendorlib_stem=''
+vendorlib=''
+vendorlibexp=''
+vendorarch=''
+vendorarchexp=''
+vendorprefix=''
+vendorprefixexp=''
+version='x.y.z'
+uvxformat='"lx"'
+versiononly='undef'
+voidflags=1
+xs_apiversion='5.008'
diff --git a/symbian/cwd.pl b/symbian/cwd.pl
new file mode 100644
index 0000000000..d3272d2de5
--- /dev/null
+++ b/symbian/cwd.pl
@@ -0,0 +1,6 @@
+use strict;
+use Cwd;
+my $CWD = getcwd();
+$CWD =~ s!^C:!!i;
+$CWD =~ s!/!\\!g;
+$CWD;
diff --git a/symbian/demo_pl b/symbian/demo_pl
new file mode 100644
index 0000000000..fbba5f4bf9
--- /dev/null
+++ b/symbian/demo_pl
@@ -0,0 +1,128 @@
+#!/usr/bin/perl -w
+
+#
+# demo_pl
+#
+# A "self-extracting archive" for some demo scripts.
+#
+# hello - the classic
+# helloyou - advanced classic
+# httpget1 - simple sockets
+# httpget2 - simple sockets done complex
+# md5 - core extension
+# time - system call
+# times - more system calls
+#
+
+use strict;
+
+unless (@ARGV && $ARGV[0] =~ /^(?:list|extract|cleanup)$/) {
+ die "$0: Usage: $0 [list|extract|cleanup]\n";
+}
+
+my $action = shift;
+my $list = $action eq 'list';
+my $extract = $action eq 'extract';
+my $cleanup = $action eq 'cleanup';
+
+my $fh;
+while (<DATA>) {
+ if (/^-- (.+\.pl)$/) {
+ if ($cleanup) {
+ print "Deleting $1\n";
+ unlink $1 or warn "$0: $1: $!\n";
+ } elsif ($extract) {
+ defined $fh && close($fh);
+ open($fh, ">$1") or die "$0: '$1': $!\n";
+ print "Extracting $1\n";
+ } elsif ($list) {
+ print "$1\n";
+ }
+ } else {
+ print $fh $_ if $extract;
+ }
+}
+defined $fh && close($fh);
+exit(0);
+__END__
+-- hello.pl
+print "hello world!\n";
+-- helloyou.pl
+print "What is your name?\n";
+chomp(my $name = <STDIN>);
+print "Hello, $name!\n";
+print "Amazing fact #1:\n";
+printf "Your name has\n%d character%s!\n",
+ length($name), length($name) == 1 ? "" : "s";
+print "Amazing fact #2:\n";
+printf "Your name is\n%s backwards!\n", scalar reverse $name;
+-- httpget1.pl
+print "(Using plain sockets)\n";
+use Socket;
+print "Host? ";
+my $host = <STDIN>;
+chomp($host);
+$host = 'www.nokia.com' unless length $host;
+my $port = 80;
+my $iaddr = inet_aton($host) || die "no host: $host";
+my $paddr = sockaddr_in($port, $iaddr);
+my $proto = getprotobyname("tcp");
+socket(S, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
+connect(S, $paddr) || die "connect: $!";
+print "$host:$port:\nConnected.\n";
+select(S); $| = 1; select(STDOUT);
+print S "GET / HTTP/1.0\012\012" || die "GET /: $!";
+my @line;
+print "Receiving...\n";
+while (my $line = <S>) {
+ push @line, $line;
+}
+close(S) || die "close: $!";
+printf "Got %d lines.\n", scalar @line;
+-- httpget2.pl
+use IO::Socket;
+print "(Using IO::Socket)\n";
+print "Host? ";
+my $host = <STDIN>;
+chomp($host);
+$host = 'www.nokia.com' unless length $host;
+my $port = 80;
+my $remote =
+ IO::Socket::INET->new(Proto => "tcp",
+ PeerAddr => $host,
+ PeerPort => $port);
+print "$host:$port:\nConnected.\n";
+select($remote); $| = 1; select(STDOUT);
+print $remote "GET / HTTP/1.0\012\012" || die "GET /: $!";
+my @line;
+print "Receiving...\n";
+while (my $line = <$remote>) {
+ push @line, $line;
+}
+close($remote) || die "close: $!";
+printf "Got %d lines.\n", scalar @line;
+-- md5.pl
+use Digest::MD5 'md5_hex';
+print "(Using Digest::MD5)\nMD5 of 'Perl' is:\n";
+print md5_hex('Perl'), "\n";
+-- time.pl
+print "Running in $^O\n";
+print scalar localtime, "\n";
+-- times.pl
+use Time::HiRes qw(time sleep);
+print CORE::time(), "\n";
+print "Hires\n";
+print time(), "\n";
+print "Sleep 1.5 s...\n";
+sleep(1.5);
+print time(), "\n";
+print "To one million...\n";
+my $t0 = time();
+print $t0, "\n";
+print "Cpu ", scalar times(), "\n";
+for(my $i = 0; $i < 1e6; $i++) {}
+print "Cpu ", scalar times(), "\n";
+my $t1 = time();
+print $t1, "\n";
+print "Wall ", $t1 - $t0, "\n";
+
diff --git a/symbian/install.cfg b/symbian/install.cfg
new file mode 100644
index 0000000000..8cc7b10b9c
--- /dev/null
+++ b/symbian/install.cfg
@@ -0,0 +1,108 @@
+# install.cfg
+#
+# Copyright (c) 2004-2005 Nokia. All Rights Reserved.
+#
+# This file details what library files to include in the perlXYZlib.sis,
+# and what extensions to build for the perlXYZext.sis.
+# The lines beginning with "lib" are # included as-is from the lib/.
+# The lines beginning with "ext" tell either how to build and package
+# the extensions - or not.
+
+#
+# Libraries.
+#
+lib AnyDBM_File.pm
+lib AutoLoader.pm
+lib base.pm
+lib Benchmark.pm
+lib Carp.pm
+lib Carp/Heavy.pm
+lib Cwd.pm
+lib constant.pm
+lib DBM_Filter.pm
+lib Digest/base.pm
+lib DirHandle.pm
+lib Exporter.pm
+lib Exporter/Heavy.pm
+lib File/Basename.pm
+lib File/Compare.pm
+lib File/Copy.pm
+lib File/DosGlob.pm
+lib File/Find.pm
+lib File/Path.pm
+lib File/Spec.pm
+lib File/Spec/Unix.pm
+lib File/Spec/Win32.pm
+lib File/Temp.pm
+lib FileHandle.pm
+lib Filter/Simple.pm
+lib if.pm
+lib integer.pm
+lib lib.pm
+lib Net/Cmd.pm
+lib Net/Config.pm
+lib Net/Domain.pm
+lib Net/FTP.pm
+lib Net/FTP/A.pm
+lib Net/FTP/E.pm
+lib Net/FTP/I.pm
+lib Net/FTP/L.pm
+lib Net/FTP/dataconn.pm
+lib Net/NNTP.pm
+lib Net/Netrc.pm
+lib Net/Ping.pm
+lib Net/POP3.pm
+lib Net/SMTP.pm
+lib Net/Time.pm
+lib NEXT.pm
+lib overload.pm
+lib SelectSaver.pm
+lib strict.pm
+lib Symbol.pm
+lib UNIVERSAL.pm
+# lib utf8.pm
+# lib utf8_heavy.pl
+lib vars.pm
+lib warnings.pm
+lib warnings/register.pm
+#
+# Extensions.
+#
+ext attrs
+ext Cwd
+ext Data/Dumper
+ext Devel/Peek
+ext Digest/MD5
+ext Errno
+ext Fcntl CONST
+ext File/Glob CONST
+ext Filter/Util/Call
+ext IO
+ext List/Util
+ext MIME/Base64
+ext PerlIO/scalar
+ext PerlIO/via
+ext SDBM_File -sdbm/db?.c -sdbm/util.c
+ext Socket CONST
+ext Storable
+ext Time/HiRes CONST
+ext XSLoader
+# ext B ERROR
+# ext ByteLoader byterun.c ERROR VERSION
+# ext Devel/DProf nonconst
+# ext Devel/PPPort PORT
+# ext Encode nonconst Encode/encode.h def_t.c encengine.c
+# ext I18N/Langinfo PORT
+# ext IPC/SysV PORT
+# ext Opcode ERROR
+# ext PerlIO/encoding Encode
+# ext POSIX CONST USELESS
+# ext re ERROR
+# ext Sys/Hostname PORT
+# ext Sys/Syslog PORT
+# ext threads PORT
+# ext threads/shared PORT
+# ext Unicode/Normalize nonconst
+# ext XS/APItest USELESS
+# ext XS/Typemap nonconst USELESS
+
diff --git a/symbian/makesis.pl b/symbian/makesis.pl
new file mode 100644
index 0000000000..1ee5e8dc2f
--- /dev/null
+++ b/symbian/makesis.pl
@@ -0,0 +1,185 @@
+#!/usr/bin/perl -w
+
+# Copyright (c) 2004-2005 Nokia. All rights reserved.
+
+use strict;
+use lib "symbian";
+
+do "sanity.pl";
+
+my %VERSION = %{ do "version.pl" };
+my $VERSION = "$VERSION{REVISION}$VERSION{VERSION}$VERSION{SUBVERSION}";
+my $R_V_SV = "$VERSION{REVISION}.$VERSION{VERSION}.$VERSION{SUBVERSION}";
+
+my $SDK = do "sdk.pl";
+my $UID = do "uid.pl";
+my %PORT = %{ do "port.pl" };
+
+my $ARM = 'thumb'; # TODO
+my $S60SK = $ENV{S60SDK}; # from sdk.pl
+
+my $UREL = $ENV{UREL}; # from sdk.pl
+$UREL =~ s/-ARM-/$ARM/;
+
+my $app = '!:\System\Apps\Perl';
+my $lib = '!:\System\Libs';
+
+my @target = @ARGV
+ ? @ARGV
+ : (
+ "miniperl", "perl",
+ "perl${VERSION}dll", "perl${VERSION}lib",
+ "perl${VERSION}ext"
+ );
+
+my %suffix;
+@suffix{ "miniperl", "perl", "perl$VERSION" } = ( "exe", "exe", "dll", );
+
+for my $target (@target) {
+ $target = "perl${VERSION}" if $target eq "perl${VERSION}dll";
+
+ my %copy;
+ my $pkg = "$target.pkg";
+ print "\nCreating $pkg...\n";
+
+ my $suffix = $suffix{$target} || "";
+ my $dst = $suffix eq "dll" ? $lib : $app;
+
+ my $srctarget = "$UREL\\$target.$suffix";
+
+ if ( $target =~ /^(miniperl|perl|perl${VERSION}(?:dll)?)$/ ) {
+ $copy{$srctarget} = "$dst\\$target.$suffix";
+ print "\t$target.$suffix\n";
+ }
+ if ( $target eq "perl${VERSION}lib" ) {
+ print "Libraries...\n";
+
+ print "\tConfig.pm\n";
+ $copy{"lib\\Config.pm"} =
+ "$lib\\Perl\\$R_V_SV\\thumb-symbian\\Config.pm";
+
+ print "\tConfig_heavy.pl\n";
+ $copy{"xlib\\symbian\\Config_heavy.pl"} =
+ "$lib\\Perl\\$R_V_SV\\thumb-symbian\\Config_heavy.pl";
+
+ print "\tDynaLoader.pm\n";
+ $copy{"ext\\DynaLoader\\DynaLoader.pm"} =
+ "$lib\\Perl\\$R_V_SV\\DynaLoader.pm";
+
+ print "\tErrno.pm\n";
+ $copy{"ext\\Errno\\Errno.pm"} = "$lib\\Perl\\$R_V_SV\\Errno.pm";
+
+ open( my $cfg, "symbian/install.cfg" )
+ or die "$!: symbian/install.cfg: $!\n";
+ while (<$cfg>) {
+ next unless /^lib\s+(.+)/;
+ chomp;
+ my $f = $1;
+ $f =~ s:/:\\:g;
+ $copy{"lib\\$f"} = "$lib\\Perl\\$R_V_SV\\$f";
+ print "\t$f\n";
+ }
+ close($cfg);
+ }
+
+ if ( $target eq "perl${VERSION}ext" ) {
+ my @lst = glob("symbian/*.lst");
+ print "Extensions...\n";
+ print "\t(none found)\n" unless @lst;
+ for my $lst (@lst) {
+ $lst =~ m:^symbian/(.+)\.:;
+ my $ext = $1;
+ $ext =~ s!-!::!g;
+ print "\t$ext\n";
+ if ( open( my $pkg, $lst ) ) {
+ while (<$pkg>) {
+ if (m!^"(.+)"-"(.+)"$!) {
+ my ( $src, $dst ) = ( $1, $2 );
+ $copy{$src} = $dst;
+ }
+ else {
+ warn "$0: $lst: $.: unknown syntax\n";
+ }
+ }
+ close($pkg);
+ }
+ else {
+ warn "$0: $lst: $!\n";
+ }
+ }
+ }
+
+ for my $file ( keys %copy ) {
+ warn "$0: $file does not exist\n" unless -f $file;
+ }
+
+ my @copy = map { qq["$_"-"$copy{$_}"] } sort keys %copy;
+ my $copy = join( "\n", @copy );
+
+ my %UID = (
+ "miniperl" => 0,
+ "perl" => 0,
+ "perl${VERSION}" => $UID + 0,
+ "perl${VERSION}dll" => $UID + 0,
+ "perl${VERSION}ext" => $UID + 1,
+ "perl${VERSION}lib" => $UID + 2,
+
+ # app = + 3
+ # rec = + 4
+ );
+
+ die "$0: target has no UID\n" unless defined $UID{$target};
+
+ my $uid = sprintf( "0x%08X", $UID{$target} );
+
+ my ( $MAJOR, $MINOR, $PATCH ) = ( 0, 0, 0 );
+
+ if ( $target =~ m:^perl$VERSION(dll|ext|lib)?$: ) {
+ my $pkg = defined $1 ? $1 : "dll";
+ $MAJOR = $PORT{$pkg}->{MAJOR};
+ $MINOR = $PORT{$pkg}->{MINOR};
+ $PATCH = $PORT{$pkg}->{PATCH};
+ }
+
+ die "$0: Bad version for $target\n"
+ unless defined $MAJOR
+ && ( $MAJOR eq 0 || $MAJOR > 0 )
+ && defined $MINOR
+ && ( $MINOR eq 0 || $MINOR > 0 )
+ && defined $PATCH
+ && ( $PATCH eq 0 || $PATCH > 0 );
+
+ open PKG, ">$pkg" or die "$0: failed to create $pkg: $!\n";
+ print PKG <<__EOF__;
+; \u$target installation script
+;
+; The supported languages
+&EN;
+;
+; The installation name and header data
+;
+#{"\u$target"},($uid),$MAJOR,$MINOR,$PATCH
+;
+; Private key and certificate (unused)
+;
+;* "\u$target.key", "\u$target.cer"
+;
+; Supports Series60 v0.9
+(0x101F6F88), 0, 0, 0, {"Series60ProductID"}
+; The files to install
+;
+$copy
+__EOF__
+ close PKG;
+
+ print "Created $pkg\n";
+
+ print "Running makesis...\n";
+
+ unlink("$target.sis");
+
+ system("makesis $pkg") == 0
+ || die "$0: makesis $pkg failed: $!\n";
+}
+
+exit(0);
diff --git a/symbian/port.pl b/symbian/port.pl
new file mode 100644
index 0000000000..affb42c461
--- /dev/null
+++ b/symbian/port.pl
@@ -0,0 +1,6 @@
+{
+ dll => { MAJOR => 0, MINOR => 1, PATCH => 0 },
+ ext => { MAJOR => 0, MINOR => 1, PATCH => 0 },
+ lib => { MAJOR => 0, MINOR => 1, PATCH => 0 },
+}
+
diff --git a/symbian/sanity.pl b/symbian/sanity.pl
new file mode 100644
index 0000000000..eb50244dde
--- /dev/null
+++ b/symbian/sanity.pl
@@ -0,0 +1,28 @@
+use strict;
+
+if (exists $ENV{'!C:'}) {
+ print "You are running this under Cygwin, aren't you?\n";
+ print "I'm sorry but only cmd.exe will work.\n";
+ exit(1);
+}
+
+if (# SDK 2.x
+ $ENV{PATH} !~ m!c:\\program files\\common files\\symbian\\tools!i
+ &&
+ # SDK 1.2
+ $ENV{PATH} !~ m!c:\\symbian\\6.1\\shared\\epoc32\\tools!i) {
+ print "I think you have not installed the Symbian SDK.\n";
+ exit(1);
+}
+
+unless (-f "symbian/symbianish.h") {
+ print "You must run this in the top level directory.\n";
+ exit(1);
+}
+
+if ($] < 5.008) {
+ print "You must configure with Perl 5.8 or later.\n";
+ exit(1);
+}
+
+1;
diff --git a/symbian/sdk.pl b/symbian/sdk.pl
new file mode 100644
index 0000000000..1dc4d2f552
--- /dev/null
+++ b/symbian/sdk.pl
@@ -0,0 +1,48 @@
+use strict;
+
+my $SDK;
+my $WIN;
+
+if ($ENV{PATH} =~ m!\\Symbian\\(.+?)\\gcc\\bin!) {
+ my $cc = $1;
+ $WIN = $cc =~ m!_CW!i ? 'winscw' : 'wins';
+ $ENV{WIN} = $WIN;
+ if ($cc =~ m!Series60_v20!) {
+ $ENV{S60SDK} = '2.0';
+ } elsif ($cc =~ m!Series60_v21!) {
+ $ENV{S60SDK} = '2.1';
+ } elsif ($cc =~ m!S60_2nd_FP2!) {
+ $ENV{S60SDK} = '2.6';
+ }
+}
+
+if (open(GCC, "gcc -v 2>&1|")) {
+ while (<GCC>) {
+ if (/Reading specs from ((?:C:)?\\Symbian.+?)\\Epoc32\\/i) {
+ $SDK = $1;
+ # The S60SDK tells the Series 60 SDK version.
+ if ($SDK eq 'C:\Symbian\6.1\Shared') { # Visual C.
+ $SDK = 'C:\Symbian\6.1\Series60';
+ $ENV{S60SDK} = '1.2';
+ } elsif ($SDK eq 'C:\Symbian\Series60_1_2_CW') { # CodeWarrior.
+ $ENV{S60SDK} = '1.2';
+ }
+ last;
+ }
+ }
+ close GCC;
+} else {
+ die "$0: failed to run gcc: $!\n";
+}
+
+my $UARM = $ENV{UARM} ? $ENV{UARM} : "urel";
+my $UREL = "$SDK\\epoc32\\release\\-ARM-\\$UARM";
+if ($SDK eq 'C:\Symbian\6.1\Series60' && $ENV{WIN} eq 'winscw') {
+ $UREL = "C:\\Symbian\\Series60_1_2_CW\\epoc32\\release\\-ARM-\\urel";
+}
+$ENV{UREL} = $UREL;
+$ENV{UARM} = $UARM;
+
+die "$0: failed to locate the Symbian SDK\n" unless defined $SDK;
+
+$SDK;
diff --git a/symbian/symbian_dll.cpp b/symbian/symbian_dll.cpp
new file mode 100644
index 0000000000..92a06b883f
--- /dev/null
+++ b/symbian/symbian_dll.cpp
@@ -0,0 +1,20 @@
+/*
+ * symbian_dll.cpp
+ *
+ * Copyright (c) Nokia 2004-2005. All rights reserved.
+ * This code is licensed under the same terms as Perl itself.
+ *
+ */
+
+#define SYMBIAN_DLL_CPP
+#include <e32base.h>
+#include "PerlBase.h"
+
+EXPORT_C GLDEF_C TInt E32Dll(TDllReason /*aReason*/) { return KErrNone; }
+
+extern "C" {
+ EXPORT_C void* symbian_get_vars(void) { return Dll::Tls(); }
+ EXPORT_C void symbian_set_vars(const void *p) { Dll::SetTls((TAny*)p); }
+ EXPORT_C void symbian_unset_vars(void) { Dll::SetTls(0); }
+}
+
diff --git a/symbian/symbian_proto.h b/symbian/symbian_proto.h
new file mode 100644
index 0000000000..f50de34af8
--- /dev/null
+++ b/symbian/symbian_proto.h
@@ -0,0 +1,72 @@
+/*
+ * symbian_proto.h
+ *
+ * Copyright (c) Nokia 2004-2005. All rights reserved.
+ * This code is licensed under the same terms as Perl itself.
+ *
+ */
+
+#ifndef SYMBIAN_PROTO_H
+#define SYMBIAN_PROTO_H
+
+#include <sys/types.h>
+#include <sys/times.h>
+
+#if defined(PERL_CORE) || defined(PERL_EXT)
+
+/* We can't include the <string.h> unconditionally
+ * since it has prototypes conflicting with the gcc builtins. */
+extern void *memchr(const void *s, int c, size_t n);
+#ifndef DL_SYMBIAN_XS
+/* dl_symbian.xs needs to see the C++ prototype of memset() instead */
+extern void *memset(void *s, int c, size_t n);
+extern size_t strlen(const char *s);
+#endif
+extern void *memmove(void *dst, const void *src, size_t n);
+extern char *strcat(char *dst, const char *src);
+extern char *strchr(const char *s, int c);
+extern char *strerror(int errnum);
+extern int strncmp(const char *s1, const char *s2, size_t n);
+extern char *strrchr(const char *s, int c);
+
+extern int setmode(int fd, long flags);
+
+#ifndef __GNUC__
+#define memcpy _e32memcpy /* GCC intrinsic */
+extern void *memcpy(const void *s1, const void *s2, size_t n);
+extern int strcmp(const char *s1, const char *s2);
+extern char* strcpy(char *dst, const char *src);
+extern char* strncpy(char *dst, const char *src, size_t n);
+#endif
+
+#endif /* PERL_CORE || PERL_EXT */
+
+#if defined(SYMBIAN_DLL_CPP) || defined(SYMBIAN_UTILS_CPP) || defined(PERLBASE_CPP)
+# define PERL_SYMBIAN_START_EXTERN_C extern "C" {
+# define PERL_SYMBIAN_EXPORT_C EXPORT_C
+# define PERL_SYMBIAN_END_EXTERN_C }
+#else
+# define PERL_SYMBIAN_START_EXTERN_C
+# define PERL_SYMBIAN_EXPORT_C
+# define PERL_SYMBIAN_END_EXTERN_C
+#endif
+
+PERL_SYMBIAN_START_EXTERN_C
+PERL_SYMBIAN_EXPORT_C int symbian_sys_init(int *argcp, char ***argvp);
+PERL_SYMBIAN_EXPORT_C void* symbian_get_vars(void);
+PERL_SYMBIAN_EXPORT_C void symbian_set_vars(const void *);
+PERL_SYMBIAN_EXPORT_C void symbian_unset_vars(void);
+PERL_SYMBIAN_EXPORT_C SSize_t symbian_read_stdin(const int fd, char *b, int n);
+PERL_SYMBIAN_EXPORT_C SSize_t symbian_write_stdout(const int fd, const char *b, int n);
+PERL_SYMBIAN_EXPORT_C char* symbian_get_error_string(const int error);
+PERL_SYMBIAN_EXPORT_C void symbian_sleep_usec(const long usec);
+PERL_SYMBIAN_EXPORT_C int symbian_get_cpu_time(long* sec, long* usec);
+PERL_SYMBIAN_EXPORT_C clock_t symbian_times(struct tms* buf);
+PERL_SYMBIAN_EXPORT_C int symbian_usleep(unsigned int usec);
+PERL_SYMBIAN_EXPORT_C int symbian_do_aspawn(void* vreally, void *vmark, void* sp);
+PERL_SYMBIAN_EXPORT_C int symbian_do_spawn(const char* command);
+PERL_SYMBIAN_EXPORT_C int symbian_do_spawn_nowait(const char* command);
+PERL_SYMBIAN_END_EXTERN_C
+
+#endif /* !SYMBIAN_PROTO_H */
+
diff --git a/symbian/symbian_stubs.c b/symbian/symbian_stubs.c
new file mode 100644
index 0000000000..1505698703
--- /dev/null
+++ b/symbian/symbian_stubs.c
@@ -0,0 +1,112 @@
+/*
+ * symbian_stubs.c
+ *
+ * Copyright (c) Nokia 2004-2005. All rights reserved.
+ * This code is licensed under the same terms as Perl itself.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "symbian_stubs.h"
+
+static int setENOSYS(void) { errno = ENOSYS; return -1; }
+
+uid_t getuid(void) { return setENOSYS(); }
+gid_t getgid(void) { return setENOSYS(); }
+uid_t geteuid(void) { return setENOSYS(); }
+gid_t getegid(void) { return setENOSYS(); }
+
+int setuid(uid_t uid) { return setENOSYS(); }
+int setgid(gid_t gid) { return setENOSYS(); }
+int seteuid(uid_t uid) { return setENOSYS(); }
+int setegid(gid_t gid) { return setENOSYS(); }
+
+int execv(const char* path, char* const argv []) { return setENOSYS(); }
+int execvp(const char* path, char* const argv []) { return setENOSYS(); }
+
+#ifndef USE_PERLIO
+FILE *popen(const char *command, const char *mode) { return 0; }
+int pclose(FILE *stream) { return setENOSYS(); }
+#endif
+int pipe(int fd[2]) { return setENOSYS(); }
+
+int setmode(int fd, long flags) { return -1; }
+
+_sig_func_ptr signal(int signum, _sig_func_ptr handler) { return (_sig_func_ptr)setENOSYS(); }
+int kill(pid_t pid, int signum) { return setENOSYS(); }
+pid_t wait(int *status) { return setENOSYS(); }
+
+#if PERL_VERSION <= 8
+void Perl_my_setenv(pTHX_ char *var, char *val) { }
+#else
+void Perl_my_setenv(pTHX_ const char *var, const char *val) { }
+#endif
+
+bool Perl_do_exec(pTHX_ char *cmd) { return FALSE; }
+bool Perl_do_exec3(pTHX_ char *cmd, int fd, int flag) { return FALSE; }
+
+int Perl_do_spawn(pTHX_ char *cmd) { return symbian_do_spawn(cmd); }
+int Perl_do_aspawn(pTHX_ SV *really, SV** mark, SV **sp) { return symbian_do_aspawn(really, mark, sp); }
+
+static const struct protoent protocols[] = {
+ { "tcp", 0, 6 },
+ { "udp", 0, 17 }
+};
+
+/* The protocol field (the last) is left empty to save both space
+ * and time because practically all services have both tcp and udp
+ * allocations in IANA. */
+static const struct servent services[] = {
+ { "http", 0, 80, 0 }, /* Optimization. */
+ { "https", 0, 443, 0 },
+ { "imap", 0, 143, 0 },
+ { "imaps", 0, 993, 0 },
+ { "smtp", 0, 25, 0 },
+ { "irc", 0, 194, 0 },
+
+ { "ftp", 0, 21, 0 },
+ { "ssh", 0, 22, 0 },
+ { "tftp", 0, 69, 0 },
+ { "pop3", 0, 110, 0 },
+ { "sftp", 0, 115, 0 },
+ { "nntp", 0, 119, 0 },
+ { "ntp", 0, 123, 0 },
+ { "snmp", 0, 161, 0 },
+ { "ldap", 0, 389, 0 },
+ { "rsync", 0, 873, 0 },
+ { "socks", 0, 1080, 0 }
+};
+
+struct protoent* getprotobynumber(int number) {
+ int i;
+ for (i = 0; i < sizeof(protocols)/sizeof(struct protoent); i++)
+ if (protocols[i].p_proto == number)
+ return (struct protoent*)(&(protocols[i]));
+ return 0;
+}
+
+struct protoent* getprotobyname(const char* name) {
+ int i;
+ for (i = 0; i < sizeof(protocols)/sizeof(struct protoent); i++)
+ if (strcmp(name, protocols[i].p_name) == 0)
+ return (struct protoent*)(&(protocols[i]));
+ return 0;
+}
+
+struct servent* getservbyname(const char* name, const char* proto) {
+ int i;
+ for (i = 0; i < sizeof(services)/sizeof(struct servent); i++)
+ if (strcmp(name, services[i].s_name) == 0)
+ return (struct servent*)(&(services[i]));
+ return 0;
+}
+
+struct servent* getservbyport(int port, const char* proto) {
+ int i;
+ for (i = 0; i < sizeof(services)/sizeof(struct servent); i++)
+ if (services[i].s_port == port)
+ return (struct servent*)(&(services[i]));
+ return 0;
+}
+
diff --git a/symbian/symbian_stubs.h b/symbian/symbian_stubs.h
new file mode 100644
index 0000000000..ab6b9616cd
--- /dev/null
+++ b/symbian/symbian_stubs.h
@@ -0,0 +1,22 @@
+/*
+ * symbian_stubs.h
+ *
+ * Copyright (c) Nokia 2004-2005. All rights reserved.
+ * This code is licensed under the same terms as Perl itself.
+ *
+ */
+
+#ifndef PERL_SYMBIAN_STUBS_H
+#define PERL_SYMBIAN_STUBS_H
+
+int execv(const char* path, char* const argv []);
+int execvp(const char* path, char* const argv []);
+
+#ifndef USE_PERLIO
+FILE *popen(const char *command, const char *mode);
+int pclose(FILE *stream);
+#endif
+int pipe(int fd[2]);
+
+#endif /* PERL_SYMBIAN_STUBS_H */
+
diff --git a/symbian/symbian_utils.cpp b/symbian/symbian_utils.cpp
new file mode 100644
index 0000000000..16e911c81e
--- /dev/null
+++ b/symbian/symbian_utils.cpp
@@ -0,0 +1,299 @@
+/*
+ * symbian_utils.cpp
+ *
+ * Copyright (c) Nokia 2004-2005. All rights reserved.
+ * This code is licensed under the same terms as Perl itself.
+ *
+ */
+
+#define SYMBIAN_UTILS_CPP
+#include <e32base.h>
+#include <e32std.h>
+#include <textresolver.h>
+#include <utf.h>
+#include <hal.h>
+
+#include <string.h>
+#include <ctype.h>
+
+#include "PerlBase.h"
+
+extern "C" {
+ EXPORT_C int symbian_sys_init(int *argcp, char ***argvp)
+ {
+#ifdef PERL_GLOBAL_STRUCT /* Avoid unused variable warning. */
+ dVAR;
+#endif
+ (void)times(&PL_timesbase);
+ return 0;
+ }
+ EXPORT_C SSize_t symbian_read_stdin(const int fd, char *b, int n)
+ {
+#ifdef PERL_GLOBAL_STRUCT /* Avoid unused variable warning. */
+ dVAR;
+#endif
+ return ((CPerlBase*)PL_appctx)->ConsoleRead(fd, b, n);
+ }
+ EXPORT_C SSize_t symbian_write_stdout(const int fd, const char *b, int n)
+ {
+#ifdef PERL_GLOBAL_STRUCT /* Avoid unused variable warning. */
+ dVAR;
+#endif
+ return ((CPerlBase*)PL_appctx)->ConsoleWrite(fd, b, n);
+ }
+ static const char NullErr[] = "";
+ EXPORT_C char* symbian_get_error_string(const TInt error)
+ {
+ dTHX;
+ if (error >= 0)
+ return strerror(error);
+ CTextResolver* textResolver = CTextResolver::NewL();
+ CleanupStack::PushL(textResolver);
+ TBuf<KErrorResolverMaxTextLength> buf16;
+ TBuf8<KErrorResolverMaxTextLength> buf8;
+ if (error != KErrNone)
+ buf16 = textResolver->ResolveError(error);
+ if (buf16.Length()) {
+ if (CnvUtfConverter::ConvertFromUnicodeToUtf8(buf8, buf16) !=
+ KErrNone) {
+ CleanupStack::PopAndDestroy(textResolver);
+ return (char*)NullErr;
+ }
+ }
+ SV* sv = Perl_get_sv(aTHX_ "\005", TRUE); /* $^E or ${^OS_ERROR} */
+ if (!sv)
+ return (char*)NullErr;
+ sv_setpv(sv, (const char *)buf8.PtrZ());
+ SvUTF8_on(sv);
+ CleanupStack::PopAndDestroy(textResolver);
+ return SvPV_nolen(sv);
+ }
+ EXPORT_C void symbian_sleep_usec(const long usec)
+ {
+ User::After((TTimeIntervalMicroSeconds32) usec);
+ }
+#define PERL_SYMBIAN_CLK_TCK 100
+ EXPORT_C int symbian_get_cpu_time(long* sec, long* usec)
+ {
+ // The RThread().GetCpuTime() does not seem to work?
+ // (it always returns KErrNotSupported)
+ // TTimeIntervalMicroSeconds ti;
+ // TInt err = me.GetCpuTime(ti);
+ dTHX;
+ TInt periodus; /* tick period in microseconds */
+ if (HAL::Get(HALData::ESystemTickPeriod, periodus) != KErrNone)
+ return -1;
+ TUint tick = User::TickCount();
+ if (PL_timesbase.tms_utime == 0) {
+ PL_timesbase.tms_utime = tick;
+ PL_clocktick = PERL_SYMBIAN_CLK_TCK;
+ }
+ tick -= PL_timesbase.tms_utime;
+ TInt64 tickus = TInt64(tick) * TInt64(periodus);
+ TInt64 tmps = tickus / 1000000;
+ if (sec) *sec = tmps.Low();
+ if (usec) *usec = tickus.Low() - tmps.Low() * 1000000;
+ return 0;
+ }
+ EXPORT_C int symbian_usleep(unsigned int usec)
+ {
+ if (usec >= 1000000) {
+ errno = EINVAL;
+ return -1;
+ }
+ symbian_sleep_usec((const long) usec);
+ return 0;
+ }
+#define SEC_USEC_TO_CLK_TCK(s, u) \
+ (((s) * PERL_SYMBIAN_CLK_TCK) + (u / (1000000 / PERL_SYMBIAN_CLK_TCK)))
+ EXPORT_C clock_t symbian_times(struct tms *tmsbuf)
+ {
+ long s, u;
+ if (symbian_get_cpu_time(&s, &u) == -1) {
+ errno = EINVAL;
+ return -1;
+ } else {
+ tmsbuf->tms_utime = SEC_USEC_TO_CLK_TCK(s, u);
+ tmsbuf->tms_stime = 0;
+ tmsbuf->tms_cutime = 0;
+ tmsbuf->tms_cstime = 0;
+ return tmsbuf->tms_utime;
+ }
+ }
+ class CE32ProcessWait : public CActive
+ {
+ public:
+ CE32ProcessWait() : CActive(EPriorityStandard) {
+ CActiveScheduler::Add(this);
+ }
+#ifdef __WINS__
+ TInt Wait(RThread& aProcess)
+#else
+ TInt Wait(RProcess& aProcess)
+#endif
+ {
+ aProcess.Logon(iStatus);
+ aProcess.Resume();
+ SetActive();
+ CActiveScheduler::Start();
+ return iStatus.Int();
+ }
+ private:
+ void DoCancel() {;}
+ void RunL() {
+ CActiveScheduler::Stop();
+ }
+ CActiveSchedulerWait iWait;
+ };
+ class CSpawnIoRedirect : public CBase
+ {
+ public:
+ CSpawnIoRedirect();
+ // NOTE: there is no real implementation of I/O redirection yet.
+ protected:
+ private:
+ };
+ CSpawnIoRedirect::CSpawnIoRedirect()
+ {
+ }
+ typedef enum {
+ ESpawnNone = 0x00000000,
+ ESpawnWait = 0x00000001
+ } TSpawnFlag;
+ static int symbian_spawn(const TDesC& aFilename,
+ const TDesC& aCommand,
+ const TSpawnFlag aFlag,
+ const CSpawnIoRedirect& aIoRedirect) {
+ TInt error = KErrNone;
+#ifdef __WINS__
+ const TInt KStackSize = 0x1000;
+ const TInt KHeapMin = 0x1000;
+ const TInt KHeapMax = 0x100000;
+ RThread proc;
+ RLibrary lib;
+ HBufC* command = aCommand.Alloc();
+ error = lib.Load(aFilename);
+ if (error == KErrNone) {
+ TThreadFunction func = (TThreadFunction)(lib.Lookup(1));
+ if (func)
+ error = proc.Create(aFilename,
+ func,
+ KStackSize,
+ (TAny*)command,
+ &lib,
+ RThread().Heap(),
+ KHeapMin,
+ KHeapMax,
+ EOwnerProcess);
+ else
+ error = KErrNotFound;
+ lib.Close();
+ }
+ else
+ delete command;
+#else
+ RProcess proc;
+ error = proc.Create(aFilename, aCommand);
+#endif
+ if (error == KErrNone) {
+ if ((TInt)aFlag & (TInt)ESpawnWait) {
+ CE32ProcessWait* w = new CE32ProcessWait();
+ if (w) {
+ error = w->Wait(proc);
+ delete w;
+ } else
+ error = KErrNoMemory;
+ } else
+ proc.Resume();
+ proc.Close();
+ }
+ return error;
+ }
+ static int symbian_spawner(const char *command, TSpawnFlag aFlags)
+ {
+ TBuf<KMaxFileName> aFilename;
+ TBuf<KMaxFileName> aCommand;
+ TSpawnFlag aSpawnFlags = ESpawnWait;
+ CSpawnIoRedirect iord;
+ char *p = (char*)command;
+
+ // The recognized syntax is: "cmd [args] [&]". Since one
+ // cannot pass more than (an argv[0] and) an argv[1] to a
+ // Symbian process anyway, not much is done to the cmd or
+ // the args, only backslash quoting.
+
+ // Strip leading whitespace.
+ while (*p && isspace(*p)) p++;
+ if (*p) {
+ // Build argv[0].
+ while (*p && !isspace(*p) && *p != '&') {
+ if (*p == '\\') {
+ if (p[1]) {
+ aFilename.Append(p[1]);
+ p++;
+ }
+
+ }
+ else
+ aFilename.Append(*p);
+ p++;
+ }
+
+ if (*p) {
+ // Skip whitespace between argv[0] and argv[1].
+ while(*p && isspace(*p)) p++;
+ // Build argv[1].
+ if (*p) {
+ char *a = p;
+ char *b = p + 1;
+
+ while (*b) b++;
+ if (isspace(b[-1])) {
+ b--;
+ while (b > a && isspace(*b)) b--;
+ b++;
+ }
+ if (b > a && b[-1] == '&') {
+ // Parse backgrounding in any case,
+ // but turn it off only if wanted.
+ if ((aFlags & ESpawnWait))
+ aSpawnFlags =
+ (TSpawnFlag) (aSpawnFlags & ~ESpawnWait);
+ b--;
+ if (isspace(b[-1])) {
+ b--;
+ while (b > a && isspace(*b)) b--;
+ b++;
+ }
+ }
+ for (p = a; p < b; p++) {
+ if (*p == '\\') {
+ if (p[1])
+ aCommand.Append(p[1]);
+ p++;
+ }
+ else
+ aCommand.Append(*p);
+ }
+ }
+ // NOTE: I/O redirection is not yet done.
+ // Implementing that may require a separate server.
+ }
+ }
+ int spawned = symbian_spawn(aFilename, aCommand, aSpawnFlags, iord);
+ return spawned == KErrNone ? 0 : -1;
+ }
+ EXPORT_C int symbian_do_spawn(const char *command)
+ {
+ return symbian_spawner(command, ESpawnWait);
+ }
+ EXPORT_C int symbian_do_spawn_nowait(const char *command)
+ {
+ return symbian_spawner(command, ESpawnNone);
+ }
+ EXPORT_C int symbian_do_aspawn(void* vreally, void* vmark, void* sp)
+ {
+ return -1;
+ }
+}
+
diff --git a/symbian/symbianish.h b/symbian/symbianish.h
new file mode 100644
index 0000000000..1aebaf1007
--- /dev/null
+++ b/symbian/symbianish.h
@@ -0,0 +1,209 @@
+/*
+ * symbianish.h
+ *
+ * Copyright (c) Nokia 2004-2005. All rights reserved.
+ * This code is licensed under the same terms as Perl itself.
+ *
+ */
+
+#include "symbian/symbian_port.h"
+
+/*
+ * The following symbols are defined if your operating system supports
+ * functions by that name. All Unixes I know of support them, thus they
+ * are not checked by the configuration script, but are directly defined
+ * here.
+ */
+
+#ifndef PERL_MICRO
+
+/* HAS_IOCTL:
+ * This symbol, if defined, indicates that the ioctl() routine is
+ * available to set I/O characteristics
+ */
+#define HAS_IOCTL / **/
+
+/* HAS_UTIME:
+ * This symbol, if defined, indicates that the routine utime() is
+ * available to update the access and modification times of files.
+ */
+/* #define HAS_UTIME / **/
+
+/* HAS_GROUP
+ * This symbol, if defined, indicates that the getgrnam() and
+ * getgrgid() routines are available to get group entries.
+ * The getgrent() has a separate definition, HAS_GETGRENT.
+ */
+#undef HAS_GROUP /**/
+
+/* HAS_PASSWD
+ * This symbol, if defined, indicates that the getpwnam() and
+ * getpwuid() routines are available to get password entries.
+ * The getpwent() has a separate definition, HAS_GETPWENT.
+ */
+#undef HAS_PASSWD /**/
+
+#undef HAS_KILL
+#undef HAS_WAIT
+
+#endif /* !PERL_MICRO */
+
+/* USEMYBINMODE
+ * This symbol, if defined, indicates that the program should
+ * use the routine my_binmode(FILE *fp, char iotype) to insure
+ * that a file is in "binary" mode -- that is, that no translation
+ * of bytes occurs on read or write operations.
+ */
+#undef USEMYBINMODE
+
+/* Stat_t:
+ * This symbol holds the type used to declare buffers for information
+ * returned by stat(). It's usually just struct stat. It may be necessary
+ * to include <sys/stat.h> and <sys/types.h> to get any typedef'ed
+ * information.
+ */
+#define Stat_t struct stat
+
+/* USE_STAT_RDEV:
+ * This symbol is defined if this system has a stat structure declaring
+ * st_rdev
+ */
+#define USE_STAT_RDEV /**/
+
+/* ACME_MESS:
+ * This symbol, if defined, indicates that error messages should be
+ * should be generated in a format that allows the use of the Acme
+ * GUI/editor's autofind feature.
+ */
+#undef ACME_MESS /**/
+
+/* UNLINK_ALL_VERSIONS:
+ * This symbol, if defined, indicates that the program should arrange
+ * to remove all versions of a file if unlink() is called. This is
+ * probably only relevant for VMS.
+ */
+/* #define UNLINK_ALL_VERSIONS / **/
+
+/* VMS:
+ * This symbol, if defined, indicates that the program is running under
+ * VMS. It is currently automatically set by cpps running under VMS,
+ * and is included here for completeness only.
+ */
+/* #define VMS / **/
+
+/* ALTERNATE_SHEBANG:
+ * This symbol, if defined, contains a "magic" string which may be used
+ * as the first line of a Perl program designed to be executed directly
+ * by name, instead of the standard Unix #!. If ALTERNATE_SHEBANG
+ * begins with a character other then #, then Perl will only treat
+ * it as a command line if it finds the string "perl" in the first
+ * word; otherwise it's treated as the first line of code in the script.
+ * (IOW, Perl won't hand off to another interpreter via an alternate
+ * shebang sequence that might be legal Perl code.)
+ */
+/* #define ALTERNATE_SHEBANG "#!" / **/
+
+#include <signal.h>
+#define ABORT() abort()
+
+/*
+ * fwrite1() should be a routine with the same calling sequence as fwrite(),
+ * but which outputs all of the bytes requested as a single stream (unlike
+ * fwrite() itself, which on some systems outputs several distinct records
+ * if the number_of_items parameter is >1).
+ */
+#define fwrite1 fwrite
+
+#define Stat(fname,bufptr) stat((fname),(bufptr))
+#define Fstat(fd,bufptr) fstat((fd),(bufptr))
+#define Fflush(fp) fflush(fp)
+#define Mkdir(path,mode) mkdir((path),(mode))
+
+#ifndef PERL_SYS_TERM
+#define PERL_SYS_TERM() OP_REFCNT_TERM; MALLOC_TERM; CloseSTDLIB();
+#endif
+
+#define BIT_BUCKET "NUL:"
+
+#define dXSUB_SYS
+
+#define NO_ENVIRON_ARRAY
+
+int kill(pid_t pid, int signo);
+pid_t wait(int *status);
+
+#ifdef PERL_GLOBAL_STRUCT_PRIVATE
+# undef PERL_GET_VARS
+# undef PERL_SET_VARS
+# undef PERL_UNSET_VARS
+# define PERL_GET_VARS() symbian_get_vars()
+# define PERL_SET_VARS(v) symbian_set_vars(v)
+# define PERL_UNSET_VARS(v) symbian_unset_vars()
+#endif /* #ifdef PERL_GLOBAL_STRUCT_PRIVATE */
+
+#undef PERL_EXPORT_C
+#define PERL_EXPORT_C EXPORT_C /* for perlio.h */
+#define PERL_CALLCONV EXPORT_C /* for prototype.h */
+#undef PERL_XS_EXPORT_C
+#define PERL_XS_EXPORT_C EXPORT_C
+
+#ifndef PERL_CORE
+#define PERL_CORE /* for WINS builds under VC */
+#endif
+
+#ifdef USE_PERLIO
+#define PERL_NEED_APPCTX /* need storing the PerlBase* */
+#define PERLIO_STD_SPECIAL
+#define PERLIO_STD_IN(f, b, n) symbian_read_stdin(f, b, n)
+#define PERLIO_STD_OUT(f, b, n) symbian_write_stdout(f, b, n)
+/* The console (the STD*) streams are seen by Perl in UTF-8. */
+#define PERL_SYMBIAN_CONSOLE_UTF8
+
+#endif
+
+#undef Strerror
+#undef strerror
+#define Strerror(eno) ((eno) < 0 ? symbian_get_error_string(eno) : strerror(eno))
+
+#define PERL_NEED_TIMESBASE
+
+#define times(b) symbian_times(b)
+#define usleep(u) symbian_usleep(u)
+
+#define PERL_SYS_INIT(c, v) symbian_sys_init(c, v)
+
+#ifdef __SERIES60_1X__
+# error "Unfortunately Perl does not work in S60 1.2 (see FAQ-0929)"
+#endif
+
+#ifdef _MSC_VER
+
+/* The Symbian SDK insists on the /W4 flag for Visual C.
+ * The Perl sources are not _that_ clean (Perl builds for Win32 use
+ * the /W3 flag, and gcc builds always use -Wall, so the sources are
+ * quite clean). To avoid a flood of warnings let's shut up most
+ * (for VC 6.0 SP 5). */
+
+#pragma warning(disable: 4054) /* function pointer to data pointer */
+#pragma warning(disable: 4055) /* data pointer to function pointer */
+#pragma warning(disable: 4100) /* unreferenced formal parameter */
+#pragma warning(disable: 4101) /* unreferenced local variable */
+#pragma warning(disable: 4102) /* unreferenced label */
+#pragma warning(disable: 4113) /* prototype difference */
+#pragma warning(disable: 4127) /* conditional expression is constant */
+#pragma warning(disable: 4132) /* const object should be initialized */
+#pragma warning(disable: 4133) /* incompatible types */
+#pragma warning(disable: 4189) /* initialized but not referenced */
+#pragma warning(disable: 4244) /* conversion from ... possible loss ... */
+#pragma warning(disable: 4245) /* signed/unsigned char */
+#pragma warning(disable: 4310) /* cast truncates constant value */
+#pragma warning(disable: 4505) /* function has been removed */
+#pragma warning(disable: 4510) /* default constructor could not ... */
+#pragma warning(disable: 4610) /* struct ... can never be instantiated */
+#pragma warning(disable: 4701) /* used without having been initialized */
+#pragma warning(disable: 4702) /* unreachable code */
+#pragma warning(disable: 4706) /* assignment within conditional */
+#pragma warning(disable: 4761) /* integral size mismatch */
+
+#endif /* _MSC_VER */
+
diff --git a/symbian/uid.pl b/symbian/uid.pl
new file mode 100644
index 0000000000..6eae8a9bcb
--- /dev/null
+++ b/symbian/uid.pl
@@ -0,0 +1 @@
+0x102015F3
diff --git a/symbian/version.pl b/symbian/version.pl
new file mode 100644
index 0000000000..c8bb82ebf7
--- /dev/null
+++ b/symbian/version.pl
@@ -0,0 +1,22 @@
+use strict;
+
+my %VERSION;
+
+if (open(PATCHLEVEL_H, "patchlevel.h")) {
+ while (<PATCHLEVEL_H>) {
+ if (/#define\s+PERL_(REVISION|VERSION|SUBVERSION)\s+(\d+)/) {
+ $VERSION{$1} = $2;
+ }
+ }
+ close PATCHLEVEL_H;
+} else {
+ die "$0: patchlevel.h: $!\n";
+}
+
+die "$0: Perl release looks funny.\n"
+ unless (defined $VERSION{REVISION} && $VERSION{REVISION} == 5 &&
+ defined $VERSION{VERSION} && $VERSION{VERSION} >= 8 &&
+ defined $VERSION{SUBVERSION});
+
+
+\%VERSION;
diff --git a/symbian/xsbuild.pl b/symbian/xsbuild.pl
new file mode 100644
index 0000000000..ff743bda79
--- /dev/null
+++ b/symbian/xsbuild.pl
@@ -0,0 +1,861 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use Getopt::Long;
+use File::Basename;
+use Cwd;
+
+do "sanity.pl";
+
+my $CoreBuild = -d "ext" && -f "perl.h" && -d "symbian" && -f "perl.c";
+
+my $SymbianVersion = $ENV{XSBUILD_SYMBIAN_VERSION};
+my $PerlVersion = $ENV{XSBUILD_PERL_VERSION};
+my $CSuffix = '.c';
+my $CPlusPlus;
+my $Config;
+my $Build;
+my $Clean;
+my $DistClean;
+my $Sis;
+
+sub usage {
+ die <<__EOF__;
+$0: Usage: $0 [--symbian=version] [--perl=version]
+ [--csuffix=csuffix] [--cplusplus]
+ [--win=win] [--arm=arm]
+ [--config|--build|--clean|--distclean|--sis] ext
+__EOF__
+}
+
+my $CWD;
+my $SDK;
+my $VERSION;
+my $R_V_SV;
+my $PERLSDK;
+my $WIN;
+my $ARM;
+my $HOME = getcwd();
+
+if ( !defined $PerlVersion && $0 =~ m:\\symbian\\perl\\(.+)\\bin\\xsbuild.pl:i )
+{
+ $PerlVersion = $1;
+}
+
+if ( !defined $SymbianVersion) {
+ ($SymbianVersion) = ($ENV{PATH} =~ m!C:\\Symbian\\(.+?)\\!i);
+}
+
+my $S60SDK;
+
+if ($CoreBuild) {
+ unshift @INC, "symbian";
+ do "sanity.pl";
+ my %VERSION = %{ do "version.pl" };
+ $SDK = do "sdk.pl";
+ $VERSION = "$VERSION{REVISION}$VERSION{VERSION}$VERSION{SUBVERSION}";
+ $R_V_SV = "$VERSION{REVISION}.$VERSION{VERSION}.$VERSION{SUBVERSION}";
+ $HOME = do "cwd.pl";
+ $SymbianVersion = $1 if $SDK =~ m:\\Symbian\\([^\\]+):;
+ $PerlVersion = $R_V_SV;
+ $S60SDK = $ENV{S60SDK}; # from sdk.pl
+}
+
+usage()
+ unless GetOptions(
+ 'symbian=s' => \$SymbianVersion,
+ 'perl=s' => \$PerlVersion,
+ 'csuffix=s' => \$CSuffix,
+ 'cplusplus' => \$CPlusPlus,
+ 'win=s' => \$WIN,
+ 'arm=s' => \$ARM,
+ 'config' => \$Config,
+ 'build' => \$Build,
+ 'clean' => \$Clean,
+ 'distclean' => \$DistClean,
+ 'sis' => \$Sis
+ );
+
+usage() unless @ARGV;
+
+$CSuffix = '.cpp' if $CPlusPlus;
+$Build = !( $Config || $Clean || $DistClean ) || $Sis unless defined $Build;
+
+die "$0: Symbian version undefined\n" unless defined $SymbianVersion;
+
+$SymbianVersion =~ s:/:\\:g;
+
+die "$0: Symbian version '$SymbianVersion' not found\n"
+ unless -d "\\Symbian\\$SymbianVersion";
+
+die "$0: Perl version undefined\n" unless defined $PerlVersion;
+
+die "$0: Perl version '$PerlVersion' not found\n"
+ if !$CoreBuild && !-d "\\Symbian\\Perl\\$PerlVersion";
+
+print "Configuring with Symbian $SymbianVersion and Perl $PerlVersion...\n";
+
+$SDK = "\\Symbian\\$SymbianVersion" unless defined $SDK;
+$PERLSDK = "\\Symbian\\Perl\\$PerlVersion";
+
+$R_V_SV = $PerlVersion;
+
+$VERSION =~ tr/.//d;
+
+$ENV{SDK} = $SDK; # For the Errno extension
+$ENV{CROSS} = 1; # For the Encode extension
+
+my $UREL = $ENV{UREL}; # from sdk.pl
+$UREL =~ s/-ARM-/$ARM/;
+my $UARM = $ENV{UARM}; # from sdk.pl
+my $SRCDBG = $UARM eq 'udeb' ? "SRCDBG" : "";
+
+my %CONF;
+my %EXTCFG;
+
+sub write_bld_inf {
+ my ($base) = @_;
+ print "\tbld.inf\n";
+ open( BLD_INF, ">bld.inf" ) or die "$0: bld.inf: $!\n";
+ print BLD_INF <<__EOF__;
+PRJ_MMPFILES
+$base.mmp
+PRJ_PLATFORMS
+$WIN $ARM
+__EOF__
+ close(BLD_INF);
+}
+
+sub system_echo {
+ my $cmd = shift;
+ print "xsbuild: ", $cmd, "\n";
+ return system($cmd);
+}
+
+sub run_PL {
+ my ( $PL, $dir, $file ) = @_;
+ if ( defined $file ) {
+ print "\t(Running $dir\\$PL to create $file)\n";
+ unlink($file);
+ }
+ else {
+ print "\t(Running $dir\\$PL)\n";
+ }
+ my $cmd;
+ if ($CoreBuild) {
+ # Problem: the Config.pm we have in $HOME\\lib carries the
+ # version number of the Perl we are building, while the Perl
+ # we are running might have some other version. Solution:
+ # temporarily replace the Config.pm with a patched version.
+ my $V = sprintf "%vd", $^V;
+ unlink("$HOME\\lib\\Config.pm.bak");
+ system_echo("perl -pi.bak -e \"s:\\Q$R_V_SV:$V:\" $HOME\\lib\\Config.pm");
+ }
+ system_echo("perl -I$HOME\\lib -I$HOME\\xlib\\symbian $PL") == 0
+ or warn "$0: $PL failed.\n";
+ if ($CoreBuild) {
+ system_echo("copy $HOME\\lib\\Config.pm.bak $HOME\\lib\\Config.pm");
+ }
+ if ( defined $file ) { -s $file or die "$0: No $file created.\n" }
+}
+
+sub read_old_multi {
+ my ( $conf, $k ) = @_;
+ push @{ $conf->{$k} }, split( ' ', $1 ) if /^$k\s(.+)$/;
+}
+
+sub uniquefy_filenames {
+ my $b = [];
+ my %c = ();
+ for my $i (@{$_[0]}) {
+ $i =~ s!/!\\!g;
+ $i = lc $i if $i =~ m!\\!;
+ $i =~ s!^c:!!;
+ push @$b, $i unless $c{$i}++;
+ }
+ return $b;
+}
+
+sub read_mmp {
+ my ( $conf, $mmp ) = @_;
+ if ( -r $mmp && open( MMP, "<$mmp" ) ) {
+ print "\tReading $mmp...\n";
+ while (<MMP>) {
+ chomp;
+ $conf->{TARGET} = $1 if /^TARGET\s+(.+)$/;
+ $conf->{TARGETPATH} = $1 if /^TARGETPATH\s+(.+)$/;
+ $conf->{EXTVERSION} = $1 if /^EXTVERSION\s+(.+)$/;
+ read_old_multi( $conf, "SOURCE" );
+ read_old_multi( $conf, "SOURCEPATH" );
+ read_old_multi( $conf, "USERINCLUDE" );
+ read_old_multi( $conf, "SYSTEMINCLUDE" );
+ read_old_multi( $conf, "LIBRARY" );
+ read_old_multi( $conf, "MACRO" );
+ }
+ close(MMP);
+ }
+}
+
+sub write_mmp {
+ my ( $base, $userinclude, @src ) = @_;
+
+ print "\t$base.mmp\n";
+ $CONF{TARGET} = "$base.dll";
+ $CONF{TARGETPATH} = "\\System\\Libs\\Perl\\$R_V_SV";
+ $CONF{SOURCE} = [@src];
+ $CONF{SOURCEPATH} = [ $CWD, $HOME ];
+ $CONF{USERINCLUDE} = [ $CWD, $HOME ];
+ $CONF{SYSTEMINCLUDE} = ["$PERLSDK\\include"] unless $CoreBuild;
+ $CONF{SYSTEMINCLUDE} = [ $HOME ] if $CoreBuild;
+ $CONF{LIBRARY} = [];
+ $CONF{MACRO} = [];
+ read_mmp( \%CONF, "_init.mmp" );
+ read_mmp( \%CONF, "$base.mmp" );
+
+ for my $ui ( @{$userinclude} ) {
+ $ui =~ s!/!\\!g;
+ if ( $ui =~ m!^(?:[CD]:)?\\! ) {
+ push @{ $CONF{USERINCLUDE} }, $ui;
+ }
+ else {
+ push @{ $CONF{USERINCLUDE} }, "$HOME\\$ui";
+ }
+ }
+ push @{ $CONF{SYSTEMINCLUDE} }, "\\epoc32\\include";
+ push @{ $CONF{SYSTEMINCLUDE} }, "\\epoc32\\include\\libc";
+ push @{ $CONF{LIBRARY} }, "euser.lib";
+ push @{ $CONF{LIBRARY} }, "estlib.lib";
+ push @{ $CONF{LIBRARY} }, "perl$VERSION.lib";
+ push @{ $CONF{MACRO} }, "SYMBIAN" unless $CoreBuild;
+ push @{ $CONF{MACRO} }, "PERL_EXT" if $CoreBuild;
+ push @{ $CONF{MACRO} }, "MULTIPLICITY";
+ push @{ $CONF{MACRO} }, "PERL_IMPLICIT_CONTEXT";
+ push @{ $CONF{MACRO} }, "PERL_GLOBAL_STRUCT";
+ push @{ $CONF{MACRO} }, "PERL_GLOBAL_STRUCT_PRIVATE";
+
+ for my $u (qw(SOURCE SOURCEPATH SYSTEMINCLUDE USERINCLUDE LIBRARY MACRO)) {
+ $CONF{$u} = uniquefy_filenames( $CONF{$u} );
+ }
+ open( BASE_MMP, ">$base.mmp" ) or die "$0: $base.mmp: $!\n";
+
+ print BASE_MMP <<__EOF__;
+TARGET $CONF{TARGET}
+TARGETTYPE dll
+TARGETPATH $CONF{TARGETPATH}
+SOURCE @{$CONF{SOURCE}}
+$SRCDBG
+__EOF__
+ for my $u (qw(SOURCEPATH SYSTEMINCLUDE USERINCLUDE)) {
+ for my $v ( @{ $CONF{$u} } ) {
+ print BASE_MMP "$u\t$v\n";
+ }
+ }
+ # OPTION does not work in MMPs for pre-2.0 SDKs?
+ print BASE_MMP <<__EOF__;
+LIBRARY @{$CONF{LIBRARY}}
+MACRO @{$CONF{MACRO}}
+// OPTION MSVC /P
+// OPTION GCC -E
+__EOF__
+ close(BASE_MMP);
+
+}
+
+sub write_makefile {
+ my ( $base, $build ) = @_;
+
+ print "\tMakefile\n";
+
+ my $windef1 = "$SDK\\Epoc32\\Build$CWD\\$base\\$WIN\\$base.def";
+ my $windef2 = "..\\BWINS\\${base}u.def";
+ my $armdef1 = "$SDK\\Epoc32\\Build$CWD\\$base\\$ARM\\$base.def";
+ my $armdef2 = "..\\BMARM\\${base}u.def";
+
+ my $wrap = $SDK && $S60SDK eq '1.2' && $SDK !~ /_CW$/;
+ my $ABLD = $wrap ? 'perl b.pl' : 'abld';
+
+ open( MAKEFILE, ">Makefile" ) or die "$0: Makefile: $!\n";
+ print MAKEFILE <<__EOF__;
+WIN = $WIN
+ARM = $ARM
+ABLD = $ABLD
+
+all: build freeze
+
+sis: build_arm freeze_arm
+
+build: abld.bat build_win build_arm
+
+abld.bat:
+ bldmake bldfiles
+
+build_win: abld.bat
+ bldmake bldfiles
+ \$(ABLD) build \$(WIN) udeb
+
+build_arm: abld.bat
+ bldmake bldfiles
+ \$(ABLD) build \$(ARM) $UARM
+
+win: build_win freeze_win
+
+arm: build_arm freeze_arm
+
+freeze: freeze_win freeze_arm
+
+freeze_win:
+ bldmake bldfiles
+ \$(ABLD) freeze \$(WIN) $base
+
+freeze_arm:
+ bldmake bldfiles
+ \$(ABLD) freeze \$(ARM) $base
+
+defrost: defrost_win defrost_arm
+
+defrost_win:
+ -del /f $windef1
+ -del /f $windef2
+
+defrost_arm:
+ -del /f $armdef1
+ -del /f $armdef2
+
+clean: clean_win clean_arm
+
+clean_win:
+ \$(ABLD) clean \$(WIN)
+
+clean_arm:
+ \$(ABLD) clean \$(ARM)
+
+realclean: clean realclean_win realclean_arm
+ -del /f _init.c b.pl
+ -del /f $base.c $base.mmp
+
+realclean_win:
+ \$(ABLD) reallyclean \$(WIN)
+
+realclean_arm:
+ \$(ABLD) reallyclean \$(ARM)
+
+distclean: defrost realclean
+ -rmdir ..\\BWINS ..\\BMARM
+ -del /f const-c.inc const-xs.inc
+ -del /f Makefile abld.bat bld.inf
+__EOF__
+ close(MAKEFILE);
+ if ($wrap) {
+ if(open(B,">b.pl")) {
+ print B <<'__EOF__';
+# abld.pl wrapper.
+
+# nmake doesn't like MFLAGS and MAKEFLAGS being set to -w and w.
+delete $ENV{MFLAGS};
+delete $ENV{MAKEFLAGS};
+
+print "abld @ARGV\n";
+system("abld @ARGV");
+__EOF__
+ close(B);
+ } else {
+ warn "$0: failed to create b.pl: $!\n";
+ }
+ }
+}
+
+sub update_dir {
+ print "[chdir from ", getcwd(), " to ";
+ chdir(shift) or return;
+ update_cwd();
+ print getcwd(), "]\n";
+}
+
+sub xsconfig {
+ my ( $ext, $dir ) = @_;
+ print "Configuring for $ext, directory $dir...\n";
+ my $extu = $CoreBuild ? "$HOME\\lib\\ExtUtils" : "$PERLSDK\\lib\\ExtUtils";
+ update_dir($dir) or die "$0: chdir '$dir': $!\n";
+ my $build = dirname($ext);
+ my $base = basename($ext);
+ my $basexs = "$base.xs";
+ my $basepm = "$base.pm";
+ my $basec = "$base$CSuffix";
+ my $extdir = ".";
+ if ( $dir =~ m:^ext\\(.+): ) {
+ $extdir = $1;
+ }
+ elsif ( $dir ne "." ) {
+ $extdir = $dir;
+ }
+ my $extdirdir = dirname($extdir);
+ my $targetroot = "\\System\\Libs\\Perl\\$R_V_SV";
+ write_bld_inf($base) if -f $basexs;
+
+ my %src;
+ $src{$basec}++;
+
+ $extdirdir = $extdirdir eq "." ? "" : "$extdirdir\\";
+
+ my %lst;
+ $lst{"$UREL\\$base.dll"} =
+ "$targetroot\\$ARM-symbian\\$base.dll"
+ if -f $basexs;
+ $lst{"$dir\\$base.pm"} = "$targetroot\\$extdirdir$base.pm"
+ if -f $basepm && $base ne 'XSLoader';
+
+ my %incdir;
+ my $ran_PL;
+ if ( -d 'lib' ) {
+ use File::Find;
+ my @found;
+ find( sub { push @found, $File::Find::name if -f $_ }, 'lib' );
+ for my $found (@found) {
+ my ($short) = ( $found =~ m/^lib.(.+)/ );
+ $short =~ s!/!\\!g;
+ $found =~ s!/!\\!g;
+ $lst{"$dir\\$found"} = "$targetroot\\$short";
+ }
+ }
+ if ( my @pm = glob("*.pm */*.pm") ) {
+ for my $pm (@pm) {
+ next if $pm =~ m:^t/:;
+ $pm =~ s:/:\\:g;
+ $lst{"$dir\\$pm"} = "$targetroot\\$extdirdir$pm";
+ }
+ }
+ if ( my @c = glob("*.c *.cpp */*.c */*.cpp") ) {
+ for my $c (@c) {
+ $c =~ s:/:\\:g;
+ $src{$c}++;
+ }
+ }
+ if ( my @h = glob("*.h */*.h") ) {
+ for my $h (@h) {
+ $h =~ s:/:\\:g;
+ $h = dirname($h);
+ $incdir{"$dir\\$h"}++ unless $h eq ".";
+ }
+ }
+ if ( exists $EXTCFG{$ext} ) {
+ for my $cfg ( @{ $EXTCFG{$ext} } ) {
+ if ( $cfg =~ /^([-+])?(.+\.(c|cpp|h))$/ ) {
+ my $o = defined $1 ? $1 : '+';
+ my $f = $2;
+ $f =~ s:/:\\:g;
+ for my $f ( glob($f) ) {
+ if ( $o eq '+' ) {
+ warn "$0: no source file $dir\\$f\n" unless -f $f;
+ $src{$f}++ unless $cfg =~ /\.h$/;
+ if ( $f =~ m:^(.+)\\[^\\]+$: ) {
+ $incdir{$1}++;
+ }
+ }
+ elsif ( $o eq '-' ) {
+ delete $src{$f};
+ }
+ }
+ }
+ if ( $cfg =~ /^([-+])?(.+\.(pm|pl|inc))$/ ) {
+ my $o = defined $1 ? $1 : '+';
+ my $f = $2;
+ $f =~ s:/:\\:g;
+ for my $f ( glob($f) ) {
+ if ( $o eq '+' ) {
+ warn "$0: no Perl file $dir\\$f\n" unless -f $f;
+ $lst{"$dir\\$f"} = "$targetroot\\$extdir\\$f";
+ }
+ elsif ( $o eq '-' ) {
+ delete $lst{"$dir\\$f"};
+ }
+ }
+ }
+ if ( $cfg eq 'CONST' && !$ran_PL++ ) {
+ run_PL( "Makefile.PL", $dir, "const-xs.inc" );
+ }
+ }
+ }
+ unless ( $ran_PL++ ) {
+ run_PL( "Makefile.PL", $dir ) if -f "Makefile.PL";
+ }
+ if ( $dir eq "ext\\Errno" ) {
+ run_PL( "Errno_pm.PL", $dir, "Errno.pm" );
+ $lst{"$dir\\Errno.pm"} = "$targetroot\\Errno.pm";
+ }
+ elsif ( $dir eq "ext\\Devel\\PPPort" ) {
+ run_PL( "ppport_h.PL", $dir, "ppport.h" );
+ }
+ elsif ( $dir eq "ext\\DynaLoader" ) {
+ run_PL( "XSLoader_pm.PL", $dir, "XSLoader.pm" );
+ $lst{"ext\\DynaLoader\\XSLoader.pm"} = "$targetroot\\XSLoader.pm";
+ }
+ elsif ( $dir eq "ext\\Encode" ) {
+ system_echo("perl bin\\enc2xs -Q -O -o def_t.c -f def_t.fnm") == 0
+ or die "$0: running enc2xs failed: $!\n";
+ }
+
+ my @lst = sort keys %lst;
+
+ read_mmp( \%CONF, "_init.mmp" );
+ read_mmp( \%CONF, "$base.mmp" );
+
+ if ( -f $basexs ) {
+ my %MM; # MakeMaker results
+ my @MM = qw(VERSION XS_VERSION);
+ if ( -f "Makefile" ) {
+ print "\tReading MakeMaker Makefile...\n";
+ if ( open( MAKEFILE, "Makefile" ) ) {
+ while (<MAKEFILE>) {
+ for my $m (@MM) {
+ if (m!^$m = (.+)!) {
+ $MM{$m} = $1;
+ print "\t$m = $1\n";
+ }
+ }
+ }
+ close(MAKEFILE);
+ }
+ else {
+ warn "$0: Makefile: $!";
+ }
+ print "\tDeleting MakeMaker Makefile.\n";
+ unlink("Makefile");
+ }
+
+ unlink($basec);
+ print "\t$basec\n";
+ if ( defined $CONF{EXTVERSION} ) {
+ my $EXTVERSION = $CONF{EXTVERSION};
+ print "\tUsing $EXTVERSION for version...\n";
+ $MM{VERSION} = $MM{XS_VERSION} = $EXTVERSION;
+ }
+ die "VERSION or XS_VERSION undefined\n"
+ unless defined $MM{VERSION} && defined $MM{XS_VERSION};
+ if ( open( BASE_C, ">$basec" ) ) {
+ print BASE_C <<__EOF__;
+#ifndef VERSION
+#define VERSION "$MM{VERSION}"
+#endif
+#ifndef XS_VERSION
+#define XS_VERSION "$MM{XS_VERSION}"
+#endif
+__EOF__
+ close(BASE_C);
+ }
+ else {
+ warn "$0: $basec: $!";
+ }
+ unless (
+ system(
+"perl -I$PERLSDK\\lib $extu\\xsubpp -C++ -csuffix .cpp -typemap $extu\\typemap -noprototypes $basexs >> $basec"
+ ) == 0
+ && -s $basec
+ )
+ {
+ die "$0: perl xsubpp failed: $!\n";
+ }
+
+ print "\t_init.c\n";
+ open( _INIT_C, ">_init.c" ) or die "$!: _init.c: $!\n";
+ print _INIT_C <<__EOF__;
+ #include "EXTERN.h"
+ #include "perl.h"
+ EXPORT_C void _init(void *handle) {
+ }
+__EOF__
+ close(_INIT_C);
+
+ my @src = ( "_init.c", sort keys %src );
+
+ if ( $base eq "Encode" ) { # Currently unused.
+ for my $submf ( glob("*/Makefile") ) {
+ my $d = dirname($submf);
+ print "Configuring Encode::$d...\n";
+ if ( open( SUBMF, $submf ) ) {
+ if ( update_dir($d) ) {
+ my @subsrc;
+ while (<SUBMF>) {
+ next if 1 .. /postamble/;
+ if (m!^(\w+_t)\.c : !) {
+ system(
+ "perl ..\\bin\\enc2xs -Q -o $1.c -f $1.fnm")
+ == 0
+ or warn "$0: enc2xs: $!\n";
+ push @subsrc, "$1.c";
+ }
+ }
+ close(SUBMF);
+ unlink($submf);
+ my $subbase = $d;
+ $subbase =~ s!/!::!g;
+ write_mmp( $subbase, ["..\\Encode"], "$subbase.c",
+ @subsrc );
+ write_makefile( $subbase, $build );
+ write_bld_inf($subbase);
+
+ unless (
+ system(
+"perl -I$HOME\\lib ..\\$extu\\xsubpp -C++ -csuffix .cpp -typemap ..\\$extu\\typemap -noprototypes $subbase.xs > $subbase.c"
+ ) == 0
+ && -s "$subbase.c"
+ )
+ {
+ die "$0: perl xsubpp failed: $!\n";
+ }
+ update_dir("..");
+ }
+ else {
+ warn "$0: chdir $d: $!\n";
+ }
+ }
+ else {
+ warn "$0: $submf: $!";
+ }
+ }
+ print "Configuring Encode...\n";
+ }
+
+ write_mmp( $base, [ keys %incdir ], @src );
+ write_makefile( $base, $build );
+ }
+ my $lstname = $ext;
+ $lstname =~ s:^ext\\::;
+ $lstname =~ s:\\:-:g;
+ print "\t$lstname.lst\n";
+ my $lstout =
+ $CoreBuild ? "$HOME/symbian/$lstname.lst" : "$HOME/$lstname.lst";
+ if ( open( my $lst, ">$lstout" ) ) {
+ for my $f (@lst) { print $lst qq["$f"-"!:$lst{$f}"\n] }
+ close($lst);
+ }
+ else {
+ die "$0: $lstout: $!\n";
+ }
+ update_dir($HOME);
+}
+
+sub update_cwd {
+ $CWD = getcwd();
+ $CWD =~ s!^[CD]:!!i;
+ $CWD =~ s!/!\\!g;
+}
+
+for my $ext (@ARGV) {
+
+ $ext =~ s!::!\\!g;
+ $ext =~ s!/!\\!g;
+
+ my $cfg;
+
+ $cfg = $2 if $ext =~ s/(.+?),(.+)/$1/;
+
+ my $dir;
+
+ unless ( -e $ext ) {
+ if ( $ext =~ /\.xs$/ && !-f $ext ) {
+ if ( -f "ext\\$ext" ) {
+ $ext = "ext\\$ext";
+ $dir = dirname($ext);
+ }
+ }
+ elsif ( !-d $ext ) {
+ if ( -d "ext\\$ext" ) {
+ $ext = "ext\\$ext";
+ $dir = $ext;
+ }
+ }
+ $dir = "." unless defined $dir;
+ }
+ else {
+ if ( $ext =~ /\.xs$/ && -f $ext ) {
+ $ext = dirname($ext);
+ $dir = $ext;
+ }
+ elsif ( -d $ext ) {
+ $dir = $ext;
+ }
+ }
+
+ if ( $ext eq "XSLoader" ) {
+ $ext = "ext\\XSLoader";
+ }
+ if ( $ext eq "ext\\XSLoader" ) {
+ $dir = "ext\\DynaLoader";
+ }
+
+ $EXTCFG{$ext} = [ split( /,/, $cfg ) ] if defined $cfg;
+
+ die "$0: no lib\\Config.pm\n"
+ if $CoreBuild && $Build && !-f "lib\\Config.pm";
+
+ if ($CoreBuild) {
+ open( my $cfg, "symbian/install.cfg" )
+ or die "$0: symbian/install.cfg: $!\n";
+ my $extdir = $dir;
+ $extdir =~ s:^ext\\::;
+ while (<$cfg>) {
+ next unless /^ext\s+(.+)/;
+ chomp;
+ my $ext = $1;
+ my @ext = split( ' ', $ext );
+ $EXTCFG{"ext\\$ext[0]"} = [@ext];
+ }
+ close($cfg);
+ }
+
+ if ( $Config || $Build ) {
+ xsconfig( $ext, $dir ) or die "$0: xsconfig '$ext' failed\n";
+ next if $Config;
+ }
+
+ my $chdir = $ext eq "ext\\XSLoader" ? "ext\\DynaLoader" : $dir;
+ die "$0: no directory '$chdir'\n" unless -d $chdir;
+ update_dir($chdir) or die "$0: chdir '$chdir' failed: $!\n";
+
+ my %CONF;
+
+ my @ext = split( /\\/, $ext );
+ my $base = $ext[-1];
+
+ if ( $Clean || $DistClean ) {
+ print "Cleaning $ext...\n";
+ unlink("bld.inf");
+ unlink("$base.mmp");
+ unlink("_init.c");
+ unlink("const-c.inc");
+ unlink("const-xs.inc");
+ rmdir("..\\bmarm");
+ }
+
+ if ( $Build && $ext ne "ext\\XSLoader" && $ext ne "ext\\Errno" ) {
+
+ # We compile the extension three (3) times.
+ # (1) Only the _init.c to get _init() as the ordinal 1 function in the DLL.
+ # (2) With the rest and the _init.c to get ordinals for the rest.
+ # (3) With an updated _init.c that carries the symbols from step (2).
+
+ system("make clean");
+ system("make defrost") == 0 or die "$0: make defrost failed\n";
+
+ my @TARGET;
+
+ push @TARGET, 'sis' if $Sis;
+
+ # Compile #1.
+ # Hide all but the _init.c.
+ print "\n*** $ext - Compile 1 of 3.\n\n";
+ system(
+"perl -pi.bak -e \"s:^SOURCE\\s+_init.c:SOURCE\\t_init.c // :\" $base.mmp"
+ );
+ system("bldmake bldfiles");
+ system("make @TARGET") == 0 or die "$0: make #1 failed\n";
+
+ # Compile #2.
+ # Reveal the rest again.
+ print "\n*** $ext - Compile 2 of 3.\n\n";
+ system(
+"perl -pi.bak -e \"s:^SOURCE\\t_init.c // :SOURCE\\t_init.c :\" $base.mmp"
+ );
+ system("make @TARGET") == 0 or die "$0: make #2 failed\n";
+ unlink("$base.mmp.bak");
+
+ open( _INIT_C, ">_init.c" ) or die "$0: _init.c: $!\n";
+ print _INIT_C <<'__EOF__';
+#include "EXTERN.h"
+#include "perl.h"
+
+/* This is a different but matching definition from in dl_symbian.xs. */
+typedef struct {
+ void* handle;
+ int error;
+ HV* symbols;
+} PerlSymbianLibHandle;
+
+EXPORT_C void _init(void* handle) {
+__EOF__
+
+ my %symbol;
+ my $def;
+ my $basef;
+ for my $f ("$SDK\\Epoc32\\Build$CWD\\$base\\WINS\\$base.def",
+ "..\\BMARM\\${base}u.def") {
+ print "\t($f - ";
+ if ( open( $def, $f ) ) {
+ print "OK)\n";
+ $basef = $f;
+ last;
+ } else {
+ print "no)\n";
+ }
+ }
+ unless (defined $basef) {
+ die "$0: failed to find .def for $base\n";
+ }
+ while (<$def>) {
+ next while 1 .. /^EXPORTS/;
+ if (/^\s*(\w+) \@ (\d+) /) {
+ $symbol{$1} = $2;
+ }
+ }
+ close($def);
+
+ my @symbol = sort keys %symbol;
+ if (@symbol) {
+ print _INIT_C <<'__EOF__';
+ dTHX;
+ PerlSymbianLibHandle* h = (PerlSymbianLibHandle*)handle;
+ if (!h->symbols)
+ h->symbols = newHV();
+ if (h->symbols) {
+__EOF__
+ for my $sym (@symbol) {
+ my $len = length($sym);
+ print _INIT_C <<__EOF__;
+ hv_store(h->symbols, "$sym", $len, newSViv($symbol{$sym}), 0);
+__EOF__
+ }
+ }
+ else {
+ die "$0: $basef: no exports found\n";
+ }
+
+ print _INIT_C <<'__EOF__';
+ }
+}
+__EOF__
+ close(_INIT_C);
+
+ # Compile #3. This is for real.
+ print "\n*** $ext - Compile 3 of 3.\n\n";
+ system("make @TARGET") == 0 or die "$0: make #3 failed\n";
+
+ }
+ elsif ( $Clean || $DistClean ) {
+ if ( $ext eq "ext\\Errno" ) {
+ unlink( "Errno.pm", "Makefile" );
+ }
+ else {
+ if ( -f "Makefile" ) {
+ if ($Clean) {
+ system("make clean") == 0 or die "$0: make clean failed\n";
+ }
+ elsif ($DistClean) {
+ system("make distclean") == 0
+ or die "$0: make distclean failed\n";
+ }
+ }
+ if ( $ext eq "ext\\Devel\\PPPort" ) {
+ unlink("ppport.h");
+ }
+ }
+ my @B = glob("ext/BWINS ext/BMARM ext/*/BWINS ext/*/BMARM Makefile");
+ rmdir(@B) if @B;
+ }
+
+ update_dir($HOME);
+
+} # for my $ext
+
+exit(0);
+