Move XSLoader from ext/ to dist/
[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         if(!PL_appctx)
80                 ((CPerlBase*)PL_appctx) = CPerlBase::NewInterpreter();
81         return ((CPerlBase*)PL_appctx)->ConsoleRead(fd, b, n);
82     }
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[] = "";
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     }
184     EXPORT_C void symbian_sleep_usec(const long usec)
185     {
186         User::After((TTimeIntervalMicroSeconds32) usec);
187     }
188 #define PERL_SYMBIAN_CLK_TCK 100
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     }
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)))
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:
244         CProcessWait() : CActive(EPriorityStandard) {
245           CActiveScheduler::Add(this);
246         }
247 #ifdef __WINS__
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:
260       void DoCancel() {;}
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     };
273     CSpawnIoRedirect::CSpawnIoRedirect()
274     {
275     }
276     typedef enum {
277         ESpawnNone = 0x00000000,
278         ESpawnWait = 0x00000001
279     } TSpawnFlag;
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     }
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     }
409     EXPORT_C int symbian_do_spawn(const char *command)
410     {
411         return symbian_spawner(command, ESpawnWait);
412     }
413     EXPORT_C int symbian_do_spawn_nowait(const char *command)
414     {
415         return symbian_spawner(command, ESpawnNone);
416     }
417     EXPORT_C int symbian_do_aspawn(void* vreally, void* vmark, void* sp)
418     {
419         return -1;
420     }
421 }
422