Commit | Line | Data |
27da23d5 |
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(const TInt error) |
46 | { |
47 | dTHX; |
48 | if (error >= 0) |
49 | return strerror(error); |
50 | CTextResolver* textResolver = CTextResolver::NewL(); |
51 | CleanupStack::PushL(textResolver); |
52 | TBuf<KErrorResolverMaxTextLength> buf16; |
53 | TBuf8<KErrorResolverMaxTextLength> buf8; |
54 | if (error != KErrNone) |
55 | buf16 = textResolver->ResolveError(error); |
56 | if (buf16.Length()) { |
57 | if (CnvUtfConverter::ConvertFromUnicodeToUtf8(buf8, buf16) != |
58 | KErrNone) { |
59 | CleanupStack::PopAndDestroy(textResolver); |
60 | return (char*)NullErr; |
61 | } |
62 | } |
63 | SV* sv = Perl_get_sv(aTHX_ "\005", TRUE); /* $^E or ${^OS_ERROR} */ |
64 | if (!sv) |
65 | return (char*)NullErr; |
66 | sv_setpv(sv, (const char *)buf8.PtrZ()); |
67 | SvUTF8_on(sv); |
68 | CleanupStack::PopAndDestroy(textResolver); |
69 | return SvPV_nolen(sv); |
70 | } |
71 | EXPORT_C void symbian_sleep_usec(const long usec) |
72 | { |
73 | User::After((TTimeIntervalMicroSeconds32) usec); |
74 | } |
75 | #define PERL_SYMBIAN_CLK_TCK 100 |
76 | EXPORT_C int symbian_get_cpu_time(long* sec, long* usec) |
77 | { |
78 | // The RThread().GetCpuTime() does not seem to work? |
79 | // (it always returns KErrNotSupported) |
80 | // TTimeIntervalMicroSeconds ti; |
81 | // TInt err = me.GetCpuTime(ti); |
82 | dTHX; |
83 | TInt periodus; /* tick period in microseconds */ |
84 | if (HAL::Get(HALData::ESystemTickPeriod, periodus) != KErrNone) |
85 | return -1; |
86 | TUint tick = User::TickCount(); |
87 | if (PL_timesbase.tms_utime == 0) { |
88 | PL_timesbase.tms_utime = tick; |
89 | PL_clocktick = PERL_SYMBIAN_CLK_TCK; |
90 | } |
91 | tick -= PL_timesbase.tms_utime; |
92 | TInt64 tickus = TInt64(tick) * TInt64(periodus); |
93 | TInt64 tmps = tickus / 1000000; |
94 | if (sec) *sec = tmps.Low(); |
95 | if (usec) *usec = tickus.Low() - tmps.Low() * 1000000; |
96 | return 0; |
97 | } |
98 | EXPORT_C int symbian_usleep(unsigned int usec) |
99 | { |
100 | if (usec >= 1000000) { |
101 | errno = EINVAL; |
102 | return -1; |
103 | } |
104 | symbian_sleep_usec((const long) usec); |
105 | return 0; |
106 | } |
107 | #define SEC_USEC_TO_CLK_TCK(s, u) \ |
108 | (((s) * PERL_SYMBIAN_CLK_TCK) + (u / (1000000 / PERL_SYMBIAN_CLK_TCK))) |
109 | EXPORT_C clock_t symbian_times(struct tms *tmsbuf) |
110 | { |
111 | long s, u; |
112 | if (symbian_get_cpu_time(&s, &u) == -1) { |
113 | errno = EINVAL; |
114 | return -1; |
115 | } else { |
116 | tmsbuf->tms_utime = SEC_USEC_TO_CLK_TCK(s, u); |
117 | tmsbuf->tms_stime = 0; |
118 | tmsbuf->tms_cutime = 0; |
119 | tmsbuf->tms_cstime = 0; |
120 | return tmsbuf->tms_utime; |
121 | } |
122 | } |
123 | class CE32ProcessWait : public CActive |
124 | { |
125 | public: |
126 | CE32ProcessWait() : CActive(EPriorityStandard) { |
127 | CActiveScheduler::Add(this); |
128 | } |
129 | #ifdef __WINS__ |
130 | TInt Wait(RThread& aProcess) |
131 | #else |
132 | TInt Wait(RProcess& aProcess) |
133 | #endif |
134 | { |
135 | aProcess.Logon(iStatus); |
136 | aProcess.Resume(); |
137 | SetActive(); |
138 | CActiveScheduler::Start(); |
139 | return iStatus.Int(); |
140 | } |
141 | private: |
142 | void DoCancel() {;} |
143 | void RunL() { |
144 | CActiveScheduler::Stop(); |
145 | } |
146 | CActiveSchedulerWait iWait; |
147 | }; |
148 | class CSpawnIoRedirect : public CBase |
149 | { |
150 | public: |
151 | CSpawnIoRedirect(); |
152 | // NOTE: there is no real implementation of I/O redirection yet. |
153 | protected: |
154 | private: |
155 | }; |
156 | CSpawnIoRedirect::CSpawnIoRedirect() |
157 | { |
158 | } |
159 | typedef enum { |
160 | ESpawnNone = 0x00000000, |
161 | ESpawnWait = 0x00000001 |
162 | } TSpawnFlag; |
163 | static int symbian_spawn(const TDesC& aFilename, |
164 | const TDesC& aCommand, |
165 | const TSpawnFlag aFlag, |
166 | const CSpawnIoRedirect& aIoRedirect) { |
167 | TInt error = KErrNone; |
168 | #ifdef __WINS__ |
169 | const TInt KStackSize = 0x1000; |
170 | const TInt KHeapMin = 0x1000; |
171 | const TInt KHeapMax = 0x100000; |
172 | RThread proc; |
173 | RLibrary lib; |
174 | HBufC* command = aCommand.Alloc(); |
175 | error = lib.Load(aFilename); |
176 | if (error == KErrNone) { |
177 | TThreadFunction func = (TThreadFunction)(lib.Lookup(1)); |
178 | if (func) |
179 | error = proc.Create(aFilename, |
180 | func, |
181 | KStackSize, |
182 | (TAny*)command, |
183 | &lib, |
184 | RThread().Heap(), |
185 | KHeapMin, |
186 | KHeapMax, |
187 | EOwnerProcess); |
188 | else |
189 | error = KErrNotFound; |
190 | lib.Close(); |
191 | } |
192 | else |
193 | delete command; |
194 | #else |
195 | RProcess proc; |
196 | error = proc.Create(aFilename, aCommand); |
197 | #endif |
198 | if (error == KErrNone) { |
199 | if ((TInt)aFlag & (TInt)ESpawnWait) { |
200 | CE32ProcessWait* w = new CE32ProcessWait(); |
201 | if (w) { |
202 | error = w->Wait(proc); |
203 | delete w; |
204 | } else |
205 | error = KErrNoMemory; |
206 | } else |
207 | proc.Resume(); |
208 | proc.Close(); |
209 | } |
210 | return error; |
211 | } |
212 | static int symbian_spawner(const char *command, TSpawnFlag aFlags) |
213 | { |
214 | TBuf<KMaxFileName> aFilename; |
215 | TBuf<KMaxFileName> aCommand; |
216 | TSpawnFlag aSpawnFlags = ESpawnWait; |
217 | CSpawnIoRedirect iord; |
218 | char *p = (char*)command; |
219 | |
220 | // The recognized syntax is: "cmd [args] [&]". Since one |
221 | // cannot pass more than (an argv[0] and) an argv[1] to a |
222 | // Symbian process anyway, not much is done to the cmd or |
223 | // the args, only backslash quoting. |
224 | |
225 | // Strip leading whitespace. |
226 | while (*p && isspace(*p)) p++; |
227 | if (*p) { |
228 | // Build argv[0]. |
229 | while (*p && !isspace(*p) && *p != '&') { |
230 | if (*p == '\\') { |
231 | if (p[1]) { |
232 | aFilename.Append(p[1]); |
233 | p++; |
234 | } |
235 | |
236 | } |
237 | else |
238 | aFilename.Append(*p); |
239 | p++; |
240 | } |
241 | |
242 | if (*p) { |
243 | // Skip whitespace between argv[0] and argv[1]. |
244 | while(*p && isspace(*p)) p++; |
245 | // Build argv[1]. |
246 | if (*p) { |
247 | char *a = p; |
248 | char *b = p + 1; |
249 | |
250 | while (*b) b++; |
251 | if (isspace(b[-1])) { |
252 | b--; |
253 | while (b > a && isspace(*b)) b--; |
254 | b++; |
255 | } |
256 | if (b > a && b[-1] == '&') { |
257 | // Parse backgrounding in any case, |
258 | // but turn it off only if wanted. |
259 | if ((aFlags & ESpawnWait)) |
260 | aSpawnFlags = |
261 | (TSpawnFlag) (aSpawnFlags & ~ESpawnWait); |
262 | b--; |
263 | if (isspace(b[-1])) { |
264 | b--; |
265 | while (b > a && isspace(*b)) b--; |
266 | b++; |
267 | } |
268 | } |
269 | for (p = a; p < b; p++) { |
270 | if (*p == '\\') { |
271 | if (p[1]) |
272 | aCommand.Append(p[1]); |
273 | p++; |
274 | } |
275 | else |
276 | aCommand.Append(*p); |
277 | } |
278 | } |
279 | // NOTE: I/O redirection is not yet done. |
280 | // Implementing that may require a separate server. |
281 | } |
282 | } |
283 | int spawned = symbian_spawn(aFilename, aCommand, aSpawnFlags, iord); |
284 | return spawned == KErrNone ? 0 : -1; |
285 | } |
286 | EXPORT_C int symbian_do_spawn(const char *command) |
287 | { |
288 | return symbian_spawner(command, ESpawnWait); |
289 | } |
290 | EXPORT_C int symbian_do_spawn_nowait(const char *command) |
291 | { |
292 | return symbian_spawner(command, ESpawnNone); |
293 | } |
294 | EXPORT_C int symbian_do_aspawn(void* vreally, void* vmark, void* sp) |
295 | { |
296 | return -1; |
297 | } |
298 | } |
299 | |