426 lines
11 KiB
C++
426 lines
11 KiB
C++
/* 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()
|
|
{
|
|
dTHX;
|
|
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::NewInterpreter(TBool aCloseStdlib,
|
|
void (*aStdioInitFunc)(void*),
|
|
void *aStdioInitCookie)
|
|
{
|
|
CPerlBase* self = new (ELeave) CPerlBase;
|
|
self->iCloseStdlib = aCloseStdlib;
|
|
self->iStdioInitFunc = aStdioInitFunc;
|
|
self->iStdioInitCookie = aStdioInitCookie;
|
|
self->ConstructL();
|
|
PERL_APPCTX_SET(self);
|
|
return self;
|
|
}
|
|
|
|
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_buf((U8*)(pUtf8 + i),
|
|
(U8*)(pUtf8 + nUtf8),
|
|
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 = valid_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;
|
|
}
|
|
|