The R Project SVN R

Rev

Rev 63220 | Rev 64657 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4394 ripley 1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
3
 *  file run.c: a simple 'reading' pipe (and a command executor)
52802 ripley 4
 *  Copyright  (C) 1999-2001  Guido Masarotto  and Brian Ripley
62583 ripley 5
 *             (C) 2007-13    The R Core Team
4394 ripley 6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
42300 ripley 18
 *  along with this program; if not, a copy is available at
19
 *  http://www.r-project.org/Licenses/
5458 ripley 20
 */
4394 ripley 21
 
8872 pd 22
#ifdef HAVE_CONFIG_H
23
#include <config.h>
24
#endif
57538 ripley 25
 
26
#define R_USE_SIGNALS 1
40513 ripley 27
#include <Defn.h>
60667 ripley 28
#include <Internal.h>
32888 ripley 29
#include "win-nls.h"
4394 ripley 30
 
36901 ripley 31
#define WIN32_LEAN_AND_MEAN 1
4394 ripley 32
#include <windows.h>
33
#include <string.h>
34
#include <stdlib.h>
35
#include <ctype.h>
36
#include "run.h"
37
 
53870 murdoch 38
#include <Startup.h> /* for CharacterMode and RGui */
39
extern UImode  CharacterMode;
40
 
42639 ripley 41
static char RunError[501] = "";
4394 ripley 42
 
59546 ripley 43
/* This might be given a command line (whole = 0) or just the
44
   executable (whole = 0).  In the later case the path may or may not
45
   be quoted */
46
static char *expandcmd(const char *cmd, int whole)
4394 ripley 47
{
59546 ripley 48
    char c = '\0';
49
    char *s, *p, *q = NULL, *f, *dest, *src;
43800 ripley 50
    int   d, ext, len = strlen(cmd)+1;
52804 ripley 51
    char buf[len], fl[len], fn[len];
52814 ripley 52
 
41793 ripley 53
    /* make a copy as we manipulate in place */
54
    strcpy(buf, cmd);
55
 
11169 ripley 56
    if (!(s = (char *) malloc(MAX_PATH + strlen(cmd)))) {
52804 ripley 57
	strcpy(RunError, "Insufficient memory (expandcmd)");
4394 ripley 58
	return NULL;
59
    }
41793 ripley 60
    /* skip leading spaces */
61
    for (p = buf; *p && isspace(*p); p++);
62
    /* find the command itself, possibly double-quoted */
59546 ripley 63
    if (whole) {
64
	d = 0;
65
    } else {
66
	for (q = p, d = 0; *q && ( d || !isspace(*q) ); q++)
67
	    if (*q == '\"') d = d ? 0 : 1;
68
	if (d) {
69
	    strcpy(RunError, "A \" is missing (expandcmd)");
70
	    return NULL;
71
	}
72
	c = *q; /* character after the command, normally a space */
73
	*q = '\0';
6153 guido 74
    }
12256 pd 75
 
52804 ripley 76
    /*
77
     * Guido resorted to this since SearchPath returned FOUND also
78
     * for file name without extension -> explicitly set
79
     *  extension
80
     */
81
    for (f = p, ext = 0 ; *f ; f++) {
41793 ripley 82
	if ((*f == '\\') || (*f == '/')) ext = 0;
83
	else if (*f == '.') ext = 1;
84
    }
85
    /* SearchPath doesn't like ", so strip out quotes */
52804 ripley 86
    for (dest = fl , src = p; *src ; src++)
87
	if (*src != '"') *dest++ = *src;
41793 ripley 88
    *dest = '\0';
89
    if (ext) {
90
	/*
91
	 * user set extension; we don't check that it is executable;
92
	 * it might get an error after; but maybe sometimes
93
	 * in the future every extension will be executable
94
	 */
95
	d = SearchPath(NULL, fl, NULL, MAX_PATH, fn, &f);
96
    } else {
97
	int iexts = 0;
43800 ripley 98
	const char *exts[] = { ".exe" , ".com" , ".cmd" , ".bat" , NULL };
41793 ripley 99
	while (exts[iexts]) {
100
	    strcpy(dest, exts[iexts]);
101
	    if ((d = SearchPath(NULL, fl, NULL, MAX_PATH, fn, &f))) break;
102
	    iexts++ ;
103
	}
104
    }
105
    if (!d) {
106
	free(s);
52804 ripley 107
	snprintf(RunError, 500, "'%s' not found", p);
59546 ripley 108
	if(!whole) *q = c;
41793 ripley 109
	return NULL;
110
    }
111
    /*
6153 guido 112
      Paranoia : on my system switching to short names is not needed
113
      since SearchPath already returns 'short names'. However,
114
      this is not documented so I prefer to be explicit.
115
      Problem is that we have removed \" from the executable since
116
      SearchPath seems dislikes them
41793 ripley 117
    */
118
    GetShortPathName(fn, s, MAX_PATH);
59546 ripley 119
    if (!whole) {
120
	*q = c;
121
	strcat(s, q);
122
    }
41793 ripley 123
    return s;
4394 ripley 124
}
125
 
27774 murdoch 126
/*
12256 pd 127
   finput is either NULL or the name of a file from which to
128
     redirect stdin for the child.
129
   newconsole != 0 to use a new console (if not waiting)
130
   visible = -1, 0, 1 for hide, minimized, default
53870 murdoch 131
   inpipe != 0 to duplicate I/O handles
52813 ripley 132
   pi is set based on the newly created process,
133
   with the hThread handle closed.
12256 pd 134
*/
135
 
44013 ripley 136
extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
137
 
52814 ripley 138
static void pcreate(const char* cmd, cetype_t enc,
139
		      int newconsole, int visible,
51030 murdoch 140
		      HANDLE hIN, HANDLE hOUT, HANDLE hERR,
141
		      PROCESS_INFORMATION *pi)
4394 ripley 142
{
143
    DWORD ret;
144
    STARTUPINFO si;
44013 ripley 145
    STARTUPINFOW wsi;
51015 murdoch 146
    HANDLE dupIN, dupOUT, dupERR;
147
    WORD showWindow = SW_SHOWDEFAULT;
53870 murdoch 148
    int inpipe;
4394 ripley 149
    char *ecmd;
51015 murdoch 150
    SECURITY_ATTRIBUTES sa;
4394 ripley 151
    sa.nLength = sizeof(sa);
152
    sa.lpSecurityDescriptor = NULL;
153
    sa.bInheritHandle = TRUE;
154
 
52813 ripley 155
    /* FIXME: this might need to be done in wchar_t */
59546 ripley 156
    if (!(ecmd = expandcmd(cmd, 0))) return; /* error message already set */
51015 murdoch 157
 
53870 murdoch 158
    inpipe = (hIN != INVALID_HANDLE_VALUE)
159
	|| (hOUT != INVALID_HANDLE_VALUE)
160
	|| (hERR != INVALID_HANDLE_VALUE);
52814 ripley 161
 
53870 murdoch 162
    if (inpipe) {
163
	HANDLE hNULL = CreateFile("NUL:", GENERIC_READ | GENERIC_WRITE, 0,
164
			   &sa, OPEN_EXISTING, 0, NULL);
165
	HANDLE hTHIS = GetCurrentProcess();
51015 murdoch 166
 
53870 murdoch 167
	if (hIN == INVALID_HANDLE_VALUE) hIN = hNULL;
168
	if (hOUT == INVALID_HANDLE_VALUE) hOUT = hNULL;
169
	if (hERR == INVALID_HANDLE_VALUE) hERR = hNULL;
170
 
171
	DuplicateHandle(hTHIS, hIN,
172
			hTHIS, &dupIN, 0, TRUE, DUPLICATE_SAME_ACCESS);
173
	DuplicateHandle(hTHIS, hOUT,
174
			hTHIS, &dupOUT, 0, TRUE, DUPLICATE_SAME_ACCESS);
175
	DuplicateHandle(hTHIS, hERR,
176
			hTHIS, &dupERR, 0, TRUE, DUPLICATE_SAME_ACCESS);
177
	CloseHandle(hTHIS);
178
	CloseHandle(hNULL);
179
    }
180
 
51015 murdoch 181
    switch (visible) {
182
    case -1:
183
	showWindow = SW_HIDE;
184
	break;
185
    case 0:
186
	showWindow = SW_SHOWMINIMIZED;
187
	break;
4394 ripley 188
    }
52814 ripley 189
 
44013 ripley 190
    if(enc == CE_UTF8) {
191
	wsi.cb = sizeof(wsi);
192
	wsi.lpReserved = NULL;
193
	wsi.lpReserved2 = NULL;
194
	wsi.cbReserved2 = 0;
195
	wsi.lpDesktop = NULL;
196
	wsi.lpTitle = NULL;
51015 murdoch 197
	wsi.dwFlags = STARTF_USESHOWWINDOW;
52814 ripley 198
	wsi.wShowWindow = showWindow;
53870 murdoch 199
	if (inpipe) {
200
	    wsi.dwFlags |= STARTF_USESTDHANDLES;
201
	    wsi.hStdInput  = dupIN;
202
	    wsi.hStdOutput = dupOUT;
203
	    wsi.hStdError  = dupERR;
204
	}
44013 ripley 205
    } else {
206
	si.cb = sizeof(si);
207
	si.lpReserved = NULL;
208
	si.lpReserved2 = NULL;
209
	si.cbReserved2 = 0;
210
	si.lpDesktop = NULL;
211
	si.lpTitle = NULL;
51015 murdoch 212
	si.dwFlags = STARTF_USESHOWWINDOW;
52814 ripley 213
	si.wShowWindow = showWindow;
53870 murdoch 214
	if (inpipe) {
215
	    si.dwFlags |= STARTF_USESTDHANDLES;
216
	    si.hStdInput  = dupIN;
217
	    si.hStdOutput = dupOUT;
218
	    si.hStdError  = dupERR;
219
	}
4394 ripley 220
    }
44013 ripley 221
 
222
    if(enc == CE_UTF8) {
223
	int n = strlen(ecmd); /* max no of chars */
52804 ripley 224
	wchar_t wcmd[n+1];
44013 ripley 225
	Rf_utf8towcs(wcmd, ecmd, n+1);
226
	ret = CreateProcessW(NULL, wcmd, &sa, &sa, TRUE,
45070 ripley 227
			     (newconsole && (visible == 1)) ?
44013 ripley 228
			     CREATE_NEW_CONSOLE : 0,
51030 murdoch 229
			     NULL, NULL, &wsi, pi);
45070 ripley 230
    } else
44013 ripley 231
	ret = CreateProcess(NULL, ecmd, &sa, &sa, TRUE,
45070 ripley 232
			    (newconsole && (visible == 1)) ?
44013 ripley 233
			    CREATE_NEW_CONSOLE : 0,
51030 murdoch 234
			    NULL, NULL, &si, pi);
44013 ripley 235
 
53870 murdoch 236
    if (inpipe) {
237
	CloseHandle(dupIN);
238
	CloseHandle(dupOUT);
239
	CloseHandle(dupERR);
240
    }
52804 ripley 241
    if (!ret)
60844 ripley 242
	snprintf(RunError, 500, _("'CreateProcess' failed to run '%s'"), ecmd);
52804 ripley 243
    else CloseHandle(pi->hThread);
11169 ripley 244
    free(ecmd);
51030 murdoch 245
    return;
4394 ripley 246
}
247
 
248
static int pwait(HANDLE p)
249
{
250
    DWORD ret;
251
 
252
    WaitForSingleObject(p, INFINITE);
253
    GetExitCodeProcess(p, &ret);
254
    return ret;
255
}
256
 
12256 pd 257
/* used in rpipeOpen */
258
static DWORD CALLBACK
4394 ripley 259
threadedwait(LPVOID param)
260
{
261
    rpipe *p = (rpipe *) param;
262
 
51030 murdoch 263
    p->exitcode = pwait(p->pi.hProcess);
4394 ripley 264
    FlushFileBuffers(p->write);
265
    FlushFileBuffers(p->read);
266
    p->active = 0;
51030 murdoch 267
    CloseHandle(p->thread);
268
    p->thread = NULL;
4394 ripley 269
    return 0;
270
}
271
 
44201 ripley 272
char *runerror(void)
4394 ripley 273
{
274
    return RunError;
275
}
276
 
52814 ripley 277
static HANDLE getInputHandle(const char *fin)
51015 murdoch 278
{
52814 ripley 279
    if (fin && fin[0]) {
51015 murdoch 280
	SECURITY_ATTRIBUTES sa;
281
	sa.nLength = sizeof(sa);
282
	sa.lpSecurityDescriptor = NULL;
52814 ripley 283
	sa.bInheritHandle = TRUE;
284
	HANDLE hIN = CreateFile(fin, GENERIC_READ, 0,
285
				&sa, OPEN_EXISTING, 0, NULL);
51015 murdoch 286
	if (hIN == INVALID_HANDLE_VALUE) {
52814 ripley 287
	    snprintf(RunError, 500, 
288
		     "unable to redirect input from '%s'", fin);
51015 murdoch 289
	    return NULL;
290
	}
291
	return hIN;
55955 murdoch 292
    } else if (fin) {
293
        /* GetStdHandle returns NULL for processes like RGui with no standard handles defined */
294
    	HANDLE result = GetStdHandle(STD_INPUT_HANDLE);
295
    	if (result) return result;
296
    }
51015 murdoch 297
    return INVALID_HANDLE_VALUE;
298
}
4394 ripley 299
 
53348 ripley 300
static HANDLE getOutputHandle(const char *fout, int type)
52814 ripley 301
{
302
    if (fout && fout[0]) {
303
	SECURITY_ATTRIBUTES sa;
304
	sa.nLength = sizeof(sa);
305
	sa.lpSecurityDescriptor = NULL;
306
	sa.bInheritHandle = TRUE;
307
	HANDLE hOUT = CreateFile(fout, GENERIC_WRITE, 0,
308
				 &sa, CREATE_ALWAYS, 0, NULL);
309
	if (hOUT == INVALID_HANDLE_VALUE) {
310
	    snprintf(RunError, 500, 
311
		     "unable to redirect output to '%s'", fout);
312
	    return NULL;
52836 ripley 313
	} else return hOUT;
55955 murdoch 314
    } else if (fout) {
315
        /* GetStdHandle returns NULL for processes like RGui */
316
        HANDLE result = GetStdHandle(type ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
317
        if (result) return result;
318
    }
52814 ripley 319
    return INVALID_HANDLE_VALUE;
320
}
321
 
51030 murdoch 322
BOOL CALLBACK TerminateWindow(HWND hwnd, LPARAM lParam)
323
{
324
    DWORD ID ;
325
 
326
    GetWindowThreadProcessId(hwnd, &ID);
327
 
328
    if (ID == (DWORD)lParam)
52814 ripley 329
	PostMessage(hwnd, WM_CLOSE, 0, 0);
51030 murdoch 330
    return TRUE;
331
}
332
 
333
/* Terminate the process pwait2 is waiting for. */
334
 
335
extern void GA_askok(const char *info);
336
 
337
static void terminate_process(void *p)
338
{
339
    PROCESS_INFORMATION *pi = (PROCESS_INFORMATION*)p;
340
    EnumWindows((WNDENUMPROC)TerminateWindow, (LPARAM)pi->dwProcessId);
52814 ripley 341
 
51030 murdoch 342
    if (WaitForSingleObject(pi->hProcess, 5000) == WAIT_TIMEOUT) {
52814 ripley 343
	if (R_Interactive)
344
	    GA_askok(_("Child process not responding.  R will terminate it."));
345
	TerminateProcess(pi->hProcess, 99);
51030 murdoch 346
    }
347
}
348
 
349
static int pwait2(HANDLE p)
350
{
351
    DWORD ret;
352
 
52814 ripley 353
    while( WaitForSingleObject(p, 100) == WAIT_TIMEOUT )
354
	R_CheckUserInterrupt();
355
 
51030 murdoch 356
    GetExitCodeProcess(p, &ret);
357
    return ret;
358
}
359
 
12256 pd 360
/*
52813 ripley 361
  Used for external commands in file.show() and edit(), and for
52814 ripley 362
  system(intern=FALSE).  Also called from postscript().
52813 ripley 363
 
364
  wait != 0 says wait for child to terminate before returning.
365
  visible = -1, 0, 1 for hide, minimized, default
52814 ripley 366
  fin is either NULL or the name of a file from which to
52813 ripley 367
  redirect stdin for the child.
53348 ripley 368
  fout/ferr are NULL (use NUL:), "" (use standard streams) or filenames.
52813 ripley 369
*/
52814 ripley 370
int runcmd(const char *cmd, cetype_t enc, int wait, int visible,
371
	   const char *fin, const char *fout, const char *ferr)
4394 ripley 372
{
52837 ripley 373
    HANDLE hIN = getInputHandle(fin), hOUT, hERR;
51030 murdoch 374
    int ret = 0;
375
    PROCESS_INFORMATION pi;
53870 murdoch 376
    int close1 = 0, close2 = 0, close3 = 0;
377
 
378
    if (hIN && fin && fin[0]) close1 = 1;
12256 pd 379
 
53348 ripley 380
    hOUT = getOutputHandle(fout, 0);
381
    if (!hOUT) return 1;
53572 ripley 382
    if (fout && fout[0]) close2 = 1;
53348 ripley 383
    if (fout && fout[0] && ferr && streql(fout, ferr)) hERR = hOUT;
384
    else { 
385
	hERR = getOutputHandle(ferr, 1);
386
	if (!hERR) return 1;
387
	if (ferr && ferr[0]) close3 = 1;
388
    }
52836 ripley 389
 
53348 ripley 390
 
52802 ripley 391
    memset(&pi, 0, sizeof(pi));
52814 ripley 392
    pcreate(cmd, enc, !wait, visible, hIN, hOUT, hERR, &pi);
51030 murdoch 393
    if (!pi.hProcess) return NOLAUNCH;
42618 ripley 394
    if (wait) {
52814 ripley 395
	RCNTXT cntxt;
396
	begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
51030 murdoch 397
		 R_NilValue, R_NilValue);
52814 ripley 398
	cntxt.cend = &terminate_process;
399
	cntxt.cenddata = &pi;
51030 murdoch 400
	ret = pwait2(pi.hProcess);
401
	endcontext(&cntxt);
62583 ripley 402
	snprintf(RunError, 501, _("Exit code was %d"), ret);
42618 ripley 403
	ret &= 0xffff;
404
    } else ret = 0;
51030 murdoch 405
    CloseHandle(pi.hProcess);
53870 murdoch 406
    if (close1) CloseHandle(hIN);
53348 ripley 407
    if (close2) CloseHandle(hOUT);
408
    if (close3) CloseHandle(hERR);
4394 ripley 409
    return ret;
410
}
411
 
12256 pd 412
/*
413
   finput is either NULL or the name of a file from which to
414
     redirect stdin for the child.
415
   visible = -1, 0, 1 for hide, minimized, default
40468 ripley 416
   io = 0 to read stdout from pipe, 1 to write to pipe,
52814 ripley 417
   2 to read stderr from pipe, 
418
   3 to read both stdout and stderr from pipe.
12256 pd 419
 */
45070 ripley 420
rpipe * rpipeOpen(const char *cmd, cetype_t enc, int visible,
53572 ripley 421
		  const char *finput, int io,
422
		  const char *fout, const char *ferr)
4394 ripley 423
{
424
    rpipe *r;
53572 ripley 425
    HANDLE hTHIS, hIN, hOUT, hERR, hReadPipe, hWritePipe;
4394 ripley 426
    DWORD id;
12256 pd 427
    BOOL res;
53870 murdoch 428
    int close1 = 0, close2 = 0, close3 = 0;
4394 ripley 429
 
11169 ripley 430
    if (!(r = (rpipe *) malloc(sizeof(struct structRPIPE)))) {
32888 ripley 431
	strcpy(RunError, _("Insufficient memory (rpipeOpen)"));
4394 ripley 432
	return NULL;
433
    }
51030 murdoch 434
    r->active = 0;
435
    r->pi.hProcess = NULL;
436
    r->thread = NULL;
51015 murdoch 437
    res = CreatePipe(&hReadPipe, &hWritePipe, NULL, 0);
438
    if (res == FALSE) {
439
	rpipeClose(r);
52804 ripley 440
	strcpy(RunError, "CreatePipe failed");
51015 murdoch 441
	return NULL;
442
    }
443
    if(io == 1) { /* pipe for R to write to */
27774 murdoch 444
	hTHIS = GetCurrentProcess();
51015 murdoch 445
	r->read = hReadPipe;
446
	DuplicateHandle(hTHIS, hWritePipe, hTHIS, &r->write,
12256 pd 447
			0, FALSE, DUPLICATE_SAME_ACCESS);
51015 murdoch 448
	CloseHandle(hWritePipe);
12256 pd 449
	CloseHandle(hTHIS);
53348 ripley 450
	/* This sends stdout and stderr to NUL: */
52814 ripley 451
	pcreate(cmd, enc, 1, visible,
452
		r->read, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
453
		&(r->pi));
12256 pd 454
	r->active = 1;
51030 murdoch 455
	if (!r->pi.hProcess) return NULL; else return r;
12256 pd 456
    }
52837 ripley 457
 
51015 murdoch 458
    /* pipe for R to read from */
4394 ripley 459
    hTHIS = GetCurrentProcess();
51015 murdoch 460
    r->write = hWritePipe;
461
    DuplicateHandle(hTHIS, hReadPipe, hTHIS, &r->read,
4394 ripley 462
		    0, FALSE, DUPLICATE_SAME_ACCESS);
51015 murdoch 463
    CloseHandle(hReadPipe);
4394 ripley 464
    CloseHandle(hTHIS);
52814 ripley 465
 
53348 ripley 466
    hIN = getInputHandle(finput); /* a file or (usually NUL:) */
53870 murdoch 467
 
468
    if (hIN && finput && finput[0]) close1 = 1;
469
 
53572 ripley 470
    if ((io == 0 || io == 3)) 
471
	hOUT = r->write;
472
    else {
473
	if (fout && fout[0]) close2 = 1;
474
 	hOUT = getOutputHandle(fout, 0);
475
    }
476
    if (io >= 2) 
477
	hERR = r->write;
478
    else {
479
	if (ferr && ferr[0]) close3 = 1;
480
	hERR = getOutputHandle(ferr, 1);
481
    }
482
    pcreate(cmd, enc, 0, visible, hIN, hOUT, hERR, &(r->pi));
53870 murdoch 483
    if (close1) CloseHandle(hIN);
53572 ripley 484
    if (close2) CloseHandle(hOUT);
485
    if (close3) CloseHandle(hERR);
52814 ripley 486
 
4394 ripley 487
    r->active = 1;
51030 murdoch 488
    if (!r->pi.hProcess)
4394 ripley 489
	return NULL;
51030 murdoch 490
    if (!(r->thread = CreateThread(NULL, 0, threadedwait, r, 0, &id))) {
4394 ripley 491
	rpipeClose(r);
52804 ripley 492
	strcpy(RunError, "CreateThread failed");
4394 ripley 493
	return NULL;
494
    }
495
    return r;
496
}
497
 
52814 ripley 498
static void
51030 murdoch 499
rpipeTerminate(rpipe * r)
500
{
501
    if (r->thread) {
52814 ripley 502
	TerminateThread(r->thread, 0);
503
	CloseHandle(r->thread);
504
	r->thread = NULL;
51030 murdoch 505
    }
506
    if (r->active) {
52814 ripley 507
	terminate_process(&(r->pi));
508
	r->active = 0;
51030 murdoch 509
    }
510
}
511
 
40513 ripley 512
#include "graphapp/ga.h"
513
extern Rboolean UserBreak;
4394 ripley 514
 
12256 pd 515
int
4394 ripley 516
rpipeGetc(rpipe * r)
517
{
518
    DWORD a, b;
519
    char  c;
520
 
521
    if (!r)
522
	return NOLAUNCH;
523
    while (PeekNamedPipe(r->read, NULL, 0, NULL, &a, NULL)) {
524
	if (!a && !r->active) {
525
	    /* I got a case in which process terminated after Peek.. */
526
	    PeekNamedPipe(r->read, NULL, 0, NULL, &a, NULL);
12256 pd 527
	    if (!a) return NOLAUNCH;/* end of pipe */
4394 ripley 528
	}
529
	if (a) {
530
	    if (ReadFile(r->read, &c, 1, &b, NULL) == TRUE)
531
		return c;
532
	    else
533
		return NOLAUNCH;/* error but...treated as eof */
534
	}
40513 ripley 535
	/* we want to look for user break here */
536
	while (peekevent()) doevent();
537
	if (UserBreak) {
51030 murdoch 538
	    rpipeTerminate(r);
40513 ripley 539
	    break;
540
	}
541
	R_ProcessEvents();
542
	Sleep(100);
4394 ripley 543
    }
544
    return NOLAUNCH;		/* again.. */
545
}
546
 
547
 
12256 pd 548
char * rpipeGets(rpipe * r, char *buf, int len)
4394 ripley 549
{
550
    int   i, c;
551
 
12256 pd 552
    if ((len < 2) || !r) return NULL;
4394 ripley 553
    for (i = 0; i < (len - 1); i++) {
554
	if ((c = rpipeGetc(r)) == NOLAUNCH) {
12256 pd 555
	    if (i == 0) return NULL;
4394 ripley 556
	    else {
557
		buf[i] = '\0';
558
		return buf;
559
	    }
560
	}
561
	buf[i] = c;
562
	if (c == '\n') {
563
	    if ((i > 0) && (buf[i - 1] == '\r')) {
564
		buf[i - 1] = '\n';
565
		buf[i] = '\0';
566
	    } else
567
		buf[i + 1] = '\0';
568
	    return buf;
569
	}
570
    }
571
    buf[len - 1] = '\0';
572
    return buf;
573
}
574
 
12256 pd 575
int rpipeClose(rpipe * r)
4394 ripley 576
{
577
    int   i;
578
 
12256 pd 579
    if (!r) return NOLAUNCH;
51030 murdoch 580
    rpipeTerminate(r);
4394 ripley 581
    CloseHandle(r->read);
582
    CloseHandle(r->write);
51030 murdoch 583
    CloseHandle(r->pi.hProcess);
4394 ripley 584
    i = r->exitcode;
11169 ripley 585
    free(r);
42618 ripley 586
    return i &= 0xffff;
4394 ripley 587
}
6153 guido 588
 
12256 pd 589
/* ------------------- Windows pipe connections --------------------- */
6153 guido 590
 
12256 pd 591
#include <Fileio.h>
592
#include <Rconnections.h>
6153 guido 593
 
12256 pd 594
typedef struct Wpipeconn {
595
    rpipe *rp;
596
} *RWpipeconn;
6153 guido 597
 
598
 
18611 ripley 599
static Rboolean Wpipe_open(Rconnection con)
12256 pd 600
{
601
    rpipe *rp;
602
    int visible = -1, io;
6153 guido 603
 
12256 pd 604
    io = con->mode[0] == 'w';
605
    if(io) visible = 1; /* Somewhere to put the output */
53572 ripley 606
    rp = rpipeOpen(con->description, con->enc, visible, NULL, io, NULL, NULL);
18611 ripley 607
    if(!rp) {
608
	warning("cannot open cmd `%s'", con->description);
609
	return FALSE;
610
    }
12256 pd 611
    ((RWpipeconn)(con->private))->rp = rp;
612
    con->isopen = TRUE;
613
    con->canwrite = io;
614
    con->canread = !con->canwrite;
615
    if(strlen(con->mode) >= 2 && con->mode[1] == 'b') con->text = FALSE;
616
    else con->text = TRUE;
617
    con->save = -1000;
18611 ripley 618
    return TRUE;
12256 pd 619
}
6153 guido 620
 
12256 pd 621
static void Wpipe_close(Rconnection con)
622
{
61527 ripley 623
    con->status = rpipeClose( ((RWpipeconn)con->private) ->rp);
12256 pd 624
    con->isopen = FALSE;
625
}
626
 
627
static void Wpipe_destroy(Rconnection con)
628
{
629
    free(con->private);
630
}
631
 
632
 
633
static int Wpipe_fgetc(Rconnection con)
634
{
635
    rpipe *rp = ((RWpipeconn)con->private) ->rp;
636
    int c;
27774 murdoch 637
 
13371 ripley 638
    c = rpipeGetc(rp);
12256 pd 639
    return c == NOLAUNCH ? R_EOF : c;
640
}
641
 
642
 
31166 ripley 643
static double null_seek(Rconnection con, double where, int origin, int rw)
12256 pd 644
{
32888 ripley 645
    error(_("seek not enabled for this connection"));
12256 pd 646
    return 0; /* -Wall */
647
}
648
 
13315 ripley 649
static void null_truncate(Rconnection con)
650
{
32888 ripley 651
    error(_("truncate not enabled for this connection"));
13315 ripley 652
}
653
 
12256 pd 654
static int Wpipe_fflush(Rconnection con)
655
{
656
    BOOL res;
657
 
658
    rpipe *rp = ((RWpipeconn)con->private) ->rp;
659
    res = FlushFileBuffers(rp->write);
660
    return res ? 0 : EOF;
661
}
662
 
663
static size_t Wpipe_read(void *ptr, size_t size, size_t nitems,
664
			Rconnection con)
665
{
666
    rpipe *rp = ((RWpipeconn)con->private) ->rp;
667
    DWORD ntoread, read;
668
 
669
    while (PeekNamedPipe(rp->read, NULL, 0, NULL, &ntoread, NULL)) {
670
	if (!ntoread && !rp->active) {
671
	    /* I got a case in which process terminated after Peek.. */
672
	    PeekNamedPipe(rp->read, NULL, 0, NULL, &ntoread, NULL);
673
	    if (!ntoread) return 0; /* end of pipe */
674
	}
675
	if (ntoread) {
676
	    if (ReadFile(rp->read, ptr, nitems * size, &read, NULL) == TRUE)
677
		return read/size;
678
	    else return 0; /* error */
679
	}
680
    }
681
    return 0;
682
}
683
 
684
static size_t Wpipe_write(const void *ptr, size_t size, size_t nitems,
685
			 Rconnection con)
686
{
687
    rpipe *rp = ((RWpipeconn)con->private) ->rp;
688
    DWORD towrite = nitems * size, write, ret;
689
 
690
    if(!rp->active) return 0;
51030 murdoch 691
    GetExitCodeProcess(rp->pi.hProcess, &ret);
12256 pd 692
    if(ret != STILL_ACTIVE) {
693
	rp->active = 0;
694
	warning("broken Windows pipe");
695
	return 0;
696
    }
697
    if (WriteFile(rp->write, ptr, towrite, &write, NULL) != 0)
698
	return write/size;
699
    else return 0;
700
}
701
 
63220 ripley 702
#define BUFSIZE 10000
12256 pd 703
static int Wpipe_vfprintf(Rconnection con, const char *format, va_list ap)
704
{
63221 ripley 705
    R_CheckStack2(BUFSIZE);
63181 ripley 706
    char buf[BUFSIZE], *b = buf;
63220 ripley 707
    int res = 0;
12256 pd 708
 
709
    res = vsnprintf(b, BUFSIZE, format, ap);
710
    if(res < 0) { /* a failure indication, so try again */
63220 ripley 711
	b[BUFSIZE -1] = '\0';
712
	warning("printing of extremely long output is truncated");
713
	res = BUFSIZE;
12256 pd 714
    }
63220 ripley 715
    return Wpipe_write(buf, res, 1, con);
12256 pd 716
}
717
 
63219 ripley 718
 
44013 ripley 719
Rconnection newWpipe(const char *description, int ienc, const char *mode)
12256 pd 720
{
721
    Rconnection new;
42638 murdoch 722
    char *command;
723
    int len;
45070 ripley 724
 
12256 pd 725
    new = (Rconnection) malloc(sizeof(struct Rconn));
32888 ripley 726
    if(!new) error(_("allocation of pipe connection failed"));
12256 pd 727
    new->class = (char *) malloc(strlen("pipe") + 1);
728
    if(!new->class) {
729
	free(new);
32888 ripley 730
	error(_("allocation of pipe connection failed"));
12256 pd 731
    }
732
    strcpy(new->class, "pipe");
45070 ripley 733
 
42638 murdoch 734
    len = strlen(getenv("COMSPEC")) + strlen(description) + 5;
735
    command = (char *) malloc(len);
45070 ripley 736
    if (command)
737
	new->description = (char *) malloc(len);
42638 murdoch 738
    else
45070 ripley 739
	new->description = NULL;
740
 
12256 pd 741
    if(!new->description) {
45070 ripley 742
	free(command); free(new->class); free(new);
32888 ripley 743
	error(_("allocation of pipe connection failed"));
12256 pd 744
    }
45070 ripley 745
 
746
    /* We always use COMSPEC here, not R_SHELL or SHELL,
747
       for compatibility with Rterm.
748
       We also use /c for the same reason.
749
    */
750
 
42638 murdoch 751
    strcpy(command, getenv("COMSPEC"));
752
    strcat(command, " /c ");
753
    strcat(command, description);
45070 ripley 754
 
44013 ripley 755
    init_con(new, command, ienc, mode);
42638 murdoch 756
    free(command);
45070 ripley 757
 
12256 pd 758
    new->open = &Wpipe_open;
759
    new->close = &Wpipe_close;
760
    new->destroy = &Wpipe_destroy;
761
    new->vfprintf = &Wpipe_vfprintf;
762
    new->fgetc = &Wpipe_fgetc;
763
    new->seek = &null_seek;
13315 ripley 764
    new->truncate = &null_truncate;
12256 pd 765
    new->fflush = &Wpipe_fflush;
766
    new->read = &Wpipe_read;
767
    new->write = &Wpipe_write;
768
    new->private = (void *) malloc(sizeof(struct Wpipeconn));
769
    if(!new->private) {
770
	free(new->description); free(new->class); free(new);
32888 ripley 771
	error(_("allocation of pipe connection failed"));
12256 pd 772
    }
773
    return new;
774
}
42480 ripley 775
 
776
 
777
SEXP do_syswhich(SEXP call, SEXP op, SEXP args, SEXP env)
778
{
779
    SEXP nm, ans;
780
    int i, n;
781
 
782
    checkArity(op, args);
783
    nm = CAR(args);
784
    if(!isString(nm))
61427 ripley 785
	error(_("'names' is not a character vector"));
42480 ripley 786
    n = LENGTH(nm);
787
    PROTECT(ans = allocVector(STRSXP, n));
788
    for(i = 0; i < n; i++) {
61427 ripley 789
	if (STRING_ELT(nm, i) == NA_STRING) {
790
	    SET_STRING_ELT(ans, i, NA_STRING);
791
	} else {
792
	    const char *this = CHAR(STRING_ELT(nm, i));
793
	    char *that = expandcmd(this, 1);
794
	    SET_STRING_ELT(ans, i, mkChar(that ? that : ""));
795
	    free(that);
796
	}
42480 ripley 797
    }
798
    setAttrib(ans, R_NamesSymbol, nm);
799
    UNPROTECT(1);
800
    return ans;
801
}