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