1 /*
2  *      symbian_utils.cpp
3  *
4  *      Copyright (c) Nokia 2004-2005.  All rights reserved.
5  *      This code is licensed under the same terms as Perl itself.
6  *
7  */
8 
9 #define SYMBIAN_UTILS_CPP
10 #include <e32base.h>
11 #include <e32std.h>
12 #include <utf.h>
13 #include <hal.h>
14 
15 #include <eikenv.h>
16 
17 #include <string.h>
18 #include <ctype.h>
19 
20 #include "PerlUi.h"
21 #include "PerlBase.h"
22 #include "PerlUtil.h"
23 
24 #include "EXTERN.h"
25 #include "perl.h"
26 #include "XSUB.h"
27 
28 extern "C" {
symbian_sys_init(int * argcp,char *** argvp)29     EXPORT_C int symbian_sys_init(int *argcp, char ***argvp)
30     {
31 #ifdef PERL_GLOBAL_STRUCT /* Avoid unused variable warning. */
32         dVAR;
33 #endif
34         (void)times(&PL_timesbase);
35         return 0;
36     }
XS(XS_PerlApp_TextQuery)37     XS(XS_PerlApp_TextQuery) // Can't be made static because of XS().
38     {
39         dXSARGS;
40 	if (items != 0)
41 	    Perl_croak(aTHX_ "PerlApp::TextQuery: no arguments, please");
42 	SP -= items;
43 	// TODO: parse arguments for title, prompt, and maxsize.
44 	// Suggested syntax:
45 	// TextQuery(title => ..., prompt => ..., maxsize => ...)
46 	// For an example see e.g. universal.c:XS_PerlIO_get_layers().
47 	_LIT(KTitle,  "Title");
48 	_LIT(KPrompt, "Prompt");
49         HBufC* cData = HBufC::New(KPerlUiOneLinerSize);
50 	TBool cSuccess = EFalse;
51 	if (cData) {
52 	    TPtr cPtr(cData->Des());
53 	    if (CPerlUi::TextQueryDialogL(KTitle,
54 					  KPrompt,
55 					  cPtr,
56 					  KPerlUiOneLinerSize)) {
57 	        ST(0) = sv_2mortal(PerlUtil::newSvPVfromTDesC16(*cData));
58 		cSuccess = ETrue;
59 	    }
60 	    delete cData;
61 	}
62 	if (cSuccess)
63 	    XSRETURN(1);
64 	else
65 	    XSRETURN_UNDEF;
66     }
init_os_extras(void)67     EXPORT_C void init_os_extras(void)
68     {
69         dTHX;
70 	char *file = __FILE__;
71 	dXSUB_SYS;
72 	newXS("PerlApp::TextQuery", XS_PerlApp_TextQuery, file);
73     }
symbian_read_stdin(const int fd,char * b,int n)74     EXPORT_C SSize_t symbian_read_stdin(const int fd, char *b, int n)
75     {
76 #ifdef PERL_GLOBAL_STRUCT /* Avoid unused variable warning. */
77         dVAR;
78 #endif
79         if(!PL_appctx)
80         	((CPerlBase*)PL_appctx) = CPerlBase::NewInterpreter();
81         return ((CPerlBase*)PL_appctx)->ConsoleRead(fd, b, n);
82     }
symbian_write_stdout(const int fd,const char * b,int n)83     EXPORT_C SSize_t symbian_write_stdout(const int fd, const char *b, int n)
84     {
85 #ifdef PERL_GLOBAL_STRUCT /* Avoid unused variable warning. */
86         dVAR;
87 #endif
88         if(!PL_appctx)
89         	((CPerlBase*)PL_appctx) = CPerlBase::NewInterpreter();
90         return ((CPerlBase*)PL_appctx)->ConsoleWrite(fd, b, n);
91     }
92     static const char NullErr[] = "";
symbian_get_error_string(TInt error)93     EXPORT_C char* symbian_get_error_string(TInt error)
94     {
95 	// CTextResolver seems to be unreliable, so we roll our own
96         // at least for the basic Symbian errors (this does not cover
97         // the various subsystems).
98         dTHX;
99         if (error >= 0)
100             return strerror(error);
101 	error = -error; // flip
102 	const TInt KErrStringMax = 256;
103 	typedef struct {
104 	  const char* kerr;
105 	  const char* desc;
106 	} kerritem;
107 	static const kerritem kerrtable[] = {
108 	  { "None",           /*    0 */ "No error"},
109 	  { "NotFound",       /*   -1 */ "Unable to find the specified object"},
110 	  { "General",        /*   -2 */ "General (unspecified) error"},
111 	  { "Cancel",         /*   -3 */ "The operation was cancelled"},
112 	  { "NoMemory",       /*   -4 */ "Not enough memory"},
113 	  { "NotSupported",   /*   -5 */ "The operation requested is not supported"},
114 	  { "Argument",       /*   -6 */ "Bad request"},
115 	  { "TotalLossOfPrecision",
116 	                      /*   -7 */ "Total loss of precision"},
117 	  { "BadHandle",      /*   -8 */ "Bad object"},
118 	  { "Overflow",       /*   -9 */ "Overflow"},
119 	  { "Underflow",      /*  -10 */ "Underflow"},
120 	  { "AlreadyExists",  /*  -11 */ "Already exists"},
121 	  { "PathNotFound",   /*  -12 */ "Unable to find the specified folder"},
122 	  { "Died",           /*  -13 */ "Closed"},
123 	  { "InUse",          /*  -14 */
124 	    "The specified object is currently in use by another program"},
125 	  { "ServerTerminated",       /*  -15 */ "Server has closed"},
126 	  { "ServerBusy",     /*  -16 */ "Server busy"},
127 	  { "Completion",     /*  -17 */ "Completion error"},
128 	  { "NotReady",       /*  -18 */ "Not ready"},
129 	  { "Unknown",        /*  -19 */ "Unknown error"},
130 	  { "Corrupt",        /*  -20 */ "Corrupt"},
131 	  { "AccessDenied",   /*  -21 */ "Access denied"},
132 	  { "Locked",         /*  -22 */ "Locked"},
133 	  { "Write",          /*  -23 */ "Failed to write"},
134 	  { "DisMounted",     /*  -24 */ "Wrong disk present"},
135 	  { "Eof",            /*  -25 */ "Unexpected end of file"},
136 	  { "DiskFull",       /*  -26 */ "Disk full"},
137 	  { "BadDriver",      /*  -27 */ "Bad device driver"},
138 	  { "BadName",        /*  -28 */ "Bad name"},
139 	  { "CommsLineFail",  /*  -29 */ "Comms line failed"},
140 	  { "CommsFrame",     /*  -30 */ "Comms frame error"},
141 	  { "CommsOverrun",   /*  -31 */ "Comms overrun error"},
142 	  { "CommsParity",    /*  -32 */ "Comms parity error"},
143 	  { "TimedOut",       /*  -33 */ "Timed out"},
144 	  { "CouldNotConnect",/*  -34 */ "Failed to connect"},
145 	  { "CouldNotDisconnect",
146 	                      /* -35 */ "Failed to disconnect"},
147 	  { "Disconnected",   /* -36 */ "Disconnected"},
148 	  { "BadLibraryEntryPoint",
149 	                      /*  -37 */ "Bad library entry point"},
150 	  { "BadDescriptor",  /*  -38 */ "Bad descriptor"},
151 	  { "Abort",          /*  -39 */ "Interrupted"},
152 	  { "TooBig",         /*  -40 */ "Too big"},
153 	  { "DivideByZero",   /*  -41 */ "Divide by zero"},
154 	  { "BadPower",       /*  -42 */ "Batteries too low"},
155 	  { "DirFull",        /*  -43 */ "Folder full"},
156 	  { "KErrHardwareNotAvailable",
157 	                      /*  -44 */ "Hardware is not available"},
158 	  { "SessionClosed",  /*  -45 */ "Session was closed"},
159 	  { "PermissionDenied",
160 	                      /*  -46 */ "Permission denied"}
161 	};
162 	const TInt n = sizeof(kerrtable) / sizeof(kerritem *);
163 	TBuf8<KErrStringMax> buf8;
164 	if (error >= 0 && error < n) {
165 	  const char *kerr = kerrtable[error].kerr;
166 	  const char *desc = kerrtable[error].desc;
167 	  const TPtrC8 kerrp((const unsigned char *)kerr, strlen(kerr));
168 	  const TPtrC8 descp((const unsigned char *)desc, strlen(desc));
169 	  TBuf8<KErrStringMax> ckerr;
170 	  TBuf8<KErrStringMax> cdesc;
171 	  ckerr.Copy(kerrp);
172 	  cdesc.Copy(descp);
173 	  buf8.Format(_L8("K%S (%d) %S"), &ckerr, error, &cdesc);
174 
175 	} else {
176 	  buf8.Format(_L8("Symbian error %d"), error);
177 	}
178         SV* sv = Perl_get_sv(aTHX_ "\005", TRUE); /* $^E or ${^OS_ERROR} */
179         if (!sv)
180             return (char*)NullErr;
181         sv_setpv(sv, (const char *)buf8.PtrZ());
182         return SvPV_nolen(sv);
183     }
symbian_sleep_usec(const long usec)184     EXPORT_C void symbian_sleep_usec(const long usec)
185     {
186         User::After((TTimeIntervalMicroSeconds32) usec);
187     }
188 #define PERL_SYMBIAN_CLK_TCK 100
symbian_get_cpu_time(long * sec,long * usec)189     EXPORT_C int symbian_get_cpu_time(long* sec, long* usec)
190     {
191         // The RThread().GetCpuTime() does not seem to work?
192         // (it always returns KErrNotSupported)
193         // TTimeIntervalMicroSeconds ti;
194         // TInt err = me.GetCpuTime(ti);
195         dTHX;
196         TInt periodus; /* tick period in microseconds */
197         if (HAL::Get(HALData::ESystemTickPeriod, periodus) != KErrNone)
198             return -1;
199         TUint  tick   = User::TickCount();
200         if (PL_timesbase.tms_utime == 0) {
201             PL_timesbase.tms_utime = tick;
202             PL_clocktick = PERL_SYMBIAN_CLK_TCK;
203         }
204         tick -= PL_timesbase.tms_utime;
205         TInt64 tickus = TInt64(tick) * TInt64(periodus);
206         TInt64 tmps   = tickus / 1000000;
207 #ifdef __SERIES60_3X__
208         if (sec)  *sec  = I64LOW(tmps);
209         if (usec) *usec = I64LOW(tickus) - I64LOW(tmps) * 1000000;
210 #else
211         if (sec)  *sec  = tmps.Low();
212         if (usec) *usec = tickus.Low() - tmps.Low() * 1000000;
213 #endif //__SERIES60_3X__
214         return 0;
215     }
symbian_usleep(unsigned int usec)216     EXPORT_C int symbian_usleep(unsigned int usec)
217     {
218         if (usec >= 1000000) {
219             errno = EINVAL;
220             return -1;
221         }
222         symbian_sleep_usec((const long) usec);
223         return 0;
224     }
225 #define SEC_USEC_TO_CLK_TCK(s, u) \
226         (((s) * PERL_SYMBIAN_CLK_TCK) + (u / (1000000 / PERL_SYMBIAN_CLK_TCK)))
symbian_times(struct tms * tmsbuf)227     EXPORT_C clock_t symbian_times(struct tms *tmsbuf)
228     {
229         long s, u;
230         if (symbian_get_cpu_time(&s, &u) == -1) {
231             errno = EINVAL;
232             return -1;
233         } else {
234             tmsbuf->tms_utime  = SEC_USEC_TO_CLK_TCK(s, u);
235             tmsbuf->tms_stime  = 0;
236             tmsbuf->tms_cutime = 0;
237             tmsbuf->tms_cstime = 0;
238             return tmsbuf->tms_utime;
239         }
240     }
241     class CProcessWait : public CActive
242     {
243     public:
CProcessWait()244         CProcessWait() : CActive(EPriorityStandard) {
245           CActiveScheduler::Add(this);
246         }
247 #ifdef __WINS__
Wait(RThread & aProcess)248         TInt Wait(RThread& aProcess)
249 #else
250         TInt Wait(RProcess& aProcess)
251 #endif
252         {
253             aProcess.Logon(iStatus);
254             aProcess.Resume();
255             SetActive();
256             CActiveScheduler::Start();
257             return iStatus.Int();
258         }
259     private:
DoCancel()260       void DoCancel() {;}
RunL()261       void RunL() {
262           CActiveScheduler::Stop();
263       }
264     };
265     class CSpawnIoRedirect : public CBase
266     {
267     public:
268         CSpawnIoRedirect();
269         // NOTE: there is no real implementation of I/O redirection yet.
270     protected:
271     private:
272     };
CSpawnIoRedirect()273     CSpawnIoRedirect::CSpawnIoRedirect()
274     {
275     }
276     typedef enum {
277         ESpawnNone = 0x00000000,
278         ESpawnWait = 0x00000001
279     } TSpawnFlag;
symbian_spawn(const TDesC & aFilename,const TDesC & aCommand,const TSpawnFlag aFlag,const CSpawnIoRedirect & aIoRedirect)280     static int symbian_spawn(const TDesC& aFilename,
281                              const TDesC& aCommand,
282                              const TSpawnFlag aFlag,
283                              const CSpawnIoRedirect& aIoRedirect) {
284         TInt error = KErrNone;
285 #ifdef __WINS__
286         const TInt KStackSize = 0x1000;
287         const TInt KHeapMin   = 0x1000;
288         const TInt KHeapMax   = 0x100000;
289         RThread proc;
290         RLibrary lib;
291         HBufC* command = aCommand.Alloc();
292         error = lib.Load(aFilename);
293         if (error == KErrNone) {
294             TThreadFunction func = (TThreadFunction)(lib.Lookup(1));
295             if (func)
296                 error = proc.Create(aFilename,
297                                     func,
298                                     KStackSize,
299 #ifdef __SERIES60_3X__
300                                     KHeapMin,
301                                     KHeapMax,
302                                     (TAny*)command,
303 #else
304                                     (TAny*)command,
305                                     &lib,
306                                     RThread().Heap(),
307                                     KHeapMin,
308                                     KHeapMax,
309 #endif
310                                     EOwnerProcess);
311             else
312                 error = KErrNotFound;
313             lib.Close();
314         }
315         else
316             delete command;
317 #else
318         RProcess proc;
319         error = proc.Create(aFilename, aCommand);
320 #endif
321         if (error == KErrNone) {
322             if ((TInt)aFlag & (TInt)ESpawnWait) {
323               CProcessWait* w = new CProcessWait();
324               if (w) {
325                   error = w->Wait(proc);
326                   delete w;
327               } else
328                   error = KErrNoMemory;
329             } else
330                 proc.Resume();
331             proc.Close();
332         }
333         return error;
334     }
symbian_spawner(const char * command,TSpawnFlag aFlags)335     static int symbian_spawner(const char *command, TSpawnFlag aFlags)
336      {
337         TBuf<KMaxFileName> aFilename;
338         TBuf<KMaxFileName> aCommand;
339         TSpawnFlag aSpawnFlags = ESpawnWait;
340         CSpawnIoRedirect iord;
341         char *p = (char*)command;
342 
343         // The recognized syntax is: "cmd [args] [&]".  Since one
344         // cannot pass more than (an argv[0] and) an argv[1] to a
345         // Symbian process anyway, not much is done to the cmd or
346         // the args, only backslash quoting.
347 
348         // Strip leading whitespace.
349         while (*p && isspace(*p)) p++;
350         if (*p) {
351             // Build argv[0].
352             while (*p && !isspace(*p) && *p != '&') {
353                 if (*p == '\\') {
354                     if (p[1]) {
355                         aFilename.Append(p[1]);
356                         p++;
357                     }
358 
359                 }
360                 else
361                     aFilename.Append(*p);
362                 p++;
363             }
364 
365             if (*p) {
366                 // Skip whitespace between argv[0] and argv[1].
367                 while(*p && isspace(*p)) p++;
368                 // Build argv[1].
369                 if (*p) {
370                     char *a = p;
371                     char *b = p + 1;
372 
373                     while (*b) b++;
374                     if (isspace(b[-1])) {
375                         b--;
376                         while (b > a && isspace(*b)) b--;
377                         b++;
378                     }
379                     if (b > a && b[-1] == '&') {
380                         // Parse backgrounding in any case,
381                         // but turn it off only if wanted.
382                         if ((aFlags & ESpawnWait))
383                           aSpawnFlags =
384                             (TSpawnFlag) (aSpawnFlags & ~ESpawnWait);
385                         b--;
386                         if (isspace(b[-1])) {
387                             b--;
388                             while (b > a && isspace(*b)) b--;
389                             b++;
390                         }
391                     }
392                     for (p = a; p < b; p++) {
393                         if (*p == '\\') {
394                             if (p[1])
395                                 aCommand.Append(p[1]);
396                             p++;
397                         }
398                         else
399                             aCommand.Append(*p);
400                     }
401                 }
402                 // NOTE: I/O redirection is not yet done.
403                 // Implementing that may require a separate server.
404             }
405         }
406         int spawned = symbian_spawn(aFilename, aCommand, aSpawnFlags, iord);
407         return spawned == KErrNone ? 0 : -1;
408     }
symbian_do_spawn(const char * command)409     EXPORT_C int symbian_do_spawn(const char *command)
410     {
411         return symbian_spawner(command, ESpawnWait);
412     }
symbian_do_spawn_nowait(const char * command)413     EXPORT_C int symbian_do_spawn_nowait(const char *command)
414     {
415         return symbian_spawner(command, ESpawnNone);
416     }
symbian_do_aspawn(void * vreally,void * vmark,void * sp)417     EXPORT_C int symbian_do_aspawn(void* vreally, void* vmark, void* sp)
418     {
419         return -1;
420     }
421 }
422 
423