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