This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Better fix for RT #2140 (list assignment with duplicated temporaries)
[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>
27da23d5
JH
12#include <utf.h>
13#include <hal.h>
14
d0d72822
JH
15#include <eikenv.h>
16
27da23d5
JH
17#include <string.h>
18#include <ctype.h>
19
d0d72822 20#include "PerlUi.h"
27da23d5 21#include "PerlBase.h"
d0d72822
JH
22#include "PerlUtil.h"
23
24#include "EXTERN.h"
25#include "perl.h"
26#include "XSUB.h"
27da23d5
JH
27
28extern "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 }
d0d72822
JH
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 }
27da23d5
JH
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
515fe3bd
AR
79 if(!PL_appctx)
80 ((CPerlBase*)PL_appctx) = CPerlBase::NewInterpreter();
27da23d5
JH
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
515fe3bd
AR
88 if(!PL_appctx)
89 ((CPerlBase*)PL_appctx) = CPerlBase::NewInterpreter();
27da23d5
JH
90 return ((CPerlBase*)PL_appctx)->ConsoleWrite(fd, b, n);
91 }
92 static const char NullErr[] = "";
0added8b 93 EXPORT_C char* symbian_get_error_string(TInt error)
27da23d5 94 {
0added8b 95 // CTextResolver seems to be unreliable, so we roll our own
25ca88e0
JH
96 // at least for the basic Symbian errors (this does not cover
97 // the various subsystems).
27da23d5
JH
98 dTHX;
99 if (error >= 0)
100 return strerror(error);
0added8b
JH
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 }
515fe3bd 178 SV* sv = Perl_get_sv(aTHX_ "\005", TRUE); /* $^E or ${^OS_ERROR} */
27da23d5
JH
179 if (!sv)
180 return (char*)NullErr;
181 sv_setpv(sv, (const char *)buf8.PtrZ());
27da23d5
JH
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;
d3efbecc 202 PL_clocktick = PERL_SYMBIAN_CLK_TCK;
27da23d5
JH
203 }
204 tick -= PL_timesbase.tms_utime;
205 TInt64 tickus = TInt64(tick) * TInt64(periodus);
206 TInt64 tmps = tickus / 1000000;
d3efbecc 207#ifdef __SERIES60_3X__
515fe3bd
AR
208 if (sec) *sec = I64LOW(tmps);
209 if (usec) *usec = I64LOW(tickus) - I64LOW(tmps) * 1000000;
d3efbecc
AR
210#else
211 if (sec) *sec = tmps.Low();
212 if (usec) *usec = tickus.Low() - tmps.Low() * 1000000;
213#endif //__SERIES60_3X__
27da23d5
JH
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 }
d0d72822 241 class CProcessWait : public CActive
27da23d5
JH
242 {
243 public:
d0d72822 244 CProcessWait() : CActive(EPriorityStandard) {
27da23d5
JH
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 }
27da23d5
JH
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)
d3efbecc
AR
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);
27da23d5
JH
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) {
d0d72822 323 CProcessWait* w = new CProcessWait();
27da23d5
JH
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