| 5223 |
ripley |
1 |
/*
|
|
|
2 |
* R : A Computer Language for Statistical Data Analysis
|
|
|
3 |
* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka
|
| 75824 |
kalibera |
4 |
* Copyright (C) 1997--2018 The R Core Team
|
| 5223 |
ripley |
5 |
*
|
|
|
6 |
* This program is free software; you can redistribute it and/or modify
|
|
|
7 |
* it under the terms of the GNU General Public License as published by
|
|
|
8 |
* the Free Software Foundation; either version 2 of the License, or
|
|
|
9 |
* (at your option) any later version.
|
|
|
10 |
*
|
|
|
11 |
* This program is distributed in the hope that it will be useful,
|
|
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
14 |
* GNU General Public License for more details.
|
|
|
15 |
*
|
|
|
16 |
* You should have received a copy of the GNU General Public License
|
| 42300 |
ripley |
17 |
* along with this program; if not, a copy is available at
|
| 68956 |
ripley |
18 |
* https://www.R-project.org/Licenses/
|
| 5223 |
ripley |
19 |
*/
|
|
|
20 |
|
| 45070 |
ripley |
21 |
/* See ../unix/system.txt for a description of functions */
|
| 5223 |
ripley |
22 |
|
| 45070 |
ripley |
23 |
/* Windows analogue of unix/sys-unix.c: often rather similar */
|
| 5223 |
ripley |
24 |
|
|
|
25 |
#ifdef HAVE_CONFIG_H
|
| 7701 |
hornik |
26 |
#include <config.h>
|
| 5223 |
ripley |
27 |
#endif
|
|
|
28 |
|
| 51849 |
ripley |
29 |
#include <Defn.h>
|
| 60667 |
ripley |
30 |
#include <Internal.h>
|
| 51849 |
ripley |
31 |
#include <Fileio.h>
|
|
|
32 |
#include <Startup.h>
|
| 5223 |
ripley |
33 |
|
| 51849 |
ripley |
34 |
#include <ctype.h> /* for isalpha */
|
|
|
35 |
|
| 5223 |
ripley |
36 |
/*
|
|
|
37 |
* 4) INITIALIZATION AND TERMINATION ACTIONS
|
|
|
38 |
*/
|
|
|
39 |
|
|
|
40 |
FILE *R_OpenInitFile(void)
|
|
|
41 |
{
|
| 51229 |
ripley |
42 |
char buf[PATH_MAX], *p = getenv("R_PROFILE_USER");
|
| 5223 |
ripley |
43 |
FILE *fp;
|
|
|
44 |
|
|
|
45 |
fp = NULL;
|
|
|
46 |
if (LoadInitFile) {
|
| 54654 |
ripley |
47 |
if(p) {
|
|
|
48 |
if(!*p) return NULL; /* set to "" */
|
| 45059 |
ripley |
49 |
return R_fopen(R_ExpandFileName(p), "r");
|
| 54654 |
ripley |
50 |
}
|
| 5223 |
ripley |
51 |
if ((fp = R_fopen(".Rprofile", "r")))
|
|
|
52 |
return fp;
|
| 51229 |
ripley |
53 |
snprintf(buf, PATH_MAX, "%s/.Rprofile", getenv("R_USER"));
|
| 5223 |
ripley |
54 |
if ((fp = R_fopen(buf, "r")))
|
|
|
55 |
return fp;
|
|
|
56 |
}
|
|
|
57 |
return fp;
|
|
|
58 |
}
|
|
|
59 |
/*
|
|
|
60 |
* 5) FILESYSTEM INTERACTION
|
|
|
61 |
*/
|
|
|
62 |
|
|
|
63 |
|
|
|
64 |
static int HaveHOME=-1;
|
|
|
65 |
static char UserHOME[PATH_MAX];
|
|
|
66 |
static char newFileName[PATH_MAX];
|
| 72719 |
murdoch |
67 |
|
| 41783 |
ripley |
68 |
const char *R_ExpandFileName(const char *s)
|
| 5223 |
ripley |
69 |
{
|
|
|
70 |
char *p;
|
|
|
71 |
|
| 72719 |
murdoch |
72 |
if(s[0] != '~' || (s[0] && isalpha(s[1]))) return s;
|
| 5223 |
ripley |
73 |
if(HaveHOME < 0) {
|
|
|
74 |
HaveHOME = 0;
|
| 45070 |
ripley |
75 |
p = getenv("R_USER"); /* should be set so the rest is a safety measure */
|
| 28888 |
ripley |
76 |
if(p && strlen(p) && strlen(p) < PATH_MAX) {
|
| 5223 |
ripley |
77 |
strcpy(UserHOME, p);
|
| 45070 |
ripley |
78 |
HaveHOME = 1;
|
| 5223 |
ripley |
79 |
} else {
|
| 41474 |
ripley |
80 |
p = getenv("HOME");
|
|
|
81 |
if(p && strlen(p) && strlen(p) < PATH_MAX) {
|
| 5223 |
ripley |
82 |
strcpy(UserHOME, p);
|
| 41474 |
ripley |
83 |
HaveHOME = 1;
|
|
|
84 |
} else {
|
|
|
85 |
p = getenv("HOMEDRIVE");
|
|
|
86 |
if(p && strlen(p) < PATH_MAX) {
|
|
|
87 |
strcpy(UserHOME, p);
|
|
|
88 |
p = getenv("HOMEPATH");
|
|
|
89 |
if(p && strlen(UserHOME) + strlen(p) < PATH_MAX) {
|
|
|
90 |
strcat(UserHOME, p);
|
|
|
91 |
HaveHOME = 1;
|
|
|
92 |
}
|
| 5223 |
ripley |
93 |
}
|
|
|
94 |
}
|
|
|
95 |
}
|
|
|
96 |
}
|
| 28888 |
ripley |
97 |
if(HaveHOME > 0 && strlen(UserHOME) + strlen(s+1) < PATH_MAX) {
|
| 5223 |
ripley |
98 |
strcpy(newFileName, UserHOME);
|
|
|
99 |
strcat(newFileName, s+1);
|
|
|
100 |
return newFileName;
|
| 41778 |
ripley |
101 |
} else return s;
|
| 5223 |
ripley |
102 |
}
|
|
|
103 |
|
| 72719 |
murdoch |
104 |
/* from sysutils.c */
|
|
|
105 |
void reEnc2(const char *x, char *y, int ny,
|
|
|
106 |
cetype_t ce_in, cetype_t ce_out, int subst);
|
|
|
107 |
|
|
|
108 |
/* The following is a version of R_ExpandFileName that assumes
|
|
|
109 |
s is in UTF-8 and returns the final result in that encoding as well. */
|
|
|
110 |
const char *R_ExpandFileNameUTF8(const char *s)
|
|
|
111 |
{
|
|
|
112 |
if (s[0] !='~' || (s[0] && isalpha(s[1]))) return s;
|
|
|
113 |
else {
|
|
|
114 |
char home[PATH_MAX];
|
|
|
115 |
reEnc2(R_ExpandFileName("~"), home, PATH_MAX, CE_NATIVE, CE_UTF8, 3);
|
|
|
116 |
if (strlen(home) + strlen(s+1) < PATH_MAX) {
|
|
|
117 |
strcpy(newFileName, home);
|
|
|
118 |
strcat(newFileName, s+1);
|
|
|
119 |
return newFileName;
|
|
|
120 |
} else return s;
|
|
|
121 |
}
|
|
|
122 |
}
|
|
|
123 |
|
| 5223 |
ripley |
124 |
/*
|
|
|
125 |
* 7) PLATFORM DEPENDENT FUNCTIONS
|
|
|
126 |
*/
|
|
|
127 |
|
|
|
128 |
SEXP do_machine(SEXP call, SEXP op, SEXP args, SEXP env)
|
|
|
129 |
{
|
| 69326 |
luke |
130 |
checkArity(op, args);
|
| 5223 |
ripley |
131 |
return mkString("Win32");
|
|
|
132 |
}
|
|
|
133 |
|
| 36901 |
ripley |
134 |
#define WIN32_LEAN_AND_MEAN 1
|
| 6121 |
ripley |
135 |
#include <windows.h>
|
|
|
136 |
|
| 5223 |
ripley |
137 |
static DWORD StartTime;
|
|
|
138 |
|
| 6098 |
pd |
139 |
static FILETIME Create, Exit, Kernel, User;
|
|
|
140 |
|
| 7824 |
ripley |
141 |
void R_setStartTime(void)
|
| 5223 |
ripley |
142 |
{
|
|
|
143 |
StartTime = GetTickCount();
|
|
|
144 |
}
|
|
|
145 |
|
| 10172 |
luke |
146 |
void R_getProcTime(double *data)
|
| 5223 |
ripley |
147 |
{
|
| 42253 |
ripley |
148 |
DWORD elapsed;
|
| 6098 |
pd |
149 |
double kernel, user;
|
| 45072 |
ripley |
150 |
|
| 37670 |
ripley |
151 |
/* This is in msec, but to clock-tick accuracy,
|
|
|
152 |
said to be 10ms on NT and 55ms on Win95 */
|
| 6098 |
pd |
153 |
elapsed = (GetTickCount() - StartTime) / 10;
|
| 5223 |
ripley |
154 |
|
| 45072 |
ripley |
155 |
/* These are in units of 100ns, but with an accuracy only
|
|
|
156 |
in clock ticks. So we round to 0.01s */
|
|
|
157 |
GetProcessTimes(GetCurrentProcess(), &Create, &Exit, &Kernel, &User);
|
|
|
158 |
user = 1e-5 * ((double) User.dwLowDateTime +
|
|
|
159 |
(double) User.dwHighDateTime * 4294967296.0);
|
|
|
160 |
user = floor(user)/100.0;
|
|
|
161 |
kernel = 1e-5 * ((double) Kernel.dwLowDateTime +
|
|
|
162 |
(double) Kernel.dwHighDateTime * 4294967296.0);
|
|
|
163 |
kernel = floor(kernel)/100.0;
|
| 10172 |
luke |
164 |
data[0] = user;
|
|
|
165 |
data[1] = kernel;
|
|
|
166 |
data[2] = (double) elapsed / 100.0;
|
|
|
167 |
data[3] = R_NaReal;
|
|
|
168 |
data[4] = R_NaReal;
|
|
|
169 |
}
|
|
|
170 |
|
| 57499 |
ripley |
171 |
/* use in memory.c: increments for CPU times */
|
| 10172 |
luke |
172 |
double R_getClockIncrement(void)
|
|
|
173 |
{
|
| 45070 |
ripley |
174 |
return 1.0 / 100.0;
|
| 10172 |
luke |
175 |
}
|
| 5223 |
ripley |
176 |
|
|
|
177 |
/*
|
| 75824 |
kalibera |
178 |
* Stderr, Stdout
|
|
|
179 |
* =FALSE .. drop output
|
|
|
180 |
* =TRUE .. return output
|
|
|
181 |
* ="" .. print to standard error/output
|
|
|
182 |
* =fname .. redirect to file of that name
|
|
|
183 |
*
|
|
|
184 |
* Redirection and dropping is supported with all flag values. Printing is
|
|
|
185 |
* supported with all flag values on non-RGui only (and happens via standard
|
|
|
186 |
* handles). For returning output (anywhere) and printing (on RGui),
|
|
|
187 |
* restrictions apply (below).
|
|
|
188 |
*
|
|
|
189 |
* flag =0 don't wait
|
|
|
190 |
* returning of output not supported
|
|
|
191 |
* RGui: non-redirected standard error and standard output always dropped
|
|
|
192 |
* (printing not supported)
|
|
|
193 |
*
|
|
|
194 |
* flag =1 wait
|
|
|
195 |
* otherwise like flag =0
|
|
|
196 |
*
|
|
|
197 |
* flag =2 wait/printing in RGui
|
|
|
198 |
* returning of output not supported
|
|
|
199 |
* non-RGui: works like flag =1
|
|
|
200 |
* RGui: standard error and/or standard output is printed on console;
|
|
|
201 |
* flag=2 may only be used when at least one of the outputs
|
|
|
202 |
* is to be printed
|
|
|
203 |
*
|
|
|
204 |
* flag =3 wait/return output
|
|
|
205 |
* standard error and/or standard output is returned
|
|
|
206 |
* flag=3 may only be used when at least one of the outputs is to be returned
|
|
|
207 |
* RGui: printing is not supported (one cannot return one output and print
|
|
|
208 |
* the other)
|
|
|
209 |
*
|
|
|
210 |
* Add 10 to flag to minimize application
|
|
|
211 |
* Add 20 to flag make application "invisible"
|
| 5223 |
ripley |
212 |
*/
|
|
|
213 |
|
|
|
214 |
#include "run.h"
|
|
|
215 |
|
| 22229 |
ripley |
216 |
#define INTERN_BUFSIZE 8096
|
| 5223 |
ripley |
217 |
SEXP do_system(SEXP call, SEXP op, SEXP args, SEXP rho)
|
|
|
218 |
{
|
|
|
219 |
rpipe *fp;
|
| 22229 |
ripley |
220 |
char buf[INTERN_BUFSIZE];
|
| 52814 |
ripley |
221 |
const char *fout = "", *ferr = "";
|
| 64237 |
murdoch |
222 |
int vis = 0, flag = 2, i = 0, j, ll = 0;
|
| 52814 |
ripley |
223 |
SEXP cmd, fin, Stdout, Stderr, tlist = R_NilValue, tchar, rval;
|
| 75824 |
kalibera |
224 |
PROTECT_INDEX ti;
|
| 72855 |
kalibera |
225 |
int timeout = 0, timedout = 0;
|
| 5223 |
ripley |
226 |
|
|
|
227 |
checkArity(op, args);
|
| 52814 |
ripley |
228 |
cmd = CAR(args);
|
|
|
229 |
if (!isString(cmd) || LENGTH(cmd) != 1)
|
| 32888 |
ripley |
230 |
errorcall(call, _("character string expected as first argument"));
|
| 52814 |
ripley |
231 |
args = CDR(args);
|
|
|
232 |
flag = asInteger(CAR(args)); args = CDR(args);
|
|
|
233 |
if (flag >= 20) {vis = -1; flag -= 20;}
|
|
|
234 |
else if (flag >= 10) {vis = 0; flag -= 10;}
|
|
|
235 |
else vis = 1;
|
|
|
236 |
|
|
|
237 |
fin = CAR(args);
|
|
|
238 |
if (!isString(fin))
|
| 32888 |
ripley |
239 |
errorcall(call, _("character string expected as third argument"));
|
| 52814 |
ripley |
240 |
args = CDR(args);
|
|
|
241 |
Stdout = CAR(args);
|
|
|
242 |
args = CDR(args);
|
|
|
243 |
Stderr = CAR(args);
|
| 72855 |
kalibera |
244 |
args = CDR(args);
|
|
|
245 |
timeout = asInteger(CAR(args));
|
|
|
246 |
if (timeout == NA_INTEGER || timeout < 0 || timeout > 2000000)
|
|
|
247 |
/* the limit could be increased, but not much as in milliseconds it
|
|
|
248 |
has to fit into a 32-bit unsigned integer */
|
|
|
249 |
errorcall(call, _("invalid '%s' argument"), "timeout");
|
|
|
250 |
if (timeout && !flag)
|
|
|
251 |
errorcall(call, "Timeout with background running processes is not supported.");
|
|
|
252 |
|
| 5223 |
ripley |
253 |
if (CharacterMode == RGui) {
|
| 53348 |
ripley |
254 |
/* This is a rather conservative approach: if
|
|
|
255 |
Rgui is launched from a console window it does have
|
|
|
256 |
standard handles -- but users might well not expect that.
|
|
|
257 |
*/
|
| 5223 |
ripley |
258 |
SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE);
|
|
|
259 |
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
|
|
|
260 |
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
|
| 63695 |
ripley |
261 |
if (TYPEOF(Stdout) == STRSXP) fout = CHAR(STRING_ELT(Stdout, 0));
|
|
|
262 |
if (TYPEOF(Stderr) == STRSXP) ferr = CHAR(STRING_ELT(Stderr, 0));
|
| 52814 |
ripley |
263 |
} else {
|
|
|
264 |
if (flag == 2) flag = 1; /* ignore std.output.on.console */
|
| 53348 |
ripley |
265 |
if (TYPEOF(Stdout) == STRSXP) fout = CHAR(STRING_ELT(Stdout, 0));
|
|
|
266 |
else if (asLogical(Stdout) == 0) fout = NULL;
|
|
|
267 |
if (TYPEOF(Stderr) == STRSXP) ferr = CHAR(STRING_ELT(Stderr, 0));
|
|
|
268 |
else if (asLogical(Stderr) == 0) ferr = NULL;
|
| 5223 |
ripley |
269 |
}
|
| 52814 |
ripley |
270 |
|
|
|
271 |
if (flag < 2) { /* Neither intern = TRUE nor
|
| 52813 |
ripley |
272 |
show.output.on.console for Rgui */
|
| 72855 |
kalibera |
273 |
ll = runcmd_timeout(CHAR(STRING_ELT(cmd, 0)),
|
| 52814 |
ripley |
274 |
getCharCE(STRING_ELT(cmd, 0)),
|
| 72855 |
kalibera |
275 |
flag, vis, CHAR(STRING_ELT(fin, 0)), fout, ferr,
|
|
|
276 |
timeout, &timedout);
|
| 73609 |
kalibera |
277 |
if (ll == NOLAUNCH) warning(runerror());
|
| 5223 |
ripley |
278 |
} else {
|
| 52813 |
ripley |
279 |
/* read stdout +/- stderr from pipe */
|
| 75824 |
kalibera |
280 |
int m = -1;
|
|
|
281 |
if ((TYPEOF(Stderr) == LGLSXP && asLogical(Stderr)) ||
|
|
|
282 |
(CharacterMode == RGui && TYPEOF(Stderr) == STRSXP && ferr && !ferr[0]))
|
|
|
283 |
/* read stderr from pipe */
|
|
|
284 |
m = 2;
|
|
|
285 |
if ((TYPEOF(Stdout) == LGLSXP && asLogical(Stdout)) ||
|
|
|
286 |
(CharacterMode == RGui && TYPEOF(Stdout) == STRSXP && fout && !fout[0]))
|
|
|
287 |
/* read stdout from pipe */
|
|
|
288 |
m = (m == 2) ? 3 : 0;
|
|
|
289 |
if (m == -1)
|
|
|
290 |
/* does not happen with system()/system2() */
|
|
|
291 |
error(_("invalid %s argument"), "flag");
|
|
|
292 |
|
| 53595 |
ripley |
293 |
fp = rpipeOpen(CHAR(STRING_ELT(cmd, 0)), getCharCE(STRING_ELT(cmd, 0)),
|
| 72855 |
kalibera |
294 |
vis, CHAR(STRING_ELT(fin, 0)), m, fout, ferr, timeout);
|
| 5223 |
ripley |
295 |
if (!fp) {
|
| 52813 |
ripley |
296 |
/* If intern = TRUE generate an error */
|
| 52802 |
ripley |
297 |
if (flag == 3) error(runerror());
|
| 5223 |
ripley |
298 |
ll = NOLAUNCH;
|
|
|
299 |
} else {
|
| 75824 |
kalibera |
300 |
if (flag == 3) { /* intern */
|
|
|
301 |
PROTECT_WITH_INDEX(tlist, &ti);
|
|
|
302 |
for (i = 0; rpipeGets(fp, buf, INTERN_BUFSIZE); i++) {
|
|
|
303 |
ll = strlen(buf) - 1;
|
|
|
304 |
if ((ll >= 0) && (buf[ll] == '\n')) buf[ll] = '\0';
|
|
|
305 |
tchar = mkChar(buf);
|
|
|
306 |
REPROTECT(tlist = CONS(tchar, tlist), ti);
|
|
|
307 |
}
|
|
|
308 |
} else { /* print on RGui console */
|
| 53612 |
ripley |
309 |
for (i = 0; rpipeGets(fp, buf, INTERN_BUFSIZE); i++)
|
| 5223 |
ripley |
310 |
R_WriteConsole(buf, strlen(buf));
|
|
|
311 |
}
|
| 72855 |
kalibera |
312 |
ll = rpipeClose(fp, &timedout);
|
| 5223 |
ripley |
313 |
}
|
|
|
314 |
}
|
| 72855 |
kalibera |
315 |
if (timedout) {
|
|
|
316 |
ll = 124;
|
| 73630 |
kalibera |
317 |
warning(_("command '%s' timed out after %ds"),
|
|
|
318 |
CHAR(STRING_ELT(cmd, 0)), timeout);
|
| 72855 |
kalibera |
319 |
} else if (flag == 3 && ll) {
|
| 73630 |
kalibera |
320 |
warning(_("running command '%s' had status %d"),
|
|
|
321 |
CHAR(STRING_ELT(cmd, 0)), ll);
|
| 64237 |
murdoch |
322 |
}
|
| 52813 |
ripley |
323 |
if (flag == 3) { /* intern = TRUE: convert pairlist to list */
|
| 52802 |
ripley |
324 |
PROTECT(rval = allocVector(STRSXP, i));
|
| 5223 |
ripley |
325 |
for (j = (i - 1); j >= 0; j--) {
|
| 10172 |
luke |
326 |
SET_STRING_ELT(rval, j, CAR(tlist));
|
| 5223 |
ripley |
327 |
tlist = CDR(tlist);
|
|
|
328 |
}
|
| 60370 |
urbaneks |
329 |
if(ll) {
|
|
|
330 |
SEXP lsym = install("status");
|
|
|
331 |
setAttrib(rval, lsym, ScalarInteger(ll));
|
|
|
332 |
}
|
| 75824 |
kalibera |
333 |
UNPROTECT(2); /* tlist, rval */
|
| 52802 |
ripley |
334 |
return rval;
|
| 5223 |
ripley |
335 |
} else {
|
| 52814 |
ripley |
336 |
rval = ScalarInteger(ll);
|
| 5223 |
ripley |
337 |
R_Visible = 0;
|
| 52814 |
ripley |
338 |
return rval;
|
| 5223 |
ripley |
339 |
}
|
|
|
340 |
}
|