This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
patch submission(symbian/symbian_utils.dll)
[perl5.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         if (sec)  *sec  = I64LOW(tmps);
208         if (usec) *usec = I64LOW(tickus) - I64LOW(tmps) * 1000000;
209         return 0;
210     }
211     EXPORT_C int symbian_usleep(unsigned int usec)
212     {
213         if (usec >= 1000000) {
214             errno = EINVAL;
215             return -1;
216         }
217         symbian_sleep_usec((const long) usec);
218         return 0;
219     }
220 #define SEC_USEC_TO_CLK_TCK(s, u) \
221         (((s) * PERL_SYMBIAN_CLK_TCK) + (u / (1000000 / PERL_SYMBIAN_CLK_TCK)))
222     EXPORT_C clock_t symbian_times(struct tms *tmsbuf) 
223     {
224         long s, u;
225         if (symbian_get_cpu_time(&s, &u) == -1) {
226             errno = EINVAL;
227             return -1;
228         } else {
229             tmsbuf->tms_utime  = SEC_USEC_TO_CLK_TCK(s, u);
230             tmsbuf->tms_stime  = 0;
231             tmsbuf->tms_cutime = 0;
232             tmsbuf->tms_cstime = 0;
233             return tmsbuf->tms_utime;
234         }
235     }
236     class CProcessWait : public CActive
237     {
238     public:
239         CProcessWait() : CActive(EPriorityStandard) {
240           CActiveScheduler::Add(this);
241         }
242 #ifdef __WINS__
243         TInt Wait(RThread& aProcess)
244 #else
245         TInt Wait(RProcess& aProcess)
246 #endif
247         {
248             aProcess.Logon(iStatus);
249             aProcess.Resume();
250             SetActive();
251             CActiveScheduler::Start();
252             return iStatus.Int();
253         }
254     private:
255       void DoCancel() {;}
256       void RunL() {
257           CActiveScheduler::Stop();
258       }
259     };
260     class CSpawnIoRedirect : public CBase
261     {
262     public:
263         CSpawnIoRedirect();
264         // NOTE: there is no real implementation of I/O redirection yet.
265     protected:
266     private:
267     };
268     CSpawnIoRedirect::CSpawnIoRedirect()
269     {
270     }
271     typedef enum {
272         ESpawnNone = 0x00000000,
273         ESpawnWait = 0x00000001
274     } TSpawnFlag;
275     static int symbian_spawn(const TDesC& aFilename,
276                              const TDesC& aCommand,
277                              const TSpawnFlag aFlag,
278                              const CSpawnIoRedirect& aIoRedirect) {
279         TInt error = KErrNone;
280 #ifdef __WINS__
281         const TInt KStackSize = 0x1000;
282         const TInt KHeapMin   = 0x1000;
283         const TInt KHeapMax   = 0x100000;
284         RThread proc;
285         RLibrary lib;
286         HBufC* command = aCommand.Alloc();
287         error = lib.Load(aFilename);
288         if (error == KErrNone) {
289             TThreadFunction func = (TThreadFunction)(lib.Lookup(1));
290             if (func)
291                 error = proc.Create(aFilename,
292                                                     func,
293                                                     KStackSize,
294                                                   //  (TAny*)command,
295                                                   //  &lib,
296                                                   //  RThread().Heap(),
297                                                     KHeapMin,
298                                                     KHeapMax,
299                                                     (TAny*)command,
300                                                     EOwnerProcess);
301             else
302                 error = KErrNotFound;
303             lib.Close();
304         }
305         else
306             delete command;
307 #else
308         RProcess proc;
309         error = proc.Create(aFilename, aCommand);
310 #endif
311         if (error == KErrNone) {
312             if ((TInt)aFlag & (TInt)ESpawnWait) {
313               CProcessWait* w = new CProcessWait();
314               if (w) {
315                   error = w->Wait(proc);
316                   delete w;
317               } else
318                   error = KErrNoMemory;
319             } else
320                 proc.Resume();
321             proc.Close();
322         }
323         return error;
324     }
325     static int symbian_spawner(const char *command, TSpawnFlag aFlags)
326      {
327         TBuf<KMaxFileName> aFilename;
328         TBuf<KMaxFileName> aCommand;
329         TSpawnFlag aSpawnFlags = ESpawnWait;
330         CSpawnIoRedirect iord;
331         char *p = (char*)command;
332
333         // The recognized syntax is: "cmd [args] [&]".  Since one
334         // cannot pass more than (an argv[0] and) an argv[1] to a
335         // Symbian process anyway, not much is done to the cmd or
336         // the args, only backslash quoting.
337
338         // Strip leading whitespace.
339         while (*p && isspace(*p)) p++;
340         if (*p) {
341             // Build argv[0].
342             while (*p && !isspace(*p) && *p != '&') {
343                 if (*p == '\\') {
344                     if (p[1]) {
345                         aFilename.Append(p[1]);
346                         p++;
347                     }
348                     
349                 }
350                 else
351                     aFilename.Append(*p);
352                 p++;
353             }
354
355             if (*p) {
356                 // Skip whitespace between argv[0] and argv[1].
357                 while(*p && isspace(*p)) p++;
358                 // Build argv[1].
359                 if (*p) {
360                     char *a = p;
361                     char *b = p + 1;
362
363                     while (*b) b++;
364                     if (isspace(b[-1])) {
365                         b--;
366                         while (b > a && isspace(*b)) b--;
367                         b++;
368                     }
369                     if (b > a && b[-1] == '&') {
370                         // Parse backgrounding in any case,
371                         // but turn it off only if wanted.
372                         if ((aFlags & ESpawnWait))
373                           aSpawnFlags =
374                             (TSpawnFlag) (aSpawnFlags & ~ESpawnWait);
375                         b--;
376                         if (isspace(b[-1])) {
377                             b--;
378                             while (b > a && isspace(*b)) b--;
379                             b++;
380                         }
381                     }
382                     for (p = a; p < b; p++) {
383                         if (*p == '\\') {
384                             if (p[1])
385                                 aCommand.Append(p[1]);
386                             p++;
387                         }
388                         else
389                             aCommand.Append(*p);
390                     }
391                 }
392                 // NOTE: I/O redirection is not yet done.
393                 // Implementing that may require a separate server.
394             }
395         }
396         int spawned = symbian_spawn(aFilename, aCommand, aSpawnFlags, iord);
397         return spawned == KErrNone ? 0 : -1;
398     }
399     EXPORT_C int symbian_do_spawn(const char *command)
400     {
401         return symbian_spawner(command, ESpawnWait);
402     }
403     EXPORT_C int symbian_do_spawn_nowait(const char *command)
404     {
405         return symbian_spawner(command, ESpawnNone);
406     }
407     EXPORT_C int symbian_do_aspawn(void* vreally, void* vmark, void* sp)
408     {
409         return -1;
410     }
411 }
412