This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
even more Symbian
[perl5.git] / symbian / symbian_utils.cpp
CommitLineData
27da23d5
JH
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
21extern "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[] = "";
0added8b 45 EXPORT_C char* symbian_get_error_string(TInt error)
27da23d5 46 {
0added8b
JH
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).
27da23d5
JH
50 dTHX;
51 if (error >= 0)
52 return strerror(error);
0added8b
JH
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 }
27da23d5
JH
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());
27da23d5
JH
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