1
2 #ifndef _GNU_SOURCE
3 #define _GNU_SOURCE 1
4 #endif
5
6 #include <string.h>
7 #include <errno.h>
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11
12 #include "ps-internal.h"
13
14 #ifdef PS__WINDOWS
15 #include <windows.h>
16 #endif
17
18 char ps__last_error_string[1024];
19 SEXP ps__last_error;
20
21 /* TODO: these should throw real error objects */
22
ps__set_error_impl(const char * class,int system_errno,long pid,const char * msg,...)23 void *ps__set_error_impl(const char *class, int system_errno,
24 long pid, const char *msg, ...) {
25 va_list args;
26 const char *ps_error = "ps_error", *error = "error", *condition = "condition";
27 SEXP rclass;
28
29 va_start(args, msg);
30 vsnprintf(ps__last_error_string,
31 sizeof(ps__last_error_string) - 1, msg, args);
32 va_end(args);
33
34 SET_VECTOR_ELT(ps__last_error, 0, mkString(ps__last_error_string));
35 if (class) {
36 PROTECT(rclass = ps__build_string(class, ps_error, error, condition, NULL));
37 } else {
38 PROTECT(rclass = ps__build_string(ps_error, error, condition, NULL));
39 }
40 SET_VECTOR_ELT(ps__last_error, 1, rclass);
41 UNPROTECT(1);
42 SET_VECTOR_ELT(ps__last_error, 2, ScalarInteger(system_errno));
43 SET_VECTOR_ELT(ps__last_error, 3, ScalarInteger((int) pid));
44 return NULL;
45 }
46
ps__set_error(const char * msg,...)47 void *ps__set_error(const char *msg, ...) {
48 va_list args;
49
50 va_start(args, msg);
51 ps__set_error_impl(0, 0, NA_INTEGER, msg, args);
52 va_end(args);
53
54 return NULL;
55 }
56
ps__no_such_process(long pid,const char * name)57 void *ps__no_such_process(long pid, const char *name) {
58 const char *class = "no_such_process";
59 return ps__set_error_impl(
60 class, 0, pid, "No such process, pid %ld, %s", pid,
61 name ? name : "???");
62 }
63
ps__access_denied(const char * msg)64 void *ps__access_denied(const char *msg) {
65 return ps__set_error_impl("access_denied", 0, NA_INTEGER,
66 msg && strlen(msg) ? msg : "Permission denied");
67 }
68
ps__zombie_process(long pid)69 void *ps__zombie_process(long pid) {
70 return ps__set_error_impl("zombie_process", 0, pid,
71 "Process is a zombie, pid %ld", pid);
72 }
73
ps__not_implemented(const char * what)74 void *ps__not_implemented(const char *what) {
75 return ps__set_error_impl("not_implemented", 0, NA_INTEGER,
76 "Not implemented on this platform: `%s`", what);
77 }
78
ps__no_memory(const char * msg)79 void *ps__no_memory(const char *msg) {
80 return ps__set_error_impl("no_memory",
81 #ifdef PS__WINDOWS
82 ERROR_NOT_ENOUGH_MEMORY,
83 #else
84 ENOMEM,
85 #endif
86 NA_INTEGER,
87 msg && strlen(msg) ? msg : "Out of memory");
88 }
89
ps__set_error_from_errno()90 void *ps__set_error_from_errno() {
91 if (errno) {
92 return ps__set_error_impl("os_error", errno, NA_INTEGER, "%s",
93 strerror(errno));
94 } else {
95 return ps__set_error_impl(0, errno, NA_INTEGER, "run time error");
96 }
97 }
98
99 #ifdef PS__WINDOWS
ps__set_error_from_windows_error(long err)100 void *ps__set_error_from_windows_error(long err) {
101 /* TODO: get the actual message */
102 if (!err) err = GetLastError();
103 return ps__set_error_impl("os_error", err, NA_INTEGER,
104 "System error: %i", err);
105 }
106 #endif
107
ps__throw_error()108 SEXP ps__throw_error() {
109 SEXP stopfun, call, out;
110
111 Rf_setAttrib(ps__last_error, R_ClassSymbol, VECTOR_ELT(ps__last_error, 1));
112 PROTECT(stopfun = Rf_findFun(Rf_install("stop"), R_BaseEnv));
113 PROTECT(call = Rf_lang2(stopfun, ps__last_error));
114 PROTECT(out = Rf_eval(call, R_GlobalEnv));
115
116 UNPROTECT(3);
117 return out;
118 }
119
ps__protect_free_finalizer(SEXP ptr)120 void ps__protect_free_finalizer(SEXP ptr) {
121 void *vptr = R_ExternalPtrAddr(ptr);
122 if (!vptr) return;
123 free(vptr);
124 }
125
ps__str_to_utf8(const char * str)126 SEXP ps__str_to_utf8(const char *str) {
127 /* TODO: really convert */
128 return mkString(str);
129 }
130
ps__str_to_utf8_size(const char * str,size_t size)131 SEXP ps__str_to_utf8_size(const char *str, size_t size) {
132 /* TODO: really convert */
133 return ScalarString(Rf_mkCharLen(str, (int) size));
134 }
135
136 #ifdef PS__WINDOWS
137
ps__utf8_to_utf16(const char * s,WCHAR ** ws_ptr)138 int ps__utf8_to_utf16(const char* s, WCHAR** ws_ptr) {
139 int ws_len, r;
140 WCHAR* ws;
141
142 ws_len = MultiByteToWideChar(
143 /* CodePage = */ CP_UTF8,
144 /* dwFlags = */ 0,
145 /* lpMultiByteStr = */ s,
146 /* cbMultiByte = */ -1,
147 /* lpWideCharStr = */ NULL,
148 /* cchWideChar = */ 0);
149
150 if (ws_len <= 0) { return GetLastError(); }
151
152 ws = (WCHAR*) R_alloc(ws_len, sizeof(WCHAR));
153 if (ws == NULL) { return ERROR_OUTOFMEMORY; }
154
155 r = MultiByteToWideChar(
156 /* CodePage = */ CP_UTF8,
157 /* dwFlags = */ 0,
158 /* lpMultiByteStr = */ s,
159 /* cbMultiBytes = */ -1,
160 /* lpWideCharStr = */ ws,
161 /* cchWideChar = */ ws_len);
162
163 if (r != ws_len) {
164 error("processx error interpreting UTF8 command or arguments");
165 }
166
167 *ws_ptr = ws;
168 return 0;
169 }
170
ps__utf16_to_rawsxp(const WCHAR * ws,int size)171 SEXP ps__utf16_to_rawsxp(const WCHAR* ws, int size) {
172 int s_len, r;
173 SEXP s;
174
175 s_len = WideCharToMultiByte(
176 /* CodePage = */ CP_UTF8,
177 /* dwFlags = */ 0,
178 /* lpWideCharStr = */ ws,
179 /* cchWideChar = */ size,
180 /* lpMultiByteStr = */ NULL,
181 /* cbMultiByte = */ 0,
182 /* lpDefaultChar = */ NULL,
183 /* lpUsedDefaultChar = */ NULL);
184
185 if (s_len <= 0) {
186 error("error converting wide chars to UTF-8");
187 }
188
189 PROTECT(s = allocVector(RAWSXP, s_len));
190
191 r = WideCharToMultiByte(
192 /* CodePage = */ CP_UTF8,
193 /* dwFlags = */ 0,
194 /* lpWideCharStr = */ ws,
195 /* cchWideChar = */ size,
196 /* lpMultiByteStr = */ (char*) RAW(s),
197 /* cbMultiByte = */ s_len,
198 /* lpDefaultChar = */ NULL,
199 /* lpUsedDefaultChar = */ NULL);
200
201 if (r != s_len) {
202 error("error converting wide chars to UTF-8");
203 }
204
205 UNPROTECT(1);
206 return s;
207 }
208
ps__utf16_to_strsxp(const WCHAR * ws,int size)209 SEXP ps__utf16_to_strsxp(const WCHAR* ws, int size) {
210 SEXP r, s;
211 int r_len, s_len, idx, notr = 0;
212 char *ptr, *end, *prev;
213
214 PROTECT(r = ps__utf16_to_rawsxp(ws, size));
215
216 r_len = LENGTH(r);
217 ptr = (char*) RAW(r);
218 end = ptr + r_len;
219 s_len = 0;
220 while (ptr < end) {
221 if (!*ptr) s_len++;
222 ptr++;
223 }
224
225 /* If ther is no \0 at the end */
226 if (r_len > 0 && *(end - 1) != '\0') notr = 1;
227
228 PROTECT(s = allocVector(STRSXP, s_len + notr));
229
230 prev = ptr = (char*) RAW(r);
231 idx = 0;
232 while (ptr < end) {
233 while (ptr < end && *ptr) ptr++;
234 SET_STRING_ELT(s, idx++, mkCharLen(prev, ptr - prev));
235 prev = ++ptr;
236 }
237
238 if (notr) {
239 SET_STRING_ELT(s, idx++, mkCharLen(prev, end - prev));
240 }
241
242 UNPROTECT(2);
243 return s;
244 }
245
ps__utf16_to_charsxp(const WCHAR * ws,int size)246 SEXP ps__utf16_to_charsxp(const WCHAR* ws, int size) {
247 SEXP r, s;
248
249 PROTECT(r = ps__utf16_to_rawsxp(ws, size));
250 PROTECT(s = mkCharLen((char*) RAW(r), LENGTH(r) - 1));
251 UNPROTECT(2);
252 return s;
253 }
254
255 #endif
256
ps__build_template_length(const char * template)257 static size_t ps__build_template_length(const char *template) {
258 size_t len = 0;
259 size_t n = strlen(template);
260 size_t i;
261
262 for (i = 0; i < n; i++) {
263 len += isalpha(template[i]) != 0;
264 }
265
266 return len;
267 }
268
ps__build_string(const char * str,...)269 SEXP ps__build_string(const char *str, ...) {
270 va_list args;
271 size_t len = 1;
272 SEXP res;
273 char *s;
274
275 /* Length 0 character */
276 if (!str) return(allocVector(STRSXP, 0));
277
278 /* Count the length first */
279 va_start(args, str);
280 while (va_arg(args, char*)) len++;
281 va_end(args);
282
283 PROTECT(res = allocVector(STRSXP, len));
284 SET_STRING_ELT(res, 0, mkChar(str));
285 len = 1;
286 va_start(args, str);
287 while ((s = va_arg(args, char*))) {
288 SET_STRING_ELT(res, len++, mkChar(s));
289 }
290 va_end(args);
291
292 UNPROTECT(1);
293 return res;
294 }
295
ps__build_list_impl(const char * template,int named,va_list args)296 static SEXP ps__build_list_impl(const char *template, int named,
297 va_list args) {
298 size_t slen = strlen(template);
299 size_t len = ps__build_template_length(template);
300 SEXP res = PROTECT(allocVector(VECSXP, len));
301 SEXP names = named ? PROTECT(allocVector(STRSXP, len)) : R_NilValue;
302 int ptr = 0, lptr = 0;
303
304 char *tmp1;
305 size_t tmp2;
306 char tmp3;
307
308 while (ptr < slen) {
309 if (named) {
310 SET_STRING_ELT(names, lptr, mkChar(va_arg(args, const char*)));
311 }
312
313 switch(template[ptr]) {
314
315 case 's':
316 case 'z':
317 case 'U':
318 tmp1 = va_arg(args, char*);
319 SET_VECTOR_ELT(res, lptr, tmp1 ? mkString(tmp1) : R_NilValue);
320 break;
321
322 case 'y':
323 tmp1 = va_arg(args, char*);
324 tmp2 = strlen(tmp1);
325 SET_VECTOR_ELT(res, lptr, allocVector(RAWSXP, tmp2));
326 memcpy(RAW(VECTOR_ELT(res, lptr)), tmp1, tmp2);
327 break;
328 case 'u':
329 error("'u' is not implemented yet");
330 break;
331
332 case 'i':
333 case 'b':
334 case 'h':
335 case 'B':
336 case 'H':
337 SET_VECTOR_ELT(res, lptr, ScalarInteger(va_arg(args, int)));
338 break;
339
340 case 'l':
341 SET_VECTOR_ELT(res, lptr, ScalarReal(va_arg(args, long int)));
342 break;
343
344 case 'I':
345 SET_VECTOR_ELT(res, lptr, ScalarReal(va_arg(args, unsigned int)));
346 break;
347
348 case 'k':
349 SET_VECTOR_ELT(res, lptr, ScalarReal(va_arg(args, unsigned long)));
350 break;
351
352 case 'L':
353 SET_VECTOR_ELT(res, lptr, ScalarReal(va_arg(args, long long)));
354 break;
355
356 case 'K':
357 SET_VECTOR_ELT(res, lptr, ScalarReal(va_arg(args, unsigned long long)));
358 break;
359
360 case 'n':
361 SET_VECTOR_ELT(res, lptr, ScalarReal(va_arg(args, size_t)));
362 break;
363
364 case 'c':
365 tmp3 = (char) va_arg(args, int);
366 SET_VECTOR_ELT(res, lptr, ScalarRaw(tmp3));
367 break;
368
369 case 'C':
370 tmp3 = (char) va_arg(args, int);
371 SET_VECTOR_ELT(res, lptr, ScalarString(mkCharLen(&tmp3, 1)));
372 break;
373
374 case 'd':
375 case 'f':
376 SET_VECTOR_ELT(res, lptr, ScalarReal(va_arg(args, double)));
377 break;
378
379 case 'D':
380 error("'D' is not implemented yet");
381 break;
382
383 case 'S':
384 case 'N':
385 case 'O':
386 SET_VECTOR_ELT(res, lptr, (SEXP) va_arg(args, void*));
387 break;
388
389 default:
390 error("Unknown conversion key: `%c`", template[ptr]);
391 }
392 ptr++;
393 lptr++;
394 }
395
396 if (named) {
397 setAttrib(res, R_NamesSymbol, names);
398 UNPROTECT(1);
399 }
400
401 UNPROTECT(1);
402 return res;
403 }
404
ps__build_list(const char * template,...)405 SEXP ps__build_list(const char *template, ...) {
406 va_list args;
407 SEXP res;
408 va_start(args, template);
409 res = PROTECT(ps__build_list_impl(template, 0, args));
410 va_end(args);
411 UNPROTECT(1);
412 return res;
413 }
414
ps__build_named_list(const char * template,...)415 SEXP ps__build_named_list(const char *template, ...) {
416 va_list args;
417 SEXP res;
418 va_start(args, template);
419 res = PROTECT(ps__build_list_impl(template, 1, args));
420 va_end(args);
421 UNPROTECT(1);
422 return res;
423 }
424