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