/* * 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 #include #include #include #include #include #include #include "PerlUi.h" #include "PerlBase.h" #include "PerlUtil.h" #include "EXTERN.h" #include "perl.h" #include "XSUB.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; } XS(XS_PerlApp_TextQuery) // Can't be made static because of XS(). { dXSARGS; if (items != 0) Perl_croak(aTHX_ "PerlApp::TextQuery: no arguments, please"); SP -= items; // TODO: parse arguments for title, prompt, and maxsize. // Suggested syntax: // TextQuery(title => ..., prompt => ..., maxsize => ...) // For an example see e.g. universal.c:XS_PerlIO_get_layers(). _LIT(KTitle, "Title"); _LIT(KPrompt, "Prompt"); HBufC* cData = HBufC::New(KPerlUiOneLinerSize); TBool cSuccess = EFalse; if (cData) { TPtr cPtr(cData->Des()); if (CPerlUi::TextQueryDialogL(KTitle, KPrompt, cPtr, KPerlUiOneLinerSize)) { ST(0) = sv_2mortal(PerlUtil::newSvPVfromTDesC16(*cData)); cSuccess = ETrue; } delete cData; } if (cSuccess) XSRETURN(1); else XSRETURN_UNDEF; } EXPORT_C void init_os_extras(void) { dTHX; char *file = __FILE__; dXSUB_SYS; newXS("PerlApp::TextQuery", XS_PerlApp_TextQuery, file); } 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(TInt error) { // CTextResolver seems to be unreliable, so we roll our own // at least for the basic Symbian errors (this does not cover // the various subsystems). dTHX; if (error >= 0) return strerror(error); error = -error; // flip const TInt KErrStringMax = 256; typedef struct { const char* kerr; const char* desc; } kerritem; static const kerritem kerrtable[] = { { "None", /* 0 */ "No error"}, { "NotFound", /* -1 */ "Unable to find the specified object"}, { "General", /* -2 */ "General (unspecified) error"}, { "Cancel", /* -3 */ "The operation was cancelled"}, { "NoMemory", /* -4 */ "Not enough memory"}, { "NotSupported", /* -5 */ "The operation requested is not supported"}, { "Argument", /* -6 */ "Bad request"}, { "TotalLossOfPrecision", /* -7 */ "Total loss of precision"}, { "BadHandle", /* -8 */ "Bad object"}, { "Overflow", /* -9 */ "Overflow"}, { "Underflow", /* -10 */ "Underflow"}, { "AlreadyExists", /* -11 */ "Already exists"}, { "PathNotFound", /* -12 */ "Unable to find the specified folder"}, { "Died", /* -13 */ "Closed"}, { "InUse", /* -14 */ "The specified object is currently in use by another program"}, { "ServerTerminated", /* -15 */ "Server has closed"}, { "ServerBusy", /* -16 */ "Server busy"}, { "Completion", /* -17 */ "Completion error"}, { "NotReady", /* -18 */ "Not ready"}, { "Unknown", /* -19 */ "Unknown error"}, { "Corrupt", /* -20 */ "Corrupt"}, { "AccessDenied", /* -21 */ "Access denied"}, { "Locked", /* -22 */ "Locked"}, { "Write", /* -23 */ "Failed to write"}, { "DisMounted", /* -24 */ "Wrong disk present"}, { "Eof", /* -25 */ "Unexpected end of file"}, { "DiskFull", /* -26 */ "Disk full"}, { "BadDriver", /* -27 */ "Bad device driver"}, { "BadName", /* -28 */ "Bad name"}, { "CommsLineFail", /* -29 */ "Comms line failed"}, { "CommsFrame", /* -30 */ "Comms frame error"}, { "CommsOverrun", /* -31 */ "Comms overrun error"}, { "CommsParity", /* -32 */ "Comms parity error"}, { "TimedOut", /* -33 */ "Timed out"}, { "CouldNotConnect",/* -34 */ "Failed to connect"}, { "CouldNotDisconnect", /* -35 */ "Failed to disconnect"}, { "Disconnected", /* -36 */ "Disconnected"}, { "BadLibraryEntryPoint", /* -37 */ "Bad library entry point"}, { "BadDescriptor", /* -38 */ "Bad descriptor"}, { "Abort", /* -39 */ "Interrupted"}, { "TooBig", /* -40 */ "Too big"}, { "DivideByZero", /* -41 */ "Divide by zero"}, { "BadPower", /* -42 */ "Batteries too low"}, { "DirFull", /* -43 */ "Folder full"}, { "KErrHardwareNotAvailable", /* -44 */ "Hardware is not available"}, { "SessionClosed", /* -45 */ "Session was closed"}, { "PermissionDenied", /* -46 */ "Permission denied"} }; const TInt n = sizeof(kerrtable) / sizeof(kerritem *); TBuf8 buf8; if (error >= 0 && error < n) { const char *kerr = kerrtable[error].kerr; const char *desc = kerrtable[error].desc; const TPtrC8 kerrp((const unsigned char *)kerr, strlen(kerr)); const TPtrC8 descp((const unsigned char *)desc, strlen(desc)); TBuf8 ckerr; TBuf8 cdesc; ckerr.Copy(kerrp); cdesc.Copy(descp); buf8.Format(_L8("K%S (%d) %S"), &ckerr, error, &cdesc); } else { buf8.Format(_L8("Symbian error %d"), error); } SV* sv = Perl_get_sv(aTHX_ "\005", TRUE); /* $^E or ${^OS_ERROR} */ if (!sv) return (char*)NullErr; sv_setpv(sv, (const char *)buf8.PtrZ()); 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 CProcessWait : public CActive { public: CProcessWait() : 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(); } }; 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) { CProcessWait* w = new CProcessWait(); 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 aFilename; TBuf 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; } }