/src/r-source/src/main/main.c
Line | Count | Source |
1 | | /* |
2 | | * R : A Computer Language for Statistical Data Analysis |
3 | | * Copyright (C) 1998-2026 The R Core Team |
4 | | * Copyright (C) 2002-2005 The R Foundation |
5 | | * Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka |
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 |
18 | | * along with this program; if not, a copy is available at |
19 | | * https://www.R-project.org/Licenses/ |
20 | | */ |
21 | | |
22 | | #ifdef HAVE_CONFIG_H |
23 | | #include <config.h> |
24 | | #endif |
25 | | |
26 | | #include <math.h> /* avoid redefinition of extern in Defn.h */ |
27 | | #include <float.h> |
28 | | #include <ctype.h> |
29 | | #include <stdlib.h> |
30 | | #include <string.h> |
31 | | |
32 | | #define __MAIN__ |
33 | | #define R_USE_SIGNALS 1 |
34 | | #include <Defn.h> |
35 | | #include <Internal.h> |
36 | | #include "Rinterface.h" |
37 | | #include "IOStuff.h" |
38 | | #include "Fileio.h" |
39 | | #include "Parse.h" |
40 | | #include "Startup.h" |
41 | | |
42 | | #include <locale.h> |
43 | | #include <R_ext/Print.h> |
44 | | |
45 | | #ifdef ENABLE_NLS |
46 | | attribute_hidden void nl_Rdummy(void) |
47 | 0 | { |
48 | | /* force this in as packages use it */ |
49 | 0 | dgettext("R", "dummy - do not translate"); |
50 | 0 | } |
51 | | #endif |
52 | | |
53 | | |
54 | | /* The 'real' main() program is in Rmain.c on Unix-alikes, and |
55 | | src/gnuwin/front-ends/graphappmain.c on Windows, unless of course |
56 | | R is embedded */ |
57 | | |
58 | | /* Global Variables: For convenience, all interpreter global symbols |
59 | | * ================ are declared in Defn.h as extern -- and defined here. |
60 | | * |
61 | | * NOTE: This is done by using some preprocessor trickery. If __MAIN__ |
62 | | * is defined as above, there is a sneaky |
63 | | * #define extern |
64 | | * so that the same code produces both declarations and definitions. |
65 | | * |
66 | | * This does not include user interface symbols which are included |
67 | | * in separate platform dependent modules. |
68 | | */ |
69 | | |
70 | | attribute_hidden |
71 | | void Rf_callToplevelHandlers(SEXP expr, SEXP value, Rboolean succeeded, |
72 | | Rboolean visible); |
73 | | |
74 | | static int ParseBrowser(SEXP, SEXP); |
75 | | |
76 | | |
77 | | /* Read-Eval-Print Loop [ =: REPL = repl ] with input from a file */ |
78 | | |
79 | | static void R_ReplFile(FILE *fp, SEXP rho) |
80 | 76 | { |
81 | 76 | ParseStatus status; |
82 | 76 | int savestack; |
83 | 76 | RCNTXT cntxt; |
84 | | |
85 | 76 | R_InitSrcRefState(&cntxt); |
86 | 76 | savestack = R_PPStackTop; |
87 | 5.09k | for(;;) { |
88 | 5.09k | R_PPStackTop = savestack; |
89 | 5.09k | R_CurrentExpr = R_Parse1File(fp, 1, &status); |
90 | 5.09k | switch (status) { |
91 | 3.04k | case PARSE_NULL: |
92 | 3.04k | break; |
93 | 1.97k | case PARSE_OK: |
94 | 1.97k | R_Visible = FALSE; |
95 | 1.97k | R_EvalDepth = 0; |
96 | 1.97k | resetTimeLimits(); |
97 | 1.97k | PROTECT(R_CurrentExpr); |
98 | 1.97k | R_CurrentExpr = eval(R_CurrentExpr, rho); |
99 | 1.97k | SET_SYMVALUE(R_LastvalueSymbol, R_CurrentExpr); |
100 | 1.97k | UNPROTECT(1); |
101 | 1.97k | if (R_Visible) |
102 | 0 | PrintValueEnv(R_CurrentExpr, rho); |
103 | 1.97k | if( R_CollectWarnings ) |
104 | 0 | PrintWarnings(); |
105 | 1.97k | break; |
106 | 0 | case PARSE_ERROR: |
107 | 0 | R_FinalizeSrcRefState(); |
108 | 0 | parseError(R_NilValue, R_ParseError); |
109 | 0 | break; |
110 | 76 | case PARSE_EOF: |
111 | 76 | endcontext(&cntxt); |
112 | 76 | R_FinalizeSrcRefState(); |
113 | 76 | return; |
114 | 0 | break; |
115 | 0 | case PARSE_INCOMPLETE: |
116 | | /* can't happen: just here to quieten -Wall */ |
117 | 0 | break; |
118 | 5.09k | } |
119 | 5.09k | } |
120 | 76 | } |
121 | | |
122 | | /* Read-Eval-Print loop with interactive input */ |
123 | | static int prompt_type; |
124 | | static char BrowsePrompt[20]; |
125 | | |
126 | | static const char *R_PromptString(int browselevel, int type) |
127 | 0 | { |
128 | 0 | if (R_NoEcho) { |
129 | 0 | BrowsePrompt[0] = '\0'; |
130 | 0 | return BrowsePrompt; |
131 | 0 | } |
132 | 0 | else { |
133 | 0 | if(type == 1) { |
134 | 0 | if(browselevel) { |
135 | 0 | snprintf(BrowsePrompt, 20, "Browse[%d]> ", browselevel); |
136 | 0 | return BrowsePrompt; |
137 | 0 | } |
138 | 0 | return CHAR(STRING_ELT(GetOption1(install("prompt")), 0)); |
139 | 0 | } |
140 | 0 | else { |
141 | 0 | return CHAR(STRING_ELT(GetOption1(install("continue")), 0)); |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | | /* |
147 | | This is a reorganization of the REPL (Read-Eval-Print Loop) to separate |
148 | | the loop from the actions of the body. The motivation is to make the iteration |
149 | | code (Rf_ReplIteration) available as a separately callable routine |
150 | | to avoid cutting and pasting it when one wants a single iteration |
151 | | of the loop. This is needed as we allow different implementations |
152 | | of event loops. Currently (summer 2002), we have a package in |
153 | | preparation that uses Rf_ReplIteration within either the |
154 | | Tcl or Gtk event loop and allows either (or both) loops to |
155 | | be used as a replacement for R's loop and take over the event |
156 | | handling for the R process. |
157 | | |
158 | | The modifications here are intended to leave the semantics of the REPL |
159 | | unchanged, just separate into routines. So the variables that maintain |
160 | | the state across iterations of the loop are organized into a structure |
161 | | and passed to Rf_ReplIteration() from Rf_ReplConsole(). |
162 | | */ |
163 | | |
164 | | |
165 | | /** |
166 | | (local) Structure for maintaining and exchanging the state between |
167 | | Rf_ReplConsole and its worker routine Rf_ReplIteration which is the |
168 | | implementation of the body of the REPL. |
169 | | |
170 | | In the future, we may need to make this accessible to packages |
171 | | and so put it into one of the public R header files. |
172 | | */ |
173 | | typedef struct { |
174 | | ParseStatus status; |
175 | | int prompt_type; |
176 | | int browselevel; |
177 | | unsigned char buf[CONSOLE_BUFFER_SIZE+1]; |
178 | | unsigned char *bufp; |
179 | | } R_ReplState; |
180 | | |
181 | | |
182 | | /** |
183 | | This is the body of the REPL. |
184 | | It attempts to parse the first line or expression of its input, |
185 | | and optionally request input from the user if none is available. |
186 | | If the input can be parsed correctly, |
187 | | i) the resulting expression is evaluated, |
188 | | ii) the result assigned to .Last.Value, |
189 | | iii) top-level task handlers are invoked. |
190 | | |
191 | | If the input cannot be parsed, i.e. there is a syntax error, |
192 | | it is incomplete, or we encounter an end-of-file, then we |
193 | | change the prompt accordingly. |
194 | | |
195 | | The "cursor" for the input buffer is moved to the next starting |
196 | | point, i.e. the end of the first line or after the first ;. |
197 | | */ |
198 | | attribute_hidden int |
199 | | Rf_ReplIteration(SEXP rho, int savestack, int browselevel, R_ReplState *state) |
200 | 0 | { |
201 | | /* clear warnings that might have accumulated during a jump to top level */ |
202 | 0 | if (R_CollectWarnings) |
203 | 0 | PrintWarnings(); |
204 | |
|
205 | 0 | if(!*state->bufp) { |
206 | 0 | R_Busy(0); |
207 | 0 | if (R_ReadConsole(R_PromptString(browselevel, state->prompt_type), |
208 | 0 | state->buf, CONSOLE_BUFFER_SIZE, 1) == 0) |
209 | 0 | return(-1); |
210 | 0 | state->bufp = state->buf; |
211 | 0 | } |
212 | | #ifdef SHELL_ESCAPE /* not default */ |
213 | | if (*state->bufp == '!' && state->buf == state->bufp |
214 | | && state->prompt_type == 1) { |
215 | | R_system(&(state->buf[1])); |
216 | | state->buf[0] = '\0'; |
217 | | return(0); |
218 | | } |
219 | | #endif /* SHELL_ESCAPE */ |
220 | 0 | int c; |
221 | 0 | while((c = *state->bufp)) { |
222 | 0 | state->bufp++; |
223 | 0 | R_IoBufferPutc(c, &R_ConsoleIob); |
224 | 0 | if(c == ';' || c == '\n') break; |
225 | 0 | } |
226 | |
|
227 | 0 | R_PPStackTop = savestack; |
228 | 0 | R_CurrentExpr = R_Parse1Buffer(&R_ConsoleIob, 0, &state->status); |
229 | |
|
230 | 0 | switch(state->status) { |
231 | | |
232 | 0 | case PARSE_NULL: |
233 | | |
234 | | /* The intention here is to break on CR but not on other |
235 | | null statements: see PR#9063 */ |
236 | 0 | if (browselevel && !R_DisableNLinBrowser |
237 | 0 | && !strcmp((char *) state->buf, "\n")) return -1; |
238 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
239 | 0 | state->prompt_type = 1; |
240 | 0 | return 1; |
241 | | |
242 | 0 | case PARSE_OK: |
243 | 0 | { |
244 | 0 | R_IoBufferReadReset(&R_ConsoleIob); |
245 | 0 | R_CurrentExpr = R_Parse1Buffer(&R_ConsoleIob, 1, &state->status); |
246 | 0 | int browsevalue = 0; /* -Wmaybe-uninit.. */ |
247 | 0 | if (browselevel) { |
248 | 0 | browsevalue = ParseBrowser(R_CurrentExpr, rho); |
249 | 0 | if(browsevalue == 1) return -1; |
250 | 0 | if(browsevalue == 2) { |
251 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
252 | 0 | return 0; |
253 | 0 | } |
254 | | /* PR#15770 We don't want to step into expressions entered at the debug prompt. |
255 | | + PR#18885 Disable debugging of this environment for the duration of the call. */ |
256 | 0 | browsevalue = -RDEBUG(rho); |
257 | 0 | SET_RDEBUG(rho, 0); |
258 | 0 | } |
259 | 0 | R_Visible = FALSE; |
260 | 0 | R_EvalDepth = 0; |
261 | 0 | resetTimeLimits(); |
262 | 0 | SEXP thisExpr = PROTECT(R_CurrentExpr); |
263 | 0 | R_Busy(1); |
264 | 0 | SEXP value = PROTECT(eval(thisExpr, rho)); |
265 | 0 | SET_SYMVALUE(R_LastvalueSymbol, value); |
266 | 0 | if (NO_REFERENCES(value)) |
267 | 0 | INCREMENT_REFCNT(value); |
268 | 0 | bool wasDisplayed = R_Visible; |
269 | 0 | if (R_Visible) |
270 | 0 | PrintValueEnv(value, rho); |
271 | 0 | if (R_CollectWarnings) |
272 | 0 | PrintWarnings(); |
273 | 0 | Rf_callToplevelHandlers(thisExpr, value, TRUE, wasDisplayed); |
274 | 0 | R_CurrentExpr = value; /* Necessary? Doubt it. */ |
275 | 0 | UNPROTECT(2); /* thisExpr, value */ |
276 | 0 | if (browselevel && browsevalue < 0) |
277 | | /* Done evaluating REPL expression, continue stepping. */ |
278 | 0 | SET_RDEBUG(rho, 1); |
279 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
280 | 0 | state->prompt_type = 1; |
281 | 0 | return(1); |
282 | 0 | } |
283 | 0 | case PARSE_ERROR: |
284 | |
|
285 | 0 | state->prompt_type = 1; |
286 | 0 | parseError(R_NilValue, 0); |
287 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
288 | 0 | return(1); |
289 | | |
290 | 0 | case PARSE_EOF: |
291 | | /* the parser thinks it is EOF but it may not have seen all of the |
292 | | input, so postpone the decision to exit until there is really |
293 | | no more input (the parser may be seeing a sequence of spaces) |
294 | | PR#15941 */ |
295 | 0 | case PARSE_INCOMPLETE: |
296 | 0 | R_IoBufferReadReset(&R_ConsoleIob); |
297 | 0 | state->prompt_type = 2; |
298 | 0 | return(2); |
299 | 0 | } |
300 | | |
301 | 0 | return(0); |
302 | 0 | } |
303 | | |
304 | | static void R_ReplConsole(SEXP rho, int savestack, int browselevel) |
305 | 0 | { |
306 | 0 | int status; |
307 | 0 | R_ReplState state = { PARSE_NULL, 1, 0, "", NULL}; |
308 | |
|
309 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
310 | 0 | state.buf[0] = '\0'; |
311 | 0 | state.buf[CONSOLE_BUFFER_SIZE] = '\0'; |
312 | | /* stopgap measure if line > CONSOLE_BUFFER_SIZE chars */ |
313 | 0 | state.bufp = state.buf; |
314 | | #ifdef XTR_VERBOSE |
315 | | if(R_Verbose) // if(R_Verbose >= 2) if ever the 'verbose' option would be positive integer |
316 | | REprintf(" >R_ReplConsole(): before \"for(;;)\" {main.c}\n"); |
317 | | #endif |
318 | 0 | for(;;) { |
319 | 0 | status = Rf_ReplIteration(rho, savestack, browselevel, &state); |
320 | 0 | if(status < 0) { |
321 | 0 | if (state.status == PARSE_INCOMPLETE) |
322 | 0 | error(_("unexpected end of input")); |
323 | 0 | return; |
324 | 0 | } |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | | |
329 | | static unsigned char DLLbuf[CONSOLE_BUFFER_SIZE+1], *DLLbufp; |
330 | | |
331 | | static void check_session_exit(void) |
332 | 0 | { |
333 | 0 | if (! R_Interactive) { |
334 | | /* This funtion will be called again after a LONGJMP if an |
335 | | error is signaled from one of the functions called. The |
336 | | 'exiting' variable identifies this and results in |
337 | | R_Suicide. */ |
338 | 0 | static bool exiting = FALSE; |
339 | 0 | if (exiting) |
340 | 0 | R_Suicide(_("error during cleanup\n")); |
341 | 0 | else { |
342 | 0 | exiting = TRUE; |
343 | 0 | if (GetOption1(install("error")) != R_NilValue || |
344 | 0 | R_isTRUE(GetOption1(install("catch.script.errors"))) |
345 | 0 | ) { |
346 | 0 | exiting = FALSE; |
347 | 0 | return; |
348 | 0 | } |
349 | 0 | REprintf(_("Execution halted\n")); |
350 | 0 | R_CleanUp(SA_NOSAVE, 1, 0); /* quit, no save, no .Last, status=1 */ |
351 | 0 | } |
352 | 0 | } |
353 | 0 | } |
354 | | |
355 | | void R_ReplDLLinit(void) |
356 | 0 | { |
357 | 0 | if (SETJMP(R_Toplevel.cjmpbuf)) |
358 | 0 | check_session_exit(); |
359 | 0 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
360 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
361 | 0 | prompt_type = 1; |
362 | 0 | DLLbuf[0] = DLLbuf[CONSOLE_BUFFER_SIZE] = '\0'; |
363 | 0 | DLLbufp = DLLbuf; |
364 | 0 | } |
365 | | |
366 | | /* FIXME: this should be re-written to use Rf_ReplIteration |
367 | | since it gets out of sync with it over time */ |
368 | | int R_ReplDLLdo1(void) |
369 | 0 | { |
370 | 0 | int c; |
371 | 0 | ParseStatus status; |
372 | 0 | SEXP rho = R_GlobalEnv, lastExpr; |
373 | |
|
374 | 0 | if(!*DLLbufp) { |
375 | 0 | R_Busy(0); |
376 | 0 | if (R_ReadConsole(R_PromptString(0, prompt_type), DLLbuf, |
377 | 0 | CONSOLE_BUFFER_SIZE, 1) == 0) |
378 | 0 | return -1; |
379 | 0 | DLLbufp = DLLbuf; |
380 | 0 | } |
381 | 0 | while((c = *DLLbufp++)) { |
382 | 0 | R_IoBufferPutc(c, &R_ConsoleIob); |
383 | 0 | if(c == ';' || c == '\n') break; |
384 | 0 | } |
385 | 0 | R_PPStackTop = 0; |
386 | 0 | R_CurrentExpr = R_Parse1Buffer(&R_ConsoleIob, 0, &status); |
387 | |
|
388 | 0 | switch(status) { |
389 | 0 | case PARSE_NULL: |
390 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
391 | 0 | prompt_type = 1; |
392 | 0 | break; |
393 | 0 | case PARSE_OK: |
394 | 0 | { |
395 | 0 | R_IoBufferReadReset(&R_ConsoleIob); |
396 | 0 | R_CurrentExpr = R_Parse1Buffer(&R_ConsoleIob, 1, &status); |
397 | 0 | R_Visible = FALSE; |
398 | 0 | R_EvalDepth = 0; |
399 | 0 | resetTimeLimits(); |
400 | 0 | PROTECT(R_CurrentExpr); |
401 | 0 | R_Busy(1); |
402 | 0 | lastExpr = R_CurrentExpr; |
403 | 0 | R_CurrentExpr = eval(R_CurrentExpr, rho); |
404 | 0 | SET_SYMVALUE(R_LastvalueSymbol, R_CurrentExpr); |
405 | 0 | bool wasDisplayed = R_Visible; |
406 | 0 | if (R_Visible) |
407 | 0 | PrintValueEnv(R_CurrentExpr, rho); |
408 | 0 | if (R_CollectWarnings) |
409 | 0 | PrintWarnings(); |
410 | 0 | Rf_callToplevelHandlers(lastExpr, R_CurrentExpr, TRUE, wasDisplayed); |
411 | 0 | UNPROTECT(1); |
412 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
413 | 0 | R_Busy(0); |
414 | 0 | prompt_type = 1; |
415 | 0 | break; |
416 | 0 | } |
417 | 0 | case PARSE_ERROR: |
418 | 0 | parseError(R_NilValue, 0); |
419 | 0 | R_IoBufferWriteReset(&R_ConsoleIob); |
420 | 0 | prompt_type = 1; |
421 | 0 | break; |
422 | 0 | case PARSE_INCOMPLETE: |
423 | 0 | R_IoBufferReadReset(&R_ConsoleIob); |
424 | 0 | prompt_type = 2; |
425 | 0 | break; |
426 | 0 | case PARSE_EOF: |
427 | 0 | return -1; |
428 | 0 | break; |
429 | 0 | } |
430 | 0 | return prompt_type; |
431 | 0 | } |
432 | | |
433 | | /* Main Loop: It is assumed that at this point that operating system */ |
434 | | /* specific tasks (dialog window creation etc) have been performed. */ |
435 | | /* We can now print a greeting, run the .First function and then enter */ |
436 | | /* the read-eval-print loop. */ |
437 | | |
438 | | static void handleInterrupt(int dummy) |
439 | 0 | { |
440 | 0 | R_interrupts_pending = 1; |
441 | 0 | signal(SIGINT, handleInterrupt); |
442 | 0 | } |
443 | | |
444 | | /* this flag is set if R internal code is using send() and does not |
445 | | want to trigger an error on SIGPIPE (e.g., the httpd code). |
446 | | [It is safer and more portable than other methods of handling |
447 | | broken pipes on send().] |
448 | | */ |
449 | | |
450 | | #ifndef Win32 |
451 | | // controlled by the internal http server in the internet module |
452 | | int R_ignore_SIGPIPE = 0; |
453 | | |
454 | | static void handlePipe(int dummy) |
455 | 0 | { |
456 | 0 | signal(SIGPIPE, handlePipe); |
457 | 0 | if (!R_ignore_SIGPIPE) error("ignoring SIGPIPE signal"); |
458 | 0 | } |
459 | | #endif |
460 | | |
461 | | |
462 | | #ifdef Win32 |
463 | | static int num_caught = 0; |
464 | | |
465 | | static void win32_segv(int signum) |
466 | | { |
467 | | /* NB: stack overflow is not an access violation on Win32 */ |
468 | | { /* A simple customized print of the traceback */ |
469 | | SEXP trace, p, q; |
470 | | int line = 1, i; |
471 | | PROTECT(trace = R_GetTraceback(0)); |
472 | | if(trace != R_NilValue) { |
473 | | REprintf("\nTraceback:\n"); |
474 | | for(p = trace; p != R_NilValue; p = CDR(p), line++) { |
475 | | q = CAR(p); /* a character vector */ |
476 | | REprintf("%2d: ", line); |
477 | | for(i = 0; i < LENGTH(q); i++) |
478 | | REprintf("%s", CHAR(STRING_ELT(q, i))); |
479 | | REprintf("\n"); |
480 | | } |
481 | | UNPROTECT(1); |
482 | | } |
483 | | } |
484 | | num_caught++; |
485 | | if(num_caught < 10) signal(signum, win32_segv); |
486 | | if(signum == SIGILL) |
487 | | error("caught access violation - continue with care"); |
488 | | else |
489 | | error("caught access violation - continue with care"); |
490 | | } |
491 | | #endif |
492 | | |
493 | | #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) && defined(HAVE_WORKING_SIGACTION) && defined(HAVE_SIGEMPTYSET) |
494 | | |
495 | | /* NB: this really isn't safe, but suffices for experimentation for now. |
496 | | In due course just set a flag and do this after the return. OTOH, |
497 | | if we do want to bail out with a core dump, need to do that here. |
498 | | |
499 | | 2005-12-17 BDR */ |
500 | | |
501 | | static unsigned char ConsoleBuf[CONSOLE_BUFFER_SIZE]; |
502 | | |
503 | | static void sigactionSegv(int signum, siginfo_t *ip, void *context) |
504 | 0 | { |
505 | | /* ensure R terminates if the handler segfaults (PR#18551) */ |
506 | 0 | signal(signum, SIG_DFL); |
507 | 0 | char *s; |
508 | | |
509 | | /* First check for stack overflow if we know the stack position. |
510 | | We assume anything within 16Mb beyond the stack end is a stack overflow. |
511 | | */ |
512 | 0 | if(signum == SIGSEGV && (ip != (siginfo_t *)0) && |
513 | 0 | (intptr_t) R_CStackStart != -1) { |
514 | 0 | uintptr_t addr = (uintptr_t) ip->si_addr; |
515 | 0 | intptr_t diff = (R_CStackDir > 0) ? R_CStackStart - addr: |
516 | 0 | addr - R_CStackStart; |
517 | 0 | uintptr_t upper = 0x1000000; /* 16Mb */ |
518 | 0 | if((intptr_t) R_CStackLimit != -1) upper += R_CStackLimit; |
519 | 0 | if(diff > 0 && diff < upper) { |
520 | 0 | REprintf(_("Error: segfault from C stack overflow\n")); |
521 | 0 | #if defined(linux) || defined(__linux__) || defined(__sun) || defined(sun) |
522 | 0 | sigset_t ss; |
523 | 0 | sigaddset(&ss, signum); |
524 | 0 | sigprocmask(SIG_UNBLOCK, &ss, NULL); |
525 | 0 | #endif |
526 | 0 | jump_to_toplevel(); |
527 | 0 | } |
528 | 0 | } |
529 | | |
530 | | /* need to take off stack checking as stack base has changed */ |
531 | 0 | R_CStackLimit = (uintptr_t)-1; |
532 | | |
533 | | /* Do not translate these messages */ |
534 | 0 | REprintf("\n *** caught %s ***\n", |
535 | 0 | signum == SIGILL ? "illegal operation" : |
536 | 0 | signum == SIGBUS ? "bus error" : "segfault"); |
537 | 0 | if(ip != (siginfo_t *)0) { |
538 | 0 | if(signum == SIGILL) { |
539 | |
|
540 | 0 | switch(ip->si_code) { |
541 | 0 | #ifdef ILL_ILLOPC |
542 | 0 | case ILL_ILLOPC: |
543 | 0 | s = "illegal opcode"; |
544 | 0 | break; |
545 | 0 | #endif |
546 | 0 | #ifdef ILL_ILLOPN |
547 | 0 | case ILL_ILLOPN: |
548 | 0 | s = "illegal operand"; |
549 | 0 | break; |
550 | 0 | #endif |
551 | 0 | #ifdef ILL_ILLADR |
552 | 0 | case ILL_ILLADR: |
553 | 0 | s = "illegal addressing mode"; |
554 | 0 | break; |
555 | 0 | #endif |
556 | 0 | #ifdef ILL_ILLTRP |
557 | 0 | case ILL_ILLTRP: |
558 | 0 | s = "illegal trap"; |
559 | 0 | break; |
560 | 0 | #endif |
561 | 0 | #ifdef ILL_COPROC |
562 | 0 | case ILL_COPROC: |
563 | 0 | s = "coprocessor error"; |
564 | 0 | break; |
565 | 0 | #endif |
566 | 0 | default: |
567 | 0 | s = "unknown"; |
568 | 0 | break; |
569 | 0 | } |
570 | 0 | } else if(signum == SIGBUS) |
571 | 0 | switch(ip->si_code) { |
572 | 0 | #ifdef BUS_ADRALN |
573 | 0 | case BUS_ADRALN: |
574 | 0 | s = "invalid alignment"; |
575 | 0 | break; |
576 | 0 | #endif |
577 | 0 | #ifdef BUS_ADRERR /* not on macOS, apparently */ |
578 | 0 | case BUS_ADRERR: |
579 | 0 | s = "non-existent physical address"; |
580 | 0 | break; |
581 | 0 | #endif |
582 | 0 | #ifdef BUS_OBJERR /* not on macOS, apparently */ |
583 | 0 | case BUS_OBJERR: |
584 | 0 | s = "object specific hardware error"; |
585 | 0 | break; |
586 | 0 | #endif |
587 | 0 | default: |
588 | 0 | s = "unknown"; |
589 | 0 | break; |
590 | 0 | } |
591 | 0 | else |
592 | 0 | switch(ip->si_code) { |
593 | 0 | #ifdef SEGV_MAPERR |
594 | 0 | case SEGV_MAPERR: |
595 | 0 | s = "memory not mapped"; |
596 | 0 | break; |
597 | 0 | #endif |
598 | 0 | #ifdef SEGV_ACCERR |
599 | 0 | case SEGV_ACCERR: |
600 | 0 | s = "invalid permissions"; |
601 | 0 | break; |
602 | 0 | #endif |
603 | 0 | default: |
604 | 0 | s = "unknown"; |
605 | 0 | break; |
606 | 0 | } |
607 | 0 | REprintf("address %p, cause '%s'\n", ip->si_addr, s); |
608 | 0 | } |
609 | 0 | { /* A simple customized print of the traceback */ |
610 | 0 | SEXP trace, p, q; |
611 | 0 | int line = 1, i; |
612 | 0 | PROTECT(trace = R_GetTraceback(0)); |
613 | 0 | if(trace != R_NilValue) { |
614 | 0 | REprintf("\nTraceback:\n"); |
615 | 0 | for(p = trace; p != R_NilValue; p = CDR(p), line++) { |
616 | 0 | q = CAR(p); /* a character vector */ |
617 | 0 | REprintf("%2d: ", line); |
618 | 0 | for(i = 0; i < LENGTH(q); i++) |
619 | 0 | REprintf("%s", CHAR(STRING_ELT(q, i))); |
620 | 0 | REprintf("\n"); |
621 | 0 | } |
622 | 0 | UNPROTECT(1); |
623 | 0 | } |
624 | 0 | } |
625 | 0 | if(R_Interactive) { |
626 | 0 | REprintf("\nPossible actions:\n1: %s\n2: %s\n3: %s\n4: %s\n", |
627 | 0 | "abort (with core dump, if enabled)", |
628 | 0 | "normal R exit", |
629 | 0 | "exit R without saving workspace", |
630 | 0 | "exit R saving workspace"); |
631 | 0 | while(1) { |
632 | 0 | if(R_ReadConsole("Selection: ", ConsoleBuf, CONSOLE_BUFFER_SIZE, |
633 | 0 | 0) > 0) { |
634 | 0 | if(ConsoleBuf[0] == '1') break; |
635 | 0 | if(ConsoleBuf[0] == '2') R_CleanUp(SA_DEFAULT, 0, 1); |
636 | 0 | if(ConsoleBuf[0] == '3') R_CleanUp(SA_NOSAVE, 70, 0); |
637 | 0 | if(ConsoleBuf[0] == '4') R_CleanUp(SA_SAVE, 71, 0); |
638 | 0 | } |
639 | 0 | } |
640 | 0 | REprintf("R is aborting now ...\n"); |
641 | 0 | } |
642 | 0 | else // non-interactively : |
643 | 0 | REprintf("An irrecoverable exception occurred. R is aborting now ...\n"); |
644 | 0 | R_CleanTempDir(); |
645 | | /* now do normal behaviour, e.g. core dump */ |
646 | 0 | raise(signum); |
647 | 0 | } |
648 | | |
649 | | #ifndef SIGSTKSZ |
650 | | # define SIGSTKSZ 8192 /* just a guess */ |
651 | | #endif |
652 | | |
653 | | #ifdef HAVE_STACK_T |
654 | | static stack_t sigstk; |
655 | | #else |
656 | | static struct sigaltstack sigstk; |
657 | | #endif |
658 | | static void *signal_stack; |
659 | | |
660 | 0 | #define R_USAGE 100000 /* Just a guess */ |
661 | | static void init_signal_handlers(void) |
662 | 0 | { |
663 | | /* On Windows, C signal handling functions are replaced by psignal. |
664 | | Initialization of a psignal Ctrl handler happens on the first |
665 | | signal-related call (typically here). Signal handlers for |
666 | | Ctrl signals set using C API before psignal initialization |
667 | | will have no effect. */ |
668 | | |
669 | | /* Do not set the (since 2005 experimental) SEGV handler |
670 | | UI if R_NO_SEGV_HANDLER env var is non-empty. |
671 | | This is needed to debug crashes in the handler |
672 | | (which happen as they involve the console interface). */ |
673 | 0 | const char *val = getenv("R_NO_SEGV_HANDLER"); |
674 | 0 | if (!val || !*val) { |
675 | | /* <FIXME> may need to reinstall this if we do recover. */ |
676 | 0 | struct sigaction sa; |
677 | 0 | signal_stack = malloc(SIGSTKSZ + R_USAGE); |
678 | 0 | if (signal_stack != NULL) { |
679 | 0 | sigstk.ss_sp = signal_stack; |
680 | 0 | sigstk.ss_size = SIGSTKSZ + R_USAGE; |
681 | 0 | sigstk.ss_flags = 0; |
682 | 0 | if(sigaltstack(&sigstk, NULL) < 0) |
683 | 0 | warning("failed to set alternate signal stack"); |
684 | 0 | } else |
685 | 0 | warning("failed to allocate alternate signal stack"); |
686 | 0 | sa.sa_sigaction = sigactionSegv; |
687 | 0 | sigemptyset(&sa.sa_mask); |
688 | 0 | sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER; |
689 | 0 | sigaction(SIGSEGV, &sa, NULL); |
690 | 0 | sigaction(SIGILL, &sa, NULL); |
691 | 0 | #ifdef SIGBUS |
692 | 0 | sigaction(SIGBUS, &sa, NULL); |
693 | 0 | #endif |
694 | 0 | } |
695 | |
|
696 | 0 | if (signal(SIGINT, handleInterrupt) == SIG_IGN) |
697 | 0 | signal(SIGINT, SIG_IGN); |
698 | 0 | signal(SIGUSR1, onsigusr1); |
699 | 0 | signal(SIGUSR2, onsigusr2); |
700 | 0 | signal(SIGPIPE, handlePipe); |
701 | 0 | } |
702 | | |
703 | | #else /* not sigaltstack and sigaction and sigemptyset*/ |
704 | | static void init_signal_handlers(void) |
705 | | { |
706 | | if (signal(SIGINT, handleInterrupt) == SIG_IGN) |
707 | | signal(SIGINT, SIG_IGN); |
708 | | signal(SIGUSR1, onsigusr1); |
709 | | signal(SIGUSR2, onsigusr2); |
710 | | #ifndef Win32 |
711 | | signal(SIGPIPE, handlePipe); |
712 | | #else |
713 | | signal(SIGSEGV, win32_segv); |
714 | | signal(SIGILL, win32_segv); |
715 | | #endif |
716 | | } |
717 | | #endif |
718 | | |
719 | | |
720 | | static void R_LoadProfile(FILE *fparg, SEXP env) |
721 | 114 | { |
722 | 114 | FILE * volatile fp = fparg; /* is this needed? */ |
723 | 114 | if (fp != NULL) { |
724 | 38 | if (SETJMP(R_Toplevel.cjmpbuf)) |
725 | 0 | check_session_exit(); |
726 | 38 | else { |
727 | 38 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
728 | 38 | R_ReplFile(fp, env); |
729 | 38 | } |
730 | 38 | fclose(fp); |
731 | 38 | } |
732 | 114 | } |
733 | | |
734 | | |
735 | | int R_SignalHandlers = 1; /* Exposed in R_interface.h */ |
736 | | |
737 | | const char* get_workspace_name(void); /* from startup.c */ |
738 | | |
739 | | attribute_hidden void BindDomain(char *R_Home) |
740 | 38 | { |
741 | 38 | #ifdef ENABLE_NLS |
742 | 38 | char *localedir = NULL; |
743 | 38 | # if defined(LC_MESSAGES) && !defined(Win32) |
744 | 38 | setlocale(LC_MESSAGES,""); |
745 | 38 | # endif |
746 | 38 | textdomain(PACKAGE); |
747 | 38 | char *p = getenv("R_TRANSLATIONS"); |
748 | 38 | if (p) Rasprintf_malloc(&localedir, "%s", p); |
749 | 38 | else Rasprintf_malloc(&localedir, "%s/library/translations", R_Home); |
750 | 38 | if (!localedir) |
751 | 0 | R_Suicide("allocation failure in BindDomain"); |
752 | 38 | bindtextdomain(PACKAGE, localedir); // PACKAGE = DOMAIN = "R" |
753 | 38 | bindtextdomain("R-base", localedir); |
754 | | # ifdef _WIN32 |
755 | | bindtextdomain("RGui", localedir); |
756 | | # endif |
757 | 38 | free(localedir); |
758 | 38 | #endif |
759 | 38 | } |
760 | | |
761 | | /* #define DEBUG_STACK_DETECTION */ |
762 | | /* Not to be enabled in production use: the debugging code is more fragile |
763 | | than the detection itself. */ |
764 | | |
765 | | #ifdef DEBUG_STACK_DETECTION |
766 | | static uintptr_t attribute_no_sanitizer_instrumentation almostFillStack() { |
767 | | volatile uintptr_t dummy; |
768 | | |
769 | | dummy = (uintptr_t) &dummy; |
770 | | if (R_CStackStart - R_CStackDir * R_CStackLimit + R_CStackDir * 1024 < R_CStackDir * dummy) |
771 | | return almostFillStack(); |
772 | | else |
773 | | return dummy; |
774 | | } |
775 | | #endif |
776 | | |
777 | | #ifdef Win32 |
778 | | static void invalid_parameter_handler_abort( |
779 | | const wchar_t* expression, |
780 | | const wchar_t* function, |
781 | | const wchar_t* file, |
782 | | unsigned int line, |
783 | | uintptr_t reserved) |
784 | | { |
785 | | R_OutputCon = 2; |
786 | | R_ErrorCon = 2; |
787 | | REprintf(" ----------- FAILURE REPORT -------------- \n"); |
788 | | REprintf(" --- failure: %s ---\n", "invalid parameter passed to a C runtime function"); |
789 | | REprintf(" --- srcref --- \n"); |
790 | | SrcrefPrompt("", R_getCurrentSrcref()); |
791 | | REprintf("\n"); |
792 | | REprintf(" --- call from context --- \n"); |
793 | | PrintValue(R_GlobalContext->call); |
794 | | REprintf(" --- R stacktrace ---\n"); |
795 | | printwhere(); |
796 | | REprintf(" --- function from context --- \n"); |
797 | | if (R_GlobalContext->callfun != NULL && |
798 | | TYPEOF(R_GlobalContext->callfun) == CLOSXP) |
799 | | PrintValue(R_GlobalContext->callfun); |
800 | | REprintf(" --- function search by body ---\n"); |
801 | | if (R_GlobalContext->callfun != NULL && |
802 | | TYPEOF(R_GlobalContext->callfun) == CLOSXP) |
803 | | findFunctionForBody(R_ClosureExpr(R_GlobalContext->callfun)); |
804 | | REprintf(" ----------- END OF FAILURE REPORT -------------- \n"); |
805 | | R_Suicide("invalid parameter passed to a C runtime function"); |
806 | | } |
807 | | |
808 | | extern void _invoke_watson(const wchar_t*, const wchar_t*, const wchar_t*, |
809 | | unsigned int, uintptr_t); |
810 | | |
811 | | static void invalid_parameter_handler_watson( |
812 | | const wchar_t* expression, |
813 | | const wchar_t* function, |
814 | | const wchar_t* file, |
815 | | unsigned int line, |
816 | | uintptr_t reserved) |
817 | | { |
818 | | _invoke_watson(expression, function, file, line, reserved); |
819 | | } |
820 | | #endif |
821 | | |
822 | | void setup_Rmainloop(void) |
823 | 38 | { |
824 | 38 | volatile int doneit; |
825 | 38 | volatile SEXP baseNSenv; |
826 | 38 | SEXP cmd; |
827 | 38 | char deferred_warnings[12][250]; |
828 | 38 | volatile int ndeferred_warnings = 0; |
829 | | |
830 | | #ifdef Win32 |
831 | | { |
832 | | char *p = getenv("_R_WIN_CHECK_INVALID_PARAMETERS_"); |
833 | | if (p && StringTrue(p)) |
834 | | _set_invalid_parameter_handler(invalid_parameter_handler_abort); |
835 | | else if (p && !strcmp(p, "watson")) |
836 | | _set_invalid_parameter_handler(invalid_parameter_handler_watson); |
837 | | } |
838 | | #endif |
839 | | |
840 | | #ifdef DEBUG_STACK_DETECTION |
841 | | /* testing stack base and size detection */ |
842 | | printf("stack limit %lu, start %lu dir %d \n", |
843 | | (unsigned long) R_CStackLimit, |
844 | | (unsigned long) R_CStackStart, |
845 | | R_CStackDir); |
846 | | uintptr_t firstb = R_CStackStart - R_CStackDir; |
847 | | printf("first accessible byte %lx\n", (unsigned long) firstb); |
848 | | if (R_CStackLimit != (uintptr_t)(-1)) { |
849 | | uintptr_t lastb = R_CStackStart - R_CStackDir * R_CStackLimit; |
850 | | printf("last accessible byte %lx\n", (unsigned long) lastb); |
851 | | } |
852 | | printf("accessing first byte...\n"); |
853 | | volatile char dummy = *(char *)firstb; |
854 | | if (R_CStackLimit != (uintptr_t)(-1)) { |
855 | | /* have to access all bytes in order to map stack, e.g. on Linux |
856 | | just reading does not seem to always do the job, so better |
857 | | first almost fill up the stack using recursive function calls |
858 | | */ |
859 | | printf("almost filling up stack...\n"); |
860 | | printf("filled stack up to %lx\n", almostFillStack()); |
861 | | /* the loop below writes outside the local variables and the frame, |
862 | | which is detected e.g. by ASAN as stack-buffer-overflow */ |
863 | | printf("accessing all bytes...\n"); |
864 | | for(uintptr_t o = 0; o < R_CStackLimit; o++) |
865 | | /* with exact bounds, o==-1 and o==R_CStackLimit will segfault */ |
866 | | /* +dummy to silence -Wunused-but-set-variable */ |
867 | | dummy = *((char *)firstb - R_CStackDir * o) + dummy; |
868 | | } |
869 | | #endif |
870 | | |
871 | | /* In case this is a silly limit: 2^32 -3 has been seen and |
872 | | * casting to intptr_r relies on this being smaller than 2^31 on a |
873 | | * 32-bit platform. */ |
874 | 38 | if(R_CStackLimit > 100000000U) |
875 | 38 | R_CStackLimit = (uintptr_t)-1; |
876 | | /* make sure we have enough head room to handle errors */ |
877 | 38 | if(R_CStackLimit != -1) |
878 | 0 | R_CStackLimit = (uintptr_t)(0.95 * R_CStackLimit); |
879 | | |
880 | 38 | InitConnections(); /* needed to get any output at all */ |
881 | | |
882 | | /* Initialize the interpreter's internal structures. */ |
883 | | |
884 | 38 | #ifdef HAVE_LOCALE_H |
885 | | #ifdef Win32 |
886 | | { |
887 | | char allbuf[1000]; /* Windows' locales can be very long */ |
888 | | char *p, *lcall; |
889 | | |
890 | | p = getenv("LC_ALL"); |
891 | | if(p) { |
892 | | strncpy(allbuf, p, sizeof(allbuf)); |
893 | | allbuf[1000 - 1] = '\0'; |
894 | | lcall = allbuf; |
895 | | } else |
896 | | lcall = NULL; |
897 | | |
898 | | /* We'd like to use warning, but need to defer. |
899 | | Also cannot translate. */ |
900 | | |
901 | | p = lcall ? lcall : getenv("LC_COLLATE"); |
902 | | if(!setlocale(LC_COLLATE, p ? p : "")) |
903 | | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
904 | | "Setting LC_COLLATE=%.200s failed\n", p); |
905 | | |
906 | | p = lcall ? lcall : getenv("LC_CTYPE"); |
907 | | if(!setlocale(LC_CTYPE, p ? p : "")) |
908 | | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
909 | | "Setting LC_CTYPE=%.200s failed\n", p); |
910 | | |
911 | | p = lcall ? lcall : getenv("LC_MONETARY"); |
912 | | if(!setlocale(LC_MONETARY, p ? p : "")) |
913 | | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
914 | | "Setting LC_MONETARY=%.200s failed\n", p); |
915 | | |
916 | | p = lcall ? lcall : getenv("LC_TIME"); |
917 | | if(!setlocale(LC_TIME, p ? p : "")) |
918 | | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
919 | | "Setting LC_TIME=%.200s failed\n", p); |
920 | | |
921 | | /* We set R_ARCH here: Unix does it in the shell front-end */ |
922 | | char Rarch[30]; |
923 | | strcpy(Rarch, "R_ARCH="); |
924 | | # ifdef R_ARCH |
925 | | if (strlen(R_ARCH) > 0) { |
926 | | strcat(Rarch, "/"); |
927 | | strcat(Rarch, R_ARCH); |
928 | | } |
929 | | # endif |
930 | | putenv(Rarch); |
931 | | } |
932 | | #else /* not Win32 */ |
933 | | |
934 | 38 | { /* Avoid annoying warnings if LANG and LC_ALL are unset or empty. |
935 | | This happens e.g. on Mac when primary language clash with region, |
936 | | like English in Denmark or Germany. |
937 | | |
938 | | If LANG or LC_ALL has been set to a non-existing locale, we assume |
939 | | that the user wants to ne informed. */ |
940 | | |
941 | 38 | const char *s; |
942 | 38 | int quiet; |
943 | | |
944 | 38 | quiet = !( ((s = getenv("LANG")) && *s) || ((s = getenv("LC_ALL")) && *s) ); |
945 | | |
946 | 38 | if(!setlocale(LC_CTYPE, "") && !quiet) |
947 | 0 | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
948 | |
|
949 | 0 | "Setting LC_CTYPE failed, using \"C\"\n"); |
950 | 38 | if(!setlocale(LC_COLLATE, "") && !quiet) |
951 | 0 | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
952 | 0 | "Setting LC_COLLATE failed, using \"C\"\n"); |
953 | 38 | if(!setlocale(LC_TIME, "") && !quiet) |
954 | 0 | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
955 | 0 | "Setting LC_TIME failed, using \"C\"\n"); |
956 | 38 | # if defined(ENABLE_NLS) && defined(LC_MESSAGES) |
957 | 38 | if(!setlocale(LC_MESSAGES, "") && !quiet) |
958 | 0 | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
959 | 0 | "Setting LC_MESSAGES failed, using \"C\"\n"); |
960 | 38 | # endif |
961 | | /* NB: we do not set LC_NUMERIC */ |
962 | 38 | # ifdef LC_MONETARY |
963 | 38 | if(!setlocale(LC_MONETARY, "") && !quiet) |
964 | 0 | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
965 | 0 | "Setting LC_MONETARY failed, using \"C\"\n"); |
966 | 38 | # endif |
967 | 38 | # ifdef LC_PAPER |
968 | 38 | if(!setlocale(LC_PAPER, "") && !quiet) |
969 | 0 | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
970 | 0 | "Setting LC_PAPER failed, using \"C\"\n"); |
971 | 38 | # endif |
972 | 38 | # ifdef LC_MEASUREMENT |
973 | 38 | if(!setlocale(LC_MEASUREMENT, "") && !quiet) |
974 | 0 | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
975 | 0 | "Setting LC_MEASUREMENT failed, using \"C\"\n"); |
976 | 38 | # endif |
977 | 38 | } |
978 | 38 | #endif /* not Win32 */ |
979 | 38 | #endif |
980 | | |
981 | | /* make sure srand is called before R_tmpnam, PR#14381 */ |
982 | 38 | srand(TimeToSeed()); |
983 | | |
984 | 38 | InitArithmetic(); |
985 | 38 | InitTempDir(); /* must be before InitEd */ |
986 | 38 | InitMemory(); |
987 | 38 | InitStringHash(); /* must be before InitNames */ |
988 | 38 | InitBaseEnv(); |
989 | 38 | InitNames(); /* must be after InitBaseEnv to use R_EmptyEnv */ |
990 | 38 | InitParser(); /* must be after InitMemory, InitNames */ |
991 | 38 | InitGlobalEnv(); |
992 | 38 | InitDynload(); |
993 | 38 | InitOptions(); |
994 | 38 | InitEd(); |
995 | 38 | InitGraphics(); |
996 | 38 | InitTypeTables(); /* must be before InitS3DefaultTypes */ |
997 | 38 | InitS3DefaultTypes(); |
998 | 38 | PrintDefaults(); |
999 | 38 | R_InitConditions(); |
1000 | | |
1001 | 38 | R_Is_Running = 1; |
1002 | 38 | R_check_locale(); |
1003 | | #ifdef Win32 |
1004 | | if (localeCP && systemCP != localeCP) |
1005 | | /* For now, don't warn for localeCP == 0, but it can cause problems |
1006 | | as well. Keep in step with do_setlocale. */ |
1007 | | snprintf(deferred_warnings[ndeferred_warnings++], 250, |
1008 | | "Using locale code page other than %d%s may cause problems.", |
1009 | | systemCP, systemCP == 65001 ? " (\"UTF-8\")" : ""); |
1010 | | #endif |
1011 | | /* Initialize the global context for error handling. */ |
1012 | | /* This provides a target for any non-local gotos */ |
1013 | | /* which occur during error handling */ |
1014 | | |
1015 | 38 | R_Toplevel.nextcontext = NULL; |
1016 | 38 | R_Toplevel.callflag = CTXT_TOPLEVEL; |
1017 | 38 | R_Toplevel.cstacktop = 0; |
1018 | 38 | R_Toplevel.gcenabled = R_GCEnabled; |
1019 | 38 | R_Toplevel.promargs = R_NilValue; |
1020 | 38 | R_Toplevel.callfun = R_NilValue; |
1021 | 38 | R_Toplevel.call = R_NilValue; |
1022 | 38 | R_Toplevel.cloenv = R_BaseEnv; |
1023 | 38 | R_Toplevel.sysparent = R_BaseEnv; |
1024 | 38 | R_Toplevel.conexit = R_NilValue; |
1025 | 38 | R_Toplevel.vmax = NULL; |
1026 | 38 | R_Toplevel.nodestack = R_BCNodeStackTop; |
1027 | 38 | R_Toplevel.bcprottop = R_BCProtTop; |
1028 | 38 | R_Toplevel.cend = NULL; |
1029 | 38 | R_Toplevel.cenddata = NULL; |
1030 | 38 | R_Toplevel.intsusp = FALSE; |
1031 | 38 | R_Toplevel.handlerstack = R_HandlerStack; |
1032 | 38 | R_Toplevel.restartstack = R_RestartStack; |
1033 | 38 | R_Toplevel.srcref = R_NilValue; |
1034 | 38 | R_Toplevel.prstack = NULL; |
1035 | 38 | R_Toplevel.returnValue = SEXP_TO_STACKVAL(NULL); |
1036 | 38 | R_Toplevel.evaldepth = 0; |
1037 | 38 | R_Toplevel.browserfinish = 0; |
1038 | 38 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1039 | 38 | R_ExitContext = NULL; |
1040 | | |
1041 | 38 | R_Warnings = R_NilValue; |
1042 | | |
1043 | | /* This is the same as R_BaseEnv, but this marks the environment |
1044 | | of functions as the namespace and not the package. */ |
1045 | 38 | baseNSenv = R_BaseNamespace; |
1046 | | |
1047 | | /* Set up some global variables */ |
1048 | 38 | Init_R_Variables(baseNSenv); |
1049 | | |
1050 | | /* On initial entry we open the base language package and begin by |
1051 | | running the repl on it. |
1052 | | If there is an error we pass on to the repl. |
1053 | | Perhaps it makes more sense to quit gracefully? |
1054 | | */ |
1055 | | |
1056 | | #ifdef RMIN_ONLY |
1057 | | /* This is intended to support a minimal build for experimentation. */ |
1058 | | if (R_SignalHandlers) init_signal_handlers(); |
1059 | | #else |
1060 | 38 | FILE *fp = R_OpenLibraryFile("base"); |
1061 | 38 | if (fp == NULL) |
1062 | 0 | R_Suicide(_("unable to open the base package\n")); |
1063 | | |
1064 | 38 | doneit = 0; |
1065 | 38 | if (SETJMP(R_Toplevel.cjmpbuf)) |
1066 | 0 | check_session_exit(); |
1067 | 38 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1068 | 38 | if (R_SignalHandlers) init_signal_handlers(); |
1069 | 38 | if (!doneit) { |
1070 | 38 | doneit = 1; |
1071 | 38 | R_ReplFile(fp, baseNSenv); |
1072 | 38 | } |
1073 | 38 | fclose(fp); |
1074 | 38 | #endif |
1075 | | |
1076 | | /* This is where we source the system-wide, the site's and the |
1077 | | user's profile (in that order). If there is an error, we |
1078 | | drop through to further processing. |
1079 | | */ |
1080 | 38 | R_IoBufferInit(&R_ConsoleIob); |
1081 | 38 | R_LoadProfile(R_OpenSysInitFile(), baseNSenv); |
1082 | | /* These are the same bindings, so only lock them once */ |
1083 | 38 | R_LockEnvironment(R_BaseNamespace, TRUE); |
1084 | 38 | R_LockEnvironment(R_BaseEnv, FALSE); |
1085 | | /* At least temporarily unlock some bindings used in graphics */ |
1086 | 38 | R_unLockBinding(R_DeviceSymbol, R_BaseEnv); |
1087 | 38 | R_unLockBinding(R_DevicesSymbol, R_BaseEnv); |
1088 | | |
1089 | | /* require(methods) if it is in the default packages */ |
1090 | 38 | doneit = 0; |
1091 | 38 | if (SETJMP(R_Toplevel.cjmpbuf)) |
1092 | 0 | check_session_exit(); |
1093 | 38 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1094 | 38 | if (!doneit) { |
1095 | 38 | doneit = 1; |
1096 | 38 | PROTECT(cmd = install(".OptRequireMethods")); |
1097 | 38 | R_CurrentExpr = R_findVar(cmd, R_GlobalEnv); |
1098 | 38 | if (R_CurrentExpr != R_UnboundValue && |
1099 | 38 | TYPEOF(R_CurrentExpr) == CLOSXP) { |
1100 | 38 | PROTECT(R_CurrentExpr = lang1(cmd)); |
1101 | 38 | R_CurrentExpr = eval(R_CurrentExpr, R_BaseEnv); |
1102 | 38 | UNPROTECT(1); |
1103 | 38 | } |
1104 | 38 | UNPROTECT(1); |
1105 | 38 | } |
1106 | | |
1107 | 38 | if (strcmp(R_GUIType, "Tk") == 0) { |
1108 | 0 | char *buf = NULL; |
1109 | |
|
1110 | 0 | Rasprintf_malloc(&buf, "%s/library/tcltk/exec/Tk-frontend.R", R_Home); |
1111 | 0 | if (!buf) |
1112 | 0 | R_Suicide("allocation failure in setup_Rmainloop"); |
1113 | 0 | R_LoadProfile(R_fopen(buf, "r"), R_GlobalEnv); |
1114 | 0 | free(buf); |
1115 | 0 | } |
1116 | | |
1117 | | /* Print a platform and version dependent greeting and a pointer to |
1118 | | * the copyleft. |
1119 | | */ |
1120 | 38 | if(!R_Quiet) PrintGreeting(); |
1121 | | |
1122 | 38 | R_LoadProfile(R_OpenSiteFile(), R_GlobalEnv); |
1123 | | /* The system profile creates an active binding in global environment |
1124 | | to capture writes to .Library.site executed in the site profile. This |
1125 | | effectively modifies .Library.site in the base environment to mimick |
1126 | | previous behavior when the site profile was run in the base |
1127 | | environment. */ |
1128 | 38 | R_removeVarFromFrame(install(".Library.site"), R_GlobalEnv); |
1129 | 38 | R_LoadProfile(R_OpenInitFile(), R_GlobalEnv); |
1130 | | |
1131 | | /* This is where we try to load a user's saved data. |
1132 | | The right thing to do here is very platform dependent. |
1133 | | E.g. under Unix we look in a special hidden file and on the Mac |
1134 | | we look in any documents which might have been double clicked on |
1135 | | or dropped on the application. |
1136 | | */ |
1137 | 38 | doneit = 0; |
1138 | 38 | if (SETJMP(R_Toplevel.cjmpbuf)) |
1139 | 0 | check_session_exit(); |
1140 | 38 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1141 | 38 | if (!doneit) { |
1142 | 38 | doneit = 1; |
1143 | 38 | R_InitialData(); |
1144 | 38 | } |
1145 | 0 | else { |
1146 | 0 | if (SETJMP(R_Toplevel.cjmpbuf)) |
1147 | 0 | check_session_exit(); |
1148 | 0 | else { |
1149 | 0 | warning(_("unable to restore saved data in %s\n"), get_workspace_name()); |
1150 | 0 | } |
1151 | 0 | } |
1152 | | |
1153 | | /* Initial Loading is done. |
1154 | | At this point we try to invoke the .First Function. |
1155 | | If there is an error we continue. */ |
1156 | | |
1157 | 38 | doneit = 0; |
1158 | 38 | if (SETJMP(R_Toplevel.cjmpbuf)) |
1159 | 0 | check_session_exit(); |
1160 | 38 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1161 | 38 | if (!doneit) { |
1162 | 38 | doneit = 1; |
1163 | 38 | PROTECT(cmd = install(".First")); |
1164 | 38 | R_CurrentExpr = R_findVar(cmd, R_GlobalEnv); |
1165 | 38 | if (R_CurrentExpr != R_UnboundValue && |
1166 | 0 | TYPEOF(R_CurrentExpr) == CLOSXP) { |
1167 | 0 | PROTECT(R_CurrentExpr = lang1(cmd)); |
1168 | 0 | R_CurrentExpr = eval(R_CurrentExpr, R_GlobalEnv); |
1169 | 0 | UNPROTECT(1); |
1170 | 0 | } |
1171 | 38 | UNPROTECT(1); |
1172 | 38 | } |
1173 | | /* Try to invoke the .First.sys function, which loads the default packages. |
1174 | | If there is an error we continue. */ |
1175 | | |
1176 | 38 | doneit = 0; |
1177 | 38 | if (SETJMP(R_Toplevel.cjmpbuf)) |
1178 | 0 | check_session_exit(); |
1179 | 38 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1180 | 38 | if (!doneit) { |
1181 | 38 | doneit = 1; |
1182 | 38 | PROTECT(cmd = install(".First.sys")); |
1183 | 38 | R_CurrentExpr = R_findVar(cmd, baseNSenv); |
1184 | 38 | if (R_CurrentExpr != R_UnboundValue && |
1185 | 38 | TYPEOF(R_CurrentExpr) == CLOSXP) { |
1186 | 38 | PROTECT(R_CurrentExpr = lang1(cmd)); |
1187 | 38 | R_CurrentExpr = eval(R_CurrentExpr, R_BaseEnv); |
1188 | 38 | UNPROTECT(1); |
1189 | 38 | } |
1190 | 38 | UNPROTECT(1); |
1191 | 38 | } |
1192 | 38 | { |
1193 | 38 | int i; |
1194 | 38 | for(i = 0 ; i < ndeferred_warnings; i++) |
1195 | 0 | warning("%s", deferred_warnings[i]); |
1196 | 38 | } |
1197 | 38 | if (R_CollectWarnings) { |
1198 | 0 | REprintf(_("During startup - ")); |
1199 | 0 | PrintWarnings(); |
1200 | 0 | } |
1201 | 38 | if(R_Verbose) |
1202 | 0 | REprintf(" ending setup_Rmainloop(): R_Interactive = %d {main.c}\n", |
1203 | 0 | R_Interactive); |
1204 | | |
1205 | | /* trying to do this earlier seems to run into bootstrapping issues. */ |
1206 | 38 | #ifndef RMIN_ONLY |
1207 | 38 | doneit = 0; |
1208 | 38 | if (SETJMP(R_Toplevel.cjmpbuf)) |
1209 | 0 | check_session_exit(); |
1210 | 38 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1211 | 38 | if (!doneit) { |
1212 | 38 | doneit = 1; |
1213 | 38 | R_init_jit_enabled(); |
1214 | 38 | } else |
1215 | 0 | R_Suicide(_("unable to initialize the JIT\n")); |
1216 | 38 | #endif |
1217 | 38 | R_Is_Running = 2; |
1218 | 38 | } |
1219 | | |
1220 | | extern SA_TYPE SaveAction; /* from src/main/startup.c */ |
1221 | | |
1222 | | static void end_Rmainloop(void) |
1223 | 0 | { |
1224 | | /* refrain from printing trailing '\n' in no-echo mode */ |
1225 | 0 | if (!R_NoEcho) |
1226 | 0 | Rprintf("\n"); |
1227 | | /* run the .Last function. If it gives an error, will drop back to main |
1228 | | loop. */ |
1229 | 0 | R_CleanUp(SA_DEFAULT, 0, 1); |
1230 | 0 | } |
1231 | | |
1232 | | void run_Rmainloop(void) |
1233 | 0 | { |
1234 | | /* Here is the real R read-eval-loop. */ |
1235 | | /* We handle the console until end-of-file. */ |
1236 | 0 | if (SETJMP(R_Toplevel.cjmpbuf)) |
1237 | 0 | check_session_exit(); |
1238 | 0 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1239 | 0 | R_ReplConsole(R_GlobalEnv, 0, 0); |
1240 | 0 | end_Rmainloop(); /* must go here */ |
1241 | 0 | } |
1242 | | |
1243 | | void mainloop(void) |
1244 | 0 | { |
1245 | 0 | setup_Rmainloop(); |
1246 | 0 | run_Rmainloop(); |
1247 | 0 | } |
1248 | | |
1249 | | /*this functionality now appears in 3 |
1250 | | places-jump_to_toplevel/profile/here */ |
1251 | | |
1252 | | attribute_hidden void printwhere(void) |
1253 | 0 | { |
1254 | 0 | RCNTXT *cptr; |
1255 | 0 | int lct = 1; |
1256 | |
|
1257 | 0 | for (cptr = R_GlobalContext; cptr; cptr = cptr->nextcontext) { |
1258 | 0 | if ((cptr->callflag & (CTXT_FUNCTION | CTXT_BUILTIN)) && |
1259 | 0 | (TYPEOF(cptr->call) == LANGSXP)) { |
1260 | 0 | Rprintf("where %d", lct++); |
1261 | 0 | SEXP sref; |
1262 | 0 | if (cptr->srcref == R_InBCInterpreter) |
1263 | 0 | sref = R_findBCInterpreterSrcref(cptr); |
1264 | 0 | else |
1265 | 0 | sref = cptr->srcref; |
1266 | 0 | SrcrefPrompt("", sref); |
1267 | 0 | PrintValue(cptr->call); |
1268 | 0 | } |
1269 | 0 | } |
1270 | 0 | Rprintf("\n"); |
1271 | 0 | } |
1272 | | |
1273 | | static void printBrowserHelp(void) |
1274 | 0 | { |
1275 | 0 | Rprintf("n next\n"); |
1276 | 0 | Rprintf("s step into\n"); |
1277 | 0 | Rprintf("f finish\n"); |
1278 | 0 | Rprintf("c or cont continue\n"); |
1279 | 0 | Rprintf("Q quit\n"); |
1280 | 0 | Rprintf("where show stack\n"); |
1281 | 0 | Rprintf("help show help\n"); |
1282 | 0 | Rprintf("<expr> evaluate expression\n"); |
1283 | 0 | } |
1284 | | |
1285 | | static int ParseBrowser(SEXP CExpr, SEXP rho) |
1286 | 0 | { |
1287 | 0 | int rval = 0; |
1288 | 0 | if (isSymbol(CExpr)) { |
1289 | 0 | const char *expr = CHAR(PRINTNAME(CExpr)); |
1290 | 0 | if (!strcmp(expr, "c") || !strcmp(expr, "cont")) { |
1291 | 0 | rval = 1; |
1292 | 0 | SET_RDEBUG(rho, 0); |
1293 | 0 | } else if (!strcmp(expr, "f")) { |
1294 | 0 | rval = 1; |
1295 | 0 | RCNTXT *cntxt = R_GlobalContext; |
1296 | 0 | while (cntxt != R_ToplevelContext |
1297 | 0 | && !(cntxt->callflag & (CTXT_RETURN | CTXT_LOOP))) { |
1298 | 0 | cntxt = cntxt->nextcontext; |
1299 | 0 | } |
1300 | 0 | cntxt->browserfinish = 1; |
1301 | 0 | SET_RDEBUG(rho, 1); |
1302 | 0 | R_BrowserLastCommand = 'f'; |
1303 | 0 | } else if (!strcmp(expr, "help")) { |
1304 | 0 | rval = 2; |
1305 | 0 | printBrowserHelp(); |
1306 | 0 | } else if (!strcmp(expr, "n")) { |
1307 | 0 | rval = 1; |
1308 | 0 | SET_RDEBUG(rho, 1); |
1309 | 0 | R_BrowserLastCommand = 'n'; |
1310 | 0 | } else if (!strcmp(expr, "Q")) { |
1311 | | |
1312 | | /* this is really dynamic state that should be managed as such */ |
1313 | 0 | SET_RDEBUG(rho, 0); /*PR#1721*/ |
1314 | |
|
1315 | 0 | jump_to_toplevel(); |
1316 | 0 | } else if (!strcmp(expr, "s")) { |
1317 | 0 | rval = 1; |
1318 | 0 | SET_RDEBUG(rho, 1); |
1319 | 0 | R_BrowserLastCommand = 's'; |
1320 | 0 | } else if (!strcmp(expr, "where")) { |
1321 | 0 | rval = 2; |
1322 | 0 | printwhere(); |
1323 | | /* SET_RDEBUG(rho, 1); */ |
1324 | 0 | } else if (!strcmp(expr, "r")) { |
1325 | 0 | SEXP hooksym = install(".tryResumeInterrupt"); |
1326 | 0 | if (SYMVALUE(hooksym) != R_UnboundValue) { |
1327 | 0 | SEXP hcall; |
1328 | 0 | R_Busy(1); |
1329 | 0 | PROTECT(hcall = LCONS(hooksym, R_NilValue)); |
1330 | 0 | eval(hcall, R_BaseEnv); |
1331 | 0 | UNPROTECT(1); |
1332 | 0 | } |
1333 | 0 | } |
1334 | 0 | } |
1335 | |
|
1336 | 0 | return rval; |
1337 | 0 | } |
1338 | | |
1339 | | /* There's another copy of this in eval.c */ |
1340 | | static void PrintCall(SEXP call, SEXP rho) |
1341 | 0 | { |
1342 | 0 | int old_bl = R_BrowseLines, |
1343 | 0 | blines = asInteger(GetOption1(install("deparse.max.lines"))); |
1344 | 0 | if(blines != NA_INTEGER && blines > 0) |
1345 | 0 | R_BrowseLines = blines; |
1346 | |
|
1347 | 0 | R_PrintData pars; |
1348 | 0 | PrintInit(&pars, rho); |
1349 | 0 | PrintValueRec(call, &pars); |
1350 | |
|
1351 | 0 | R_BrowseLines = old_bl; |
1352 | 0 | } |
1353 | | |
1354 | | static int countBrowserContexts(void) |
1355 | 0 | { |
1356 | | /* passing TRUE for the second argument seems to over-count */ |
1357 | 0 | return countContexts(CTXT_BROWSER, FALSE); |
1358 | 0 | } |
1359 | | |
1360 | | #ifdef USE_BROWSER_HOOK |
1361 | | struct callBrowserHookData { SEXP hook, cond, rho; }; |
1362 | | |
1363 | | static SEXP callBrowserHook(void *data) |
1364 | | { |
1365 | | struct callBrowserHookData *bhdata = data; |
1366 | | SEXP hook = bhdata-> hook; |
1367 | | SEXP cond = bhdata->cond; |
1368 | | SEXP rho = bhdata->rho; |
1369 | | SEXP args = CONS(hook, CONS(cond, CONS(rho, R_NilValue))); |
1370 | | SEXP hcall = LCONS(hook, args); |
1371 | | PROTECT(hcall); |
1372 | | R_SetOption(install("browser.hook"), R_NilValue); |
1373 | | SEXP val = eval(hcall, R_GlobalEnv); |
1374 | | UNPROTECT(1); /* hcall */ |
1375 | | return val; |
1376 | | } |
1377 | | |
1378 | | static void restoreBrowserHookOption(void *data, bool jump) |
1379 | | { |
1380 | | struct callBrowserHookData *bhdata = data; |
1381 | | SEXP hook = bhdata-> hook; |
1382 | | R_SetOption(install("browser.hook"), hook); // also on jumps |
1383 | | } |
1384 | | |
1385 | | static void R_browserRepl(SEXP rho) |
1386 | | { |
1387 | | /* save some stuff -- shouldn't be needed unless REPL is sloppy */ |
1388 | | int savestack = R_PPStackTop; |
1389 | | SEXP topExp = PROTECT(R_CurrentExpr); |
1390 | | RCNTXT *saveToplevelContext = R_ToplevelContext; |
1391 | | RCNTXT *saveGlobalContext = R_GlobalContext; |
1392 | | |
1393 | | int browselevel = countBrowserContexts(); |
1394 | | R_ReplConsole(rho, savestack, browselevel); |
1395 | | |
1396 | | /* restore the saved stuff */ |
1397 | | R_CurrentExpr = topExp; |
1398 | | UNPROTECT(1); /* topExp */ |
1399 | | R_PPStackTop = savestack; |
1400 | | R_CurrentExpr = topExp; |
1401 | | R_ToplevelContext = saveToplevelContext; |
1402 | | R_GlobalContext = saveGlobalContext; |
1403 | | } |
1404 | | #endif |
1405 | | |
1406 | | /* browser(text = "", condition = NULL, expr = TRUE, skipCalls = 0L) |
1407 | | * ------- but also called from ./eval.c */ |
1408 | | attribute_hidden SEXP do_browser(SEXP call, SEXP op, SEXP args, SEXP rho) |
1409 | 0 | { |
1410 | 0 | RCNTXT *saveToplevelContext; |
1411 | 0 | RCNTXT *saveGlobalContext; |
1412 | 0 | RCNTXT thiscontext, returncontext, *cptr; |
1413 | 0 | int savestack, browselevel; |
1414 | 0 | SEXP ap, topExp, argList; |
1415 | | |
1416 | | /* Cannot call checkArity(op, args), because "op" may be a closure */ |
1417 | | /* or a primitive other than "browser". */ |
1418 | | |
1419 | | /* argument matching */ |
1420 | 0 | PROTECT(ap = list4(R_NilValue, R_NilValue, R_NilValue, R_NilValue)); |
1421 | 0 | SET_TAG(ap, install("text")); |
1422 | 0 | SET_TAG(CDR(ap), install("condition")); |
1423 | 0 | SET_TAG(CDDR(ap), install("expr")); |
1424 | 0 | SET_TAG(CDDDR(ap), install("skipCalls")); |
1425 | | #ifdef USE_BROWSER_HOOK |
1426 | | SETCDR(CDDDR(ap), CONS(R_NilValue, R_NilValue)); |
1427 | | SET_TAG(CDR(CDDDR(ap)), install("ignoreHook")); |
1428 | | #endif |
1429 | 0 | argList = matchArgs_RC(ap, args, call); |
1430 | 0 | UNPROTECT(1); |
1431 | 0 | PROTECT(argList); |
1432 | | /* substitute defaults */ |
1433 | 0 | if(CAR(argList) == R_MissingArg) |
1434 | 0 | SETCAR(argList, mkString("")); |
1435 | 0 | if(CADR(argList) == R_MissingArg) |
1436 | 0 | SETCAR(CDR(argList), R_NilValue); |
1437 | 0 | if(CADDR(argList) == R_MissingArg) |
1438 | 0 | SETCAR(CDDR(argList), ScalarLogical(1)); |
1439 | 0 | if(CADDDR(argList) == R_MissingArg) |
1440 | 0 | SETCAR(CDDDR(argList), ScalarInteger(0)); |
1441 | | #ifdef USE_BROWSER_HOOK |
1442 | | if(CAR(CDR(CDDDR(argList))) == R_MissingArg) |
1443 | | SETCAR(CDR(CDDDR(argList)), ScalarLogical(FALSE)); |
1444 | | #endif |
1445 | | |
1446 | | /* return if 'expr' is not TRUE */ |
1447 | 0 | SEXP expr = CADDR(argList); |
1448 | 0 | if (! asLogical(expr)) { |
1449 | 0 | UNPROTECT(1); |
1450 | 0 | return R_NilValue; |
1451 | 0 | } |
1452 | | |
1453 | | #ifdef USE_BROWSER_HOOK |
1454 | | /* allow environment to use to be provides as the 'expr' argument */ |
1455 | | if (TYPEOF(expr) == ENVSXP) |
1456 | | rho = expr; |
1457 | | |
1458 | | bool ignoreHook = asBool2(CAR(CDR(CDDDR(argList))), call); |
1459 | | if (ignoreHook) { |
1460 | | R_browserRepl(rho); |
1461 | | UNPROTECT(1); /* argList */ |
1462 | | return R_ReturnedValue; |
1463 | | } |
1464 | | #endif |
1465 | | |
1466 | | /* trap non-interactive debugger invocation */ |
1467 | 0 | if(! R_Interactive) { |
1468 | 0 | char *p = getenv("_R_CHECK_BROWSER_NONINTERACTIVE_"); |
1469 | 0 | if (p != NULL && StringTrue(p)) |
1470 | 0 | error(_("non-interactive browser() -- left over from debugging?")); |
1471 | 0 | } |
1472 | | |
1473 | | /* Save the evaluator state information */ |
1474 | | /* so that it can be restored on exit. */ |
1475 | |
|
1476 | 0 | browselevel = countBrowserContexts(); |
1477 | 0 | savestack = R_PPStackTop; |
1478 | 0 | PROTECT(topExp = R_CurrentExpr); |
1479 | 0 | saveToplevelContext = R_ToplevelContext; |
1480 | 0 | saveGlobalContext = R_GlobalContext; |
1481 | |
|
1482 | 0 | if (!RDEBUG(rho)) { |
1483 | 0 | int skipCalls = asInteger(CADDDR(argList)); |
1484 | 0 | cptr = R_GlobalContext; |
1485 | | #ifdef USE_BROWSER_HOOK |
1486 | | if (! ignoreHook) |
1487 | | /* skip over the hook closure on the stack */ |
1488 | | while ((!(cptr->callflag & CTXT_FUNCTION) || cptr->cloenv != rho) |
1489 | | && cptr->callflag ) |
1490 | | cptr = cptr->nextcontext; |
1491 | | #endif |
1492 | 0 | while ( ( !(cptr->callflag & CTXT_FUNCTION) || skipCalls--) |
1493 | 0 | && cptr->callflag ) |
1494 | 0 | cptr = cptr->nextcontext; |
1495 | 0 | Rprintf("Called from: "); |
1496 | 0 | if( cptr != R_ToplevelContext ) { |
1497 | 0 | PrintCall(cptr->call, rho); |
1498 | 0 | SET_RDEBUG(cptr->cloenv, 1); |
1499 | 0 | } else |
1500 | 0 | Rprintf("top level \n"); |
1501 | |
|
1502 | 0 | R_BrowseLines = 0; |
1503 | 0 | } |
1504 | |
|
1505 | 0 | R_ReturnedValue = R_NilValue; |
1506 | | |
1507 | | /* Here we establish two contexts. The first */ |
1508 | | /* of these provides a target for return */ |
1509 | | /* statements which a user might type at the */ |
1510 | | /* browser prompt. The (optional) second one */ |
1511 | | /* acts as a target for error returns. */ |
1512 | |
|
1513 | 0 | begincontext(&returncontext, CTXT_BROWSER, call, rho, |
1514 | 0 | R_BaseEnv, argList, R_NilValue); |
1515 | 0 | if (!SETJMP(returncontext.cjmpbuf)) { |
1516 | 0 | begincontext(&thiscontext, CTXT_RESTART, R_NilValue, rho, |
1517 | 0 | R_BaseEnv, R_NilValue, R_NilValue); |
1518 | 0 | if (SETJMP(thiscontext.cjmpbuf)) { |
1519 | 0 | SET_RESTART_BIT_ON(thiscontext.callflag); |
1520 | 0 | R_ReturnedValue = R_NilValue; |
1521 | 0 | R_Visible = FALSE; |
1522 | 0 | } |
1523 | 0 | R_GlobalContext = &thiscontext; |
1524 | 0 | R_InsertRestartHandlers(&thiscontext, "browser"); |
1525 | | #ifdef USE_BROWSER_HOOK |
1526 | | /* if a browser hook is provided, call it and use the result */ |
1527 | | SEXP hook = ignoreHook ? |
1528 | | R_NilValue : GetOption1(install("browser.hook")); |
1529 | | if (isFunction(hook)) { |
1530 | | struct callBrowserHookData bhdata = { |
1531 | | .hook = hook, .cond = CADR(argList), .rho = rho |
1532 | | }; |
1533 | | R_ReturnedValue = R_UnwindProtect(callBrowserHook, &bhdata, |
1534 | | restoreBrowserHookOption, &bhdata, |
1535 | | NULL); |
1536 | | } |
1537 | | else |
1538 | | R_ReplConsole(rho, savestack, browselevel + 1); |
1539 | | #else |
1540 | 0 | R_ReplConsole(rho, savestack, browselevel + 1); |
1541 | 0 | #endif |
1542 | 0 | endcontext(&thiscontext); |
1543 | 0 | } |
1544 | 0 | endcontext(&returncontext); |
1545 | | |
1546 | | /* Reset the interpreter state. */ |
1547 | |
|
1548 | 0 | R_CurrentExpr = topExp; |
1549 | 0 | UNPROTECT(1); |
1550 | 0 | R_PPStackTop = savestack; |
1551 | 0 | UNPROTECT(1); |
1552 | 0 | R_CurrentExpr = topExp; |
1553 | 0 | R_ToplevelContext = saveToplevelContext; |
1554 | 0 | R_GlobalContext = saveGlobalContext; |
1555 | 0 | return R_ReturnedValue; |
1556 | 0 | } |
1557 | | |
1558 | | void R_dot_Last(void) |
1559 | 0 | { |
1560 | 0 | SEXP cmd; |
1561 | | |
1562 | | /* Run the .Last function. */ |
1563 | | /* Errors here should kick us back into the repl. */ |
1564 | |
|
1565 | 0 | R_GlobalContext = R_ToplevelContext = R_SessionContext = &R_Toplevel; |
1566 | 0 | PROTECT(cmd = install(".Last")); |
1567 | 0 | R_CurrentExpr = R_findVar(cmd, R_GlobalEnv); |
1568 | 0 | if (R_CurrentExpr != R_UnboundValue && TYPEOF(R_CurrentExpr) == CLOSXP) { |
1569 | 0 | PROTECT(R_CurrentExpr = lang1(cmd)); |
1570 | 0 | R_CurrentExpr = eval(R_CurrentExpr, R_GlobalEnv); |
1571 | 0 | UNPROTECT(1); |
1572 | 0 | } |
1573 | 0 | UNPROTECT(1); |
1574 | 0 | PROTECT(cmd = install(".Last.sys")); |
1575 | 0 | R_CurrentExpr = R_findVar(cmd, R_BaseNamespace); |
1576 | 0 | if (R_CurrentExpr != R_UnboundValue && TYPEOF(R_CurrentExpr) == CLOSXP) { |
1577 | 0 | PROTECT(R_CurrentExpr = lang1(cmd)); |
1578 | 0 | R_CurrentExpr = eval(R_CurrentExpr, R_BaseEnv); |
1579 | 0 | UNPROTECT(1); |
1580 | 0 | } |
1581 | 0 | UNPROTECT(1); |
1582 | 0 | } |
1583 | | |
1584 | | attribute_hidden SEXP do_quit(SEXP call, SEXP op, SEXP args, SEXP rho) |
1585 | 0 | { |
1586 | 0 | const char *tmp; |
1587 | 0 | SA_TYPE ask=SA_DEFAULT; |
1588 | 0 | int status, runLast; |
1589 | |
|
1590 | 0 | checkArity(op, args); |
1591 | | /* if there are any browser contexts active don't quit */ |
1592 | 0 | if(countContexts(CTXT_BROWSER, 1)) { |
1593 | 0 | warning(_("cannot quit from browser")); |
1594 | 0 | return R_NilValue; |
1595 | 0 | } |
1596 | 0 | if( !isString(CAR(args)) ) |
1597 | 0 | error(_("one of \"yes\", \"no\", \"ask\" or \"default\" expected.")); |
1598 | 0 | tmp = CHAR(STRING_ELT(CAR(args), 0)); /* ASCII */ |
1599 | 0 | if( !strcmp(tmp, "ask") ) { |
1600 | 0 | ask = SA_SAVEASK; |
1601 | 0 | if(!R_Interactive) |
1602 | 0 | warning(_("save=\"ask\" in non-interactive use: command-line default will be used")); |
1603 | 0 | } else if( !strcmp(tmp, "no") ) |
1604 | 0 | ask = SA_NOSAVE; |
1605 | 0 | else if( !strcmp(tmp, "yes") ) |
1606 | 0 | ask = SA_SAVE; |
1607 | 0 | else if( !strcmp(tmp, "default") ) |
1608 | 0 | ask = SA_DEFAULT; |
1609 | 0 | else |
1610 | 0 | error(_("unrecognized value of 'save'")); |
1611 | 0 | status = asInteger(CADR(args)); |
1612 | 0 | if (status == NA_INTEGER) { |
1613 | 0 | warning(_("invalid 'status', 0 assumed")); |
1614 | 0 | status = 0; |
1615 | 0 | } |
1616 | 0 | runLast = asLogical(CADDR(args)); |
1617 | 0 | if (runLast == NA_LOGICAL) { |
1618 | 0 | warning(_("invalid 'runLast', FALSE assumed")); |
1619 | 0 | runLast = 0; |
1620 | 0 | } |
1621 | | /* run the .Last function. If it gives an error, will drop back to main |
1622 | | loop. */ |
1623 | 0 | R_CleanUp(ask, status, runLast); |
1624 | 0 | exit(0); |
1625 | | /*NOTREACHED*/ |
1626 | 0 | } |
1627 | | |
1628 | | |
1629 | | #include <R_ext/Callbacks.h> |
1630 | | |
1631 | | static R_ToplevelCallbackEl *Rf_ToplevelTaskHandlers = NULL; |
1632 | | |
1633 | | /* The handler currently running or NULL. */ |
1634 | | static R_ToplevelCallbackEl *Rf_CurrentToplevelHandler = NULL; |
1635 | | |
1636 | | /* A running handler attempted to remove itself from Rf_ToplevelTaskHandlers, |
1637 | | do it after it finishes. */ |
1638 | | static Rboolean Rf_DoRemoveCurrentToplevelHandler = FALSE; |
1639 | | |
1640 | | /* A handler has been removed from the Rf_ToplevelTaskHandlers. */ |
1641 | | static Rboolean Rf_RemovedToplevelHandlers = FALSE; |
1642 | | |
1643 | | /* Flag to ensure that the top-level handlers aren't called recursively. |
1644 | | Simple state to indicate that they are currently being run. */ |
1645 | | static Rboolean Rf_RunningToplevelHandlers = FALSE; |
1646 | | |
1647 | | /** |
1648 | | This is the C-level entry point for registering a handler |
1649 | | that is to be called when each top-level task completes. |
1650 | | |
1651 | | Perhaps we need names to make removing them handlers easier |
1652 | | since they could be more identified by an invariant (rather than |
1653 | | position). |
1654 | | */ |
1655 | | //attribute_hidden |
1656 | | R_ToplevelCallbackEl * |
1657 | | Rf_addTaskCallback(R_ToplevelCallback cb, void *data, |
1658 | | void (*finalizer)(void *), const char *name, int *pos) |
1659 | 0 | { |
1660 | 0 | int which; |
1661 | 0 | R_ToplevelCallbackEl *el; |
1662 | 0 | el = (R_ToplevelCallbackEl *) malloc(sizeof(R_ToplevelCallbackEl)); |
1663 | 0 | if(!el) |
1664 | 0 | error(_("cannot allocate space for toplevel callback element")); |
1665 | |
|
1666 | 0 | el->data = data; |
1667 | 0 | el->cb = cb; |
1668 | 0 | el->next = NULL; |
1669 | 0 | el->finalizer = finalizer; |
1670 | |
|
1671 | 0 | if(Rf_ToplevelTaskHandlers == NULL) { |
1672 | 0 | Rf_ToplevelTaskHandlers = el; |
1673 | 0 | which = 0; |
1674 | 0 | } else { |
1675 | 0 | R_ToplevelCallbackEl *tmp; |
1676 | 0 | tmp = Rf_ToplevelTaskHandlers; |
1677 | 0 | which = 1; |
1678 | 0 | while(tmp->next) { |
1679 | 0 | which++; |
1680 | 0 | tmp = tmp->next; |
1681 | 0 | } |
1682 | 0 | tmp->next = el; |
1683 | 0 | } |
1684 | |
|
1685 | 0 | if(!name) { |
1686 | 0 | char buf[20]; |
1687 | 0 | snprintf(buf, 20, "%d", which+1); |
1688 | 0 | el->name = Rstrdup(buf); |
1689 | 0 | } else |
1690 | 0 | el->name = Rstrdup(name); |
1691 | |
|
1692 | 0 | if(pos) |
1693 | 0 | *pos = which; |
1694 | |
|
1695 | 0 | return(el); |
1696 | 0 | } |
1697 | | |
1698 | | static void removeToplevelHandler(R_ToplevelCallbackEl *e) |
1699 | 0 | { |
1700 | 0 | if (Rf_CurrentToplevelHandler == e) |
1701 | 0 | Rf_DoRemoveCurrentToplevelHandler = TRUE; /* postpone */ |
1702 | 0 | else { |
1703 | 0 | Rf_RemovedToplevelHandlers = TRUE; |
1704 | 0 | if(e->finalizer) |
1705 | 0 | e->finalizer(e->data); |
1706 | 0 | free(e->name); |
1707 | 0 | free(e); |
1708 | 0 | } |
1709 | 0 | } |
1710 | | |
1711 | | //attribute_hidden |
1712 | | Rboolean |
1713 | | Rf_removeTaskCallbackByName(const char *name) |
1714 | 0 | { |
1715 | 0 | R_ToplevelCallbackEl *el = Rf_ToplevelTaskHandlers, *prev = NULL; |
1716 | 0 | Rboolean status = TRUE; |
1717 | |
|
1718 | 0 | if(!Rf_ToplevelTaskHandlers) { |
1719 | 0 | return(FALSE); /* error("there are no task callbacks registered"); */ |
1720 | 0 | } |
1721 | | |
1722 | 0 | while(el) { |
1723 | 0 | if(strcmp(el->name, name) == 0) { |
1724 | 0 | if(prev == NULL) { |
1725 | 0 | Rf_ToplevelTaskHandlers = el->next; |
1726 | 0 | } else { |
1727 | 0 | prev->next = el->next; |
1728 | 0 | } |
1729 | 0 | break; |
1730 | 0 | } |
1731 | 0 | prev = el; |
1732 | 0 | el = el->next; |
1733 | 0 | } |
1734 | 0 | if(el) |
1735 | 0 | removeToplevelHandler(el); |
1736 | 0 | else |
1737 | 0 | status = FALSE; |
1738 | |
|
1739 | 0 | return(status); |
1740 | 0 | } |
1741 | | |
1742 | | /** |
1743 | | Remove the top-level task handler/callback identified by |
1744 | | its position in the list of callbacks. |
1745 | | */ |
1746 | | //attribute_hidden |
1747 | | Rboolean |
1748 | | Rf_removeTaskCallbackByIndex(int id) |
1749 | 0 | { |
1750 | 0 | R_ToplevelCallbackEl *el = Rf_ToplevelTaskHandlers, *tmp = NULL; |
1751 | 0 | Rboolean status = TRUE; |
1752 | |
|
1753 | 0 | if(id < 0) |
1754 | 0 | error(_("negative index passed to R_removeTaskCallbackByIndex")); |
1755 | |
|
1756 | 0 | if(Rf_ToplevelTaskHandlers) { |
1757 | 0 | if(id == 0) { |
1758 | 0 | tmp = Rf_ToplevelTaskHandlers; |
1759 | 0 | Rf_ToplevelTaskHandlers = Rf_ToplevelTaskHandlers->next; |
1760 | 0 | } else { |
1761 | 0 | int i = 0; |
1762 | 0 | while(el && i < (id-1)) { |
1763 | 0 | el = el->next; |
1764 | 0 | i++; |
1765 | 0 | } |
1766 | |
|
1767 | 0 | if(i == (id -1) && el) { |
1768 | 0 | tmp = el->next; |
1769 | 0 | el->next = (tmp ? tmp->next : NULL); |
1770 | 0 | } |
1771 | 0 | } |
1772 | 0 | } |
1773 | 0 | if(tmp) |
1774 | 0 | removeToplevelHandler(tmp); |
1775 | 0 | else |
1776 | 0 | status = FALSE; |
1777 | |
|
1778 | 0 | return(status); |
1779 | 0 | } |
1780 | | |
1781 | | |
1782 | | /** |
1783 | | R-level entry point to remove an entry from the |
1784 | | list of top-level callbacks. 'which' should be an |
1785 | | integer and give us the 0-based index of the element |
1786 | | to be removed from the list. |
1787 | | |
1788 | | @see Rf_RemoveToplevelCallbackByIndex(int) |
1789 | | */ |
1790 | | attribute_hidden SEXP |
1791 | | R_removeTaskCallback(SEXP which) |
1792 | 0 | { |
1793 | 0 | int id; |
1794 | 0 | Rboolean val; |
1795 | |
|
1796 | 0 | if(TYPEOF(which) == STRSXP) { |
1797 | 0 | if (LENGTH(which) == 0) |
1798 | 0 | val = FALSE; |
1799 | 0 | else |
1800 | 0 | val = Rf_removeTaskCallbackByName(CHAR(STRING_ELT(which, 0))); |
1801 | 0 | } else { |
1802 | 0 | id = asInteger(which); |
1803 | 0 | if (id != NA_INTEGER) val = Rf_removeTaskCallbackByIndex(id - 1); |
1804 | 0 | else val = FALSE; |
1805 | 0 | } |
1806 | 0 | return ScalarLogical(val); |
1807 | 0 | } |
1808 | | |
1809 | | attribute_hidden SEXP |
1810 | | R_getTaskCallbackNames(void) |
1811 | 0 | { |
1812 | 0 | SEXP ans; |
1813 | 0 | R_ToplevelCallbackEl *el; |
1814 | 0 | int n = 0; |
1815 | |
|
1816 | 0 | el = Rf_ToplevelTaskHandlers; |
1817 | 0 | while(el) { |
1818 | 0 | n++; |
1819 | 0 | el = el->next; |
1820 | 0 | } |
1821 | 0 | PROTECT(ans = allocVector(STRSXP, n)); |
1822 | 0 | n = 0; |
1823 | 0 | el = Rf_ToplevelTaskHandlers; |
1824 | 0 | while(el) { |
1825 | 0 | SET_STRING_ELT(ans, n, mkChar(el->name)); |
1826 | 0 | n++; |
1827 | 0 | el = el->next; |
1828 | 0 | } |
1829 | 0 | UNPROTECT(1); |
1830 | 0 | return(ans); |
1831 | 0 | } |
1832 | | |
1833 | | /** |
1834 | | Invokes each of the different handlers giving the |
1835 | | top-level expression that was just evaluated, |
1836 | | the resulting value from the evaluation, and |
1837 | | whether the task succeeded. The last may be useful |
1838 | | if a handler is also called as part of the error handling. |
1839 | | We also have information about whether the result was printed or not. |
1840 | | We currently do not pass this to the handler. |
1841 | | */ |
1842 | | |
1843 | | /* This is not used in R and in no header */ |
1844 | | void |
1845 | | Rf_callToplevelHandlers(SEXP expr, SEXP value, Rboolean succeeded, |
1846 | | Rboolean visible) |
1847 | 0 | { |
1848 | 0 | R_ToplevelCallbackEl *h, *prev = NULL; |
1849 | 0 | Rboolean again; |
1850 | |
|
1851 | 0 | if(Rf_RunningToplevelHandlers == TRUE) |
1852 | 0 | return; |
1853 | | |
1854 | 0 | h = Rf_ToplevelTaskHandlers; |
1855 | 0 | Rf_RunningToplevelHandlers = TRUE; |
1856 | 0 | while(h) { |
1857 | 0 | Rf_RemovedToplevelHandlers = FALSE; |
1858 | 0 | Rf_DoRemoveCurrentToplevelHandler = FALSE; |
1859 | 0 | Rf_CurrentToplevelHandler = h; |
1860 | 0 | again = (h->cb)(expr, value, succeeded, visible, h->data); |
1861 | 0 | Rf_CurrentToplevelHandler = NULL; |
1862 | |
|
1863 | 0 | if (Rf_DoRemoveCurrentToplevelHandler) { |
1864 | | /* the handler attempted to remove itself, PR#18508 */ |
1865 | 0 | Rf_DoRemoveCurrentToplevelHandler = FALSE; |
1866 | 0 | again = FALSE; |
1867 | 0 | } |
1868 | 0 | if (Rf_RemovedToplevelHandlers) { |
1869 | | /* some handlers were removed, but not "h" -> recompute "prev" */ |
1870 | 0 | prev = NULL; |
1871 | 0 | R_ToplevelCallbackEl *h2 = Rf_ToplevelTaskHandlers; |
1872 | 0 | while(h2 != h) { |
1873 | 0 | prev = h2; |
1874 | 0 | h2 = h2->next; |
1875 | 0 | if (!h2) |
1876 | 0 | R_Suicide("list of toplevel callbacks was corrupted"); |
1877 | 0 | } |
1878 | 0 | } |
1879 | |
|
1880 | 0 | if(R_CollectWarnings) { |
1881 | 0 | REprintf(_("warning messages from top-level task callback '%s'\n"), |
1882 | 0 | h->name); |
1883 | 0 | PrintWarnings(); |
1884 | 0 | } |
1885 | 0 | if(again) { |
1886 | 0 | prev = h; |
1887 | 0 | h = h->next; |
1888 | 0 | } else { |
1889 | 0 | R_ToplevelCallbackEl *tmp; |
1890 | 0 | tmp = h; |
1891 | 0 | if(prev) |
1892 | 0 | prev->next = h->next; |
1893 | 0 | h = h->next; |
1894 | 0 | if(tmp == Rf_ToplevelTaskHandlers) |
1895 | 0 | Rf_ToplevelTaskHandlers = h; |
1896 | 0 | if(tmp->finalizer) |
1897 | 0 | tmp->finalizer(tmp->data); |
1898 | 0 | free(tmp->name); |
1899 | 0 | free(tmp); |
1900 | 0 | } |
1901 | 0 | } |
1902 | |
|
1903 | 0 | Rf_RunningToplevelHandlers = FALSE; |
1904 | 0 | } |
1905 | | |
1906 | | |
1907 | | static void defineVarInc(SEXP sym, SEXP val, SEXP rho) |
1908 | 0 | { |
1909 | 0 | defineVar(sym, val, rho); |
1910 | 0 | INCREMENT_NAMED(val); /* in case this is used in a NAMED build */ |
1911 | 0 | } |
1912 | | |
1913 | | attribute_hidden Rboolean |
1914 | | R_taskCallbackRoutine(SEXP expr, SEXP value, Rboolean succeeded, |
1915 | | Rboolean visible, void *userData) |
1916 | 0 | { |
1917 | | /* install some symbols */ |
1918 | 0 | static SEXP R_cbSym = NULL; |
1919 | 0 | static SEXP R_exprSym = NULL; |
1920 | 0 | static SEXP R_valueSym = NULL; |
1921 | 0 | static SEXP R_succeededSym = NULL; |
1922 | 0 | static SEXP R_visibleSym = NULL; |
1923 | 0 | static SEXP R_dataSym = NULL; |
1924 | 0 | if (R_cbSym == NULL) { |
1925 | 0 | R_cbSym = install("cb"); |
1926 | 0 | R_exprSym = install("expr"); |
1927 | 0 | R_valueSym = install("value"); |
1928 | 0 | R_succeededSym = install("succeeded"); |
1929 | 0 | R_visibleSym = install("visible"); |
1930 | 0 | R_dataSym = install("data"); |
1931 | 0 | } |
1932 | |
|
1933 | 0 | SEXP f = (SEXP) userData; |
1934 | 0 | SEXP e, val, cur, rho; |
1935 | 0 | int errorOccurred; |
1936 | 0 | Rboolean again, useData = (Rboolean)LOGICAL(VECTOR_ELT(f, 2))[0]; |
1937 | | |
1938 | | /* create an environment with bindings for the function and arguments */ |
1939 | 0 | PROTECT(rho = NewEnvironment(R_NilValue, R_NilValue, R_GlobalEnv)); |
1940 | 0 | defineVarInc(R_cbSym, VECTOR_ELT(f, 0), rho); |
1941 | 0 | defineVarInc(R_exprSym, expr, rho); |
1942 | 0 | defineVarInc(R_valueSym, value, rho); |
1943 | 0 | defineVarInc(R_succeededSym, ScalarLogical(succeeded), rho); |
1944 | 0 | defineVarInc(R_visibleSym, ScalarLogical(visible), rho); |
1945 | 0 | if(useData) |
1946 | 0 | defineVarInc(R_dataSym, VECTOR_ELT(f, 1), rho); |
1947 | | |
1948 | | /* create the call; these could be saved and re-used */ |
1949 | 0 | PROTECT(e = allocVector(LANGSXP, 5 + useData)); |
1950 | 0 | SETCAR(e, R_cbSym); cur = CDR(e); |
1951 | 0 | SETCAR(cur, R_exprSym); cur = CDR(cur); |
1952 | 0 | SETCAR(cur, R_valueSym); cur = CDR(cur); |
1953 | 0 | SETCAR(cur, R_succeededSym); cur = CDR(cur); |
1954 | 0 | SETCAR(cur, R_visibleSym); cur = CDR(cur); |
1955 | 0 | if(useData) |
1956 | 0 | SETCAR(cur, R_dataSym); |
1957 | |
|
1958 | 0 | val = R_tryEval(e, rho, &errorOccurred); |
1959 | 0 | PROTECT(val); |
1960 | | |
1961 | | /* clear the environment to reduce reference counts */ |
1962 | 0 | defineVar(R_cbSym, R_NilValue, rho); |
1963 | 0 | defineVar(R_exprSym, R_NilValue, rho); |
1964 | 0 | defineVar(R_valueSym, R_NilValue, rho); |
1965 | 0 | defineVar(R_succeededSym, R_NilValue, rho); |
1966 | 0 | defineVar(R_visibleSym, R_NilValue, rho); |
1967 | 0 | if(useData) |
1968 | 0 | defineVar(R_dataSym, R_NilValue, rho); |
1969 | |
|
1970 | 0 | if(!errorOccurred) { |
1971 | 0 | if(TYPEOF(val) != LGLSXP) { |
1972 | | /* It would be nice to identify the function. */ |
1973 | 0 | warning(_("top-level task callback did not return a logical value")); |
1974 | 0 | } |
1975 | 0 | again = (Rboolean) asLogical(val); |
1976 | 0 | } else { |
1977 | | /* warning("error occurred in top-level task callback\n"); */ |
1978 | 0 | again = FALSE; |
1979 | 0 | } |
1980 | |
|
1981 | 0 | UNPROTECT(3); /* rho, e, val */ |
1982 | |
|
1983 | 0 | return(again); |
1984 | 0 | } |
1985 | | |
1986 | | static void releaseObjectFinalizer(void *data) |
1987 | 0 | { |
1988 | 0 | R_ReleaseObject((SEXP)data); |
1989 | 0 | } |
1990 | | |
1991 | | attribute_hidden SEXP |
1992 | | R_addTaskCallback(SEXP f, SEXP data, SEXP useData, SEXP name) |
1993 | 0 | { |
1994 | 0 | SEXP internalData; |
1995 | 0 | SEXP index; |
1996 | 0 | R_ToplevelCallbackEl *el; |
1997 | 0 | const char *tmpName = NULL; |
1998 | |
|
1999 | 0 | internalData = allocVector(VECSXP, 3); |
2000 | 0 | R_PreserveObject(internalData); |
2001 | 0 | SET_VECTOR_ELT(internalData, 0, f); |
2002 | 0 | SET_VECTOR_ELT(internalData, 1, data); |
2003 | 0 | SET_VECTOR_ELT(internalData, 2, useData); |
2004 | |
|
2005 | 0 | if(length(name)) |
2006 | 0 | tmpName = CHAR(STRING_ELT(name, 0)); |
2007 | |
|
2008 | 0 | PROTECT(index = allocVector(INTSXP, 1)); |
2009 | 0 | el = Rf_addTaskCallback(R_taskCallbackRoutine, internalData, |
2010 | 0 | releaseObjectFinalizer, tmpName, |
2011 | 0 | INTEGER(index)); |
2012 | |
|
2013 | 0 | if(length(name) == 0) { |
2014 | 0 | PROTECT(name = mkString(el->name)); |
2015 | 0 | setAttrib(index, R_NamesSymbol, name); |
2016 | 0 | UNPROTECT(1); |
2017 | 0 | } else { |
2018 | 0 | setAttrib(index, R_NamesSymbol, name); |
2019 | 0 | } |
2020 | |
|
2021 | 0 | UNPROTECT(1); |
2022 | 0 | return(index); |
2023 | 0 | } |
2024 | | |
2025 | | #undef __MAIN__ |
2026 | | |
2027 | | #ifndef Win32 |
2028 | | /* this is here solely to pull in xxxpr.o */ |
2029 | | # include <R_ext/RS.h> |
2030 | | # if defined FC_LEN_T |
2031 | | # include <stddef.h> |
2032 | | void F77_SUB(rwarnc)(char *msg, int *nchar, FC_LEN_T msg_len); |
2033 | | attribute_hidden void dummy54321(void) |
2034 | 0 | { |
2035 | 0 | int nc = 5; |
2036 | 0 | F77_CALL(rwarnc)("dummy", &nc, (FC_LEN_T) 5); |
2037 | 0 | } |
2038 | | # else |
2039 | | void F77_SUB(rwarnc)(char *msg, int *nchar); |
2040 | | attribute_hidden void dummy54321(void) |
2041 | | { |
2042 | | int nc = 5; |
2043 | | F77_CALL(rwarnc)("dummy", &nc); |
2044 | | } |
2045 | | # endif |
2046 | | #endif |