Rev 83466 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Language for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka* Copyright (C) 1997--2023 The R Core Team** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, a copy is available at* https://www.R-project.org/Licenses/*//* <UTF8> char here is handled as a whole string *//* See system.txt for a description of functions*/#ifdef HAVE_CONFIG_H# include <config.h>#endif#define R_USE_SIGNALS 1#include <Defn.h>#include <locale.h>/* necessary for some (older, i.e., ~ <= 1997) Linuxen, and apparentlyalso some AIX systems. NB, included unconditionally later on.*/#ifndef FD_SET# ifdef HAVE_SYS_TIME_H# include <sys/time.h># endif#endif#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#include <errno.h>#include "Fileio.h"// This creates the interface pointers in this file#define __SYSTEM__#define R_INTERFACE_PTRS 1#include <Rinterface.h>#undef __SYSTEM__#include "Runix.h"attribute_hidden FILE *ifp = NULL; /* used in sys-std.c */attribute_hiddenRboolean UsingReadline = TRUE; /* used in sys-std.c & ../main/platform.cand also in sys-unix.c for tilde expansion *//* call pointers to allow interface switching */NORET void R_Suicide(const char *s) {ptr_R_Suicide(s);// This should not have returned, but belt-and-bracesexit(2); // same status as Rstd_Suicide}void R_ShowMessage(const char *s) { ptr_R_ShowMessage(s); }int R_ReadConsole(const char *prompt, unsigned char *buf, int len, int addtohistory){ return ptr_R_ReadConsole(prompt, buf, len, addtohistory); }void R_WriteConsole(const char *buf, int len) {if (ptr_R_WriteConsole) ptr_R_WriteConsole(buf, len); else ptr_R_WriteConsoleEx(buf, len, 0); }void R_WriteConsoleEx(const char *buf, int len, int otype) {if (ptr_R_WriteConsole) ptr_R_WriteConsole(buf, len); else ptr_R_WriteConsoleEx(buf, len, otype); }void R_ResetConsole(void) { ptr_R_ResetConsole(); }#ifndef HAVE_AQUAvoid R_FlushConsole(void) { ptr_R_FlushConsole(); }#endifvoid R_ClearerrConsole(void) { ptr_R_ClearerrConsole(); }void R_Busy(int which) { ptr_R_Busy(which); }NORET void R_CleanUp(SA_TYPE saveact, int status, int runLast){ptr_R_CleanUp(saveact, status, runLast);// This should not have returned, but belt-and-bracesexit(status);}attribute_hiddenint R_ShowFiles(int nfile, const char **file, const char **headers,const char *wtitle, Rboolean del, const char *pager){ return ptr_R_ShowFiles(nfile, file, headers, wtitle, del, pager); }attribute_hiddenint R_ChooseFile(int _new, char *buf, int len){ return ptr_R_ChooseFile(_new, buf, len); }void R_setStartTime(void); /* in sys-unix.c */#ifdef HAVE_AQUA/* used here and in main/sysutils.c (for system). */Rboolean useaqua = FALSE;// Finally in Sep 2012 R.app sets ptr_R_FlushConsole#include <R_ext/Rdynload.h>DL_FUNC ptr_do_flushconsole;void R_FlushConsole(void) {if (ptr_R_FlushConsole) ptr_R_FlushConsole();else if (ptr_do_flushconsole) ptr_do_flushconsole();}#endifvoid R_setupHistory(void){int value, ierr;char *p;if ((R_HistoryFile = getenv("R_HISTFILE")) == NULL)R_HistoryFile = ".Rhistory";R_HistorySize = 512;if ((p = getenv("R_HISTSIZE"))) {value = (int) R_Decode2Long(p, &ierr);if (ierr != 0 || value < 0)R_ShowMessage("WARNING: invalid R_HISTSIZE ignored;");elseR_HistorySize = value;}}#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT)/*Needed by AIX and formerly by macOS (but not by POSIX).http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.basetrf1/getrlimit_64.htm*/# ifdef HAVE_SYS_TIME_H# include <sys/time.h># endif# include <sys/resource.h># ifdef HAVE_LIBC_STACK_ENDextern void * __libc_stack_end;# endif# ifdef HAVE_KERN_USRSTACK# include <unistd.h># include <sys/types.h># include <sys/sysctl.h># endif#endifint R_running_as_main_program = 0;/* In ../main/main.c, to avoid inlining */extern uintptr_t dummy_ii(void);/* Protection against embedded misuse, PR#15420 */static int num_initialized = 0;static char* unescape_arg(char *p, char* avp) {/* Undo the escaping done in the front end */char *q;for(q = avp; *q; q++) {if(*q == '~' && *(q+1) == '+' && *(q+2) == '~') {q += 2;*p++ = ' ';} else if(*q == '~' && *(q+1) == 'n' && *(q+2) == '~') {q += 2;*p++ = '\n';} else if(*q == '~' && *(q+1) == 't' && *(q+2) == '~') {q += 2;*p++ = '\t';} else *p++ = *q;}return p;}/* for thr_stksegment */#if defined(HAVE_THREAD_H)# include <thread.h>#endif#include <signal.h> /* thr_stksegment */extern int R_isWriteableDir(char *path);int Rf_initialize_R(int ac, char **av){int i, ioff = 1, j;Rboolean useX11 = TRUE, useTk = FALSE;char *p, msg[1024], cmdlines[10000], **avv;structRstart rstart;Rstart Rp = &rstart;Rboolean force_interactive = FALSE;if (num_initialized++) {fprintf(stderr, "%s", "R is already initialized\n");exit(1);}#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT){/* getrlimit is POSIX:https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html*/struct rlimit rlim;R_CStackDir = C_STACK_DIRECTION;if(getrlimit(RLIMIT_STACK, &rlim) == 0) {/* 'unlimited' is represented by RLIM_INFINITY, which is avery large (but maybe not the largest) representable value.The standard allows the values RLIM_SAVED_CUR andRLIB_SAVED_MAX, apparently used on 32-bit AIX.(http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.basetrf1/getrlimit_64.htm)These may or may not be different from RLIM_INFINITY (theyare the same on Linux and macOS but not Solaris where theyare larger). We will assume that unrepresentable limitsare very large.This is cautious: it is extremely unlikely that the softlimit is either unlimited or unrepresentable.*/rlim_t lim = rlim.rlim_cur;#if defined(RLIM_SAVED_CUR) && defined(RLIM_SAVED_MAX)if (lim == RLIM_SAVED_CUR || lim == RLIM_SAVED_MAX)lim = RLIM_INFINITY;#endifif (lim != RLIM_INFINITY) R_CStackLimit = (uintptr_t) lim;}#if defined(HAVE_LIBC_STACK_END){R_CStackStart = (uintptr_t) __libc_stack_end;/* The libc stack end is not exactly at the stack start, so onecannot access __libc_stack_end - R_CStackLimit/getrlimit + 1. Wehave to find the real stack start that matches getrlimit.A modern alternative to __libc_stack_end and to parsing /proc/mapsdirectly is pthread_getattr_np; it doesn't provide the exact stackstart, either, but provides a matching stack size smaller thanthe one obtained from getrlimit. However, pthread_getattr_npmay have not worked properly on old Linux distributions. *//* based on GDB relocatable.c */FILE *f;f = fopen("/proc/self/maps", "r");if (f) {for(;;) {int c;unsigned long start, end;if (fscanf(f, "%lx-%lx", &start, &end) == 2 &&R_CStackStart >= (uintptr_t)start &&R_CStackStart < (uintptr_t)end) {/* would this be ok for R_CStackDir == -1? */R_CStackStart = (uintptr_t) ((R_CStackDir == 1) ? end : start);break;}for(c = getc(f); c != '\n' && c != EOF; c = getc(f));if (c == EOF) {/* could also abort here, but R will usually work withR_CStackStart set just for __libc_stack_end */fprintf(stderr, "WARNING: Error parsing /proc/self/maps!\n");break;}}fclose(f);}}#elif defined(HAVE_KERN_USRSTACK){/* Borrowed from mzscheme/gc/os_dep.c */int nm[2] = {CTL_KERN, KERN_USRSTACK};void * base;size_t len = sizeof(void *);(void) sysctl(nm, 2, &base, &len, NULL, 0);R_CStackStart = (uintptr_t) base;}#elif defined(HAVE_THR_STKSEGMENT){/* Solaris */stack_t stack;if (thr_stksegment(&stack))R_Suicide("Cannot obtain stack information (thr_stksegment).");R_CStackStart = (uintptr_t) stack.ss_sp;/* This _may_ have to be adjusted for a (perhaps theoretical) platformwhere the stack would grow upwards.The stack size could be updated based on stack.ss_size, but experimentssuggest getrlimit is safe here. */}#elseif(R_running_as_main_program) {/* This is not the main program, but unless embedded it isnear the top, 5540 bytes away when checked. */R_CStackStart = (uintptr_t) &i + (6000 * R_CStackDir);}#endifif(R_CStackStart == (uintptr_t)(-1)) R_CStackLimit = (uintptr_t)(-1); /* never set *//* setup_Rmainloop includes (disabled) code to test stack detection */}#endifptr_R_Suicide = Rstd_Suicide;ptr_R_ShowMessage = Rstd_ShowMessage;ptr_R_ReadConsole = Rstd_ReadConsole;ptr_R_WriteConsole = Rstd_WriteConsole;ptr_R_ResetConsole = Rstd_ResetConsole;ptr_R_FlushConsole = Rstd_FlushConsole;ptr_R_ClearerrConsole = Rstd_ClearerrConsole;ptr_R_Busy = Rstd_Busy;ptr_R_CleanUp = Rstd_CleanUp;ptr_R_ShowFiles = Rstd_ShowFiles;ptr_R_ChooseFile = Rstd_ChooseFile;ptr_R_loadhistory = Rstd_loadhistory;ptr_R_savehistory = Rstd_savehistory;ptr_R_addhistory = Rstd_addhistory;ptr_R_EditFile = NULL; /* for future expansion */R_timeout_handler = NULL;R_timeout_val = 0;R_GlobalContext = NULL; /* Make R_Suicide less messy... */if((R_Home = R_HomeDir()) == NULL)R_Suicide("R home directory is not defined");BindDomain(R_Home);process_system_Renviron();R_setStartTime();R_DefParamsEx(Rp, RSTART_VERSION);/* Store the command line arguments before they are processedby the R option handler.*/R_set_command_line_arguments(ac, av);cmdlines[0] = '\0';/* first task is to select the GUI.If run from the shell script, only Tk|tk|X11|x11 are allowed.*/for(i = 0, avv = av; i < ac; i++, avv++) {if (!strcmp(*avv, "--args"))break;if(!strncmp(*avv, "--gui", 5) || !strncmp(*avv, "-g", 2)) {if(!strncmp(*avv, "--gui", 5) && strlen(*avv) >= 7)p = &(*avv)[6];else {if(i+1 < ac) {avv++; p = *avv; ioff++;} else {snprintf(msg, 1024,_("WARNING: --gui or -g without value ignored"));R_ShowMessage(msg);p = "X11";}}if(!strcmp(p, "none"))useX11 = FALSE; // not allowed from R.sh#ifdef HAVE_AQUAelse if(!strcmp(p, "aqua"))useaqua = TRUE; // not allowed from R.sh but used by R.app#endifelse if(!strcmp(p, "X11") || !strcmp(p, "x11"))useX11 = TRUE;else if(!strcmp(p, "Tk") || !strcmp(p, "tk"))useTk = TRUE;else {#ifdef HAVE_X11snprintf(msg, 1024,_("WARNING: unknown gui '%s', using X11\n"), p);#elsesnprintf(msg, 1024,_("WARNING: unknown gui '%s', using none\n"), p);#endifR_ShowMessage(msg);}/* now remove it/them */for(j = i; j < ac - ioff; j++)av[j] = av[j + ioff];ac -= ioff;break;}}#ifdef HAVE_X11if(useX11) R_GUIType = "X11";#endif /* HAVE_X11 */#ifdef HAVE_AQUAif(useaqua) R_GUIType = "AQUA";#endif#ifdef HAVE_TCLTKif(useTk) R_GUIType = "Tk";#endifR_common_command_line(&ac, av, Rp);while (--ac) {if (**++av == '-') {if(!strcmp(*av, "--no-readline")) {UsingReadline = FALSE;} else if(!strcmp(*av, "-f")) {ac--; av++;#define R_INIT_TREAT_F(_AV_) \Rp->R_Interactive = FALSE; \if(strcmp(_AV_, "-")) { \if(strlen(_AV_) >= R_PATH_MAX) { \snprintf(msg, 1024, \_("path given in -f/--file is too long")); \R_Suicide(msg); \} \char path[R_PATH_MAX], *p = path; \p = unescape_arg(p, _AV_); \*p = '\0'; \ifp = R_fopen(path, "r"); \if(!ifp) { \snprintf(msg, 1024, \_("cannot open file '%s': %s"), \path, strerror(errno)); \R_Suicide(msg); \} \}R_INIT_TREAT_F(*av);} else if(!strncmp(*av, "--file=", 7)) {R_INIT_TREAT_F((*av)+7);} else if(!strcmp(*av, "-e")) {ac--; av++;Rp->R_Interactive = FALSE;if(strlen(cmdlines) + strlen(*av) + 2 <= 10000) {char *p = cmdlines+strlen(cmdlines);p = unescape_arg(p, *av);*p++ = '\n'; *p = '\0';} else {snprintf(msg, 1024, _("WARNING: '-e %s' omitted as input is too long\n"), *av);R_ShowMessage(msg);}} else if(!strcmp(*av, "--args")) {break;} else if(!strcmp(*av, "--interactive")) {force_interactive = TRUE;break;} else {#ifdef HAVE_AQUA// r27492: in 2003 launching from 'Finder OSX' passed thisif(!strncmp(*av, "-psn", 4)) break; else#endifsnprintf(msg, 1024, _("WARNING: unknown option '%s'\n"), *av);R_ShowMessage(msg);}} else {snprintf(msg, 1024, _("ARGUMENT '%s' __ignored__\n"), *av);R_ShowMessage(msg);}}if(strlen(cmdlines)) { /* had at least one -e option */size_t res;char *tm;static char ifile[R_PATH_MAX] = "\0";int ifd;if(ifp) R_Suicide(_("cannot use -e with -f or --file"));/* tmpfile() does not respect TMPDIR on some systems (PR#17925).R_TempDir is not initialized, yet. */tm = getenv("TMPDIR");if (!R_isWriteableDir(tm)) {tm = getenv("TMP");if (!R_isWriteableDir(tm)) {tm = getenv("TEMP");if (!R_isWriteableDir(tm))tm = "/tmp";}}snprintf(ifile, R_PATH_MAX, "%s/Rscript%x.XXXXXX", tm, getpid());ifd = mkstemp(ifile);if (ifd >= 0) /* -1 on error, can be 0 if stdin is closed */ifp = fdopen(ifd, "w+");if(!ifp) R_Suicide(_("creating temporary file for '-e' failed"));unlink(ifile);res = fwrite(cmdlines, strlen(cmdlines)+1, 1, ifp);if(res != 1) error("fwrite error in initialize_R");fflush(ifp);rewind(ifp);}if (ifp && Rp->SaveAction != SA_SAVE) Rp->SaveAction = SA_NOSAVE;R_SetParams(Rp);if(!Rp->NoRenviron) {process_site_Renviron();process_user_Renviron();/* allow for R_MAX_[VN]SIZE and R_[VN]SIZE in user/site Renviron */R_SizeFromEnv(Rp);R_SetParams(Rp);}/* On Unix the console is a file; we just use stdio to write on it */#ifdef HAVE_AQUAif(useaqua)R_Interactive = useaqua;else#endifR_Interactive = R_Interactive && (force_interactive || R_isatty(0));#ifdef HAVE_AQUA/* for Aqua and non-dumb terminal use callbacks instead of connectionsand pretty-print warnings/errors (ESS = dumb terminal) */if(useaqua ||(R_Interactive && getenv("TERM") && strcmp(getenv("TERM"), "dumb"))) {R_Outputfile = NULL;R_Consolefile = NULL;ptr_R_WriteConsoleEx = Rstd_WriteConsoleEx;ptr_R_WriteConsole = NULL;} else {#endifR_Outputfile = stdout;R_Consolefile = stderr;#ifdef HAVE_AQUA}#endif/** Since users' expectations for save/no-save will differ, we decided* that they should be forced to specify in the non-interactive case.*/if (!R_Interactive && Rp->SaveAction != SA_SAVE &&Rp->SaveAction != SA_NOSAVE)R_Suicide(_("you must specify '--save', '--no-save' or '--vanilla'"));R_setupHistory();if (R_RestoreHistory)Rstd_read_history(R_HistoryFile);fpu_setup(1);return(0);}/*This function can be used to open the named files in texteditors. If the file does not exist then the editor should beopened to create a new file. On GUI platforms multiple filescan be opened in separate editor windows, but this currentlyonly works on Windows and Aqua.*//** nfile = number of files* file = array of filenames* editor = editor to be used.*/int R_EditFiles(int nfile, const char **file, const char **title,const char *editor){char buf[1024];if (ptr_R_EditFiles) return(ptr_R_EditFiles(nfile, file, title, editor));if (nfile > 0) {if (nfile > 1)R_ShowMessage(_("WARNING: Only editing the first in the list of files"));if (ptr_R_EditFile) ptr_R_EditFile((char *) file[0]);else {/* Quote path if not quoted */if (editor[0] != '"')snprintf(buf, 1024, "\"%s\" \"%s\"", editor, file[0]);elsesnprintf(buf, 1024, "%s \"%s\"", editor, file[0]);if (R_system(buf) == 127)warningcall(R_NilValue, _("error in running command"));}return 0;}return 1;}/* Returns the limit on the number of open files. On error or when nolimit is known, returns a negative number. */int R_GetFDLimit(void) {#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT)struct rlimit rlim;/* Historically this was RLIMIT_OFILE on BSD, but we require thePOSIX version.Most often RLIM_INFINITY >= INT_MAX, but not on some 32-bitsystems. On all current systems the limit will be at most afew thousand.Note that 'unlimited' here probably does not mean it:e.g. there is a kernel limit of OPEN_MAX on macOS.*/if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {rlim_t lim = rlim.rlim_cur;#if defined(RLIM_SAVED_CUR) && defined(RLIM_SAVED_MAX)if (lim == RLIM_SAVED_CUR || lim == RLIM_SAVED_MAX)lim = RLIM_INFINITY;#endifreturn (int)((lim > INT_MAX) ? INT_MAX : lim);}#endifreturn -1;}/* Tries to ensure that the limit on the number of open files is at leastas desired. Returns 'desired' if successful, otherwise a smaller positivenumber giving the current limit. On error (no limit known), a negativenumber is returned. */int R_EnsureFDLimit(int desired) {#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_SETRLIMIT) && defined(HAVE_GETRLIMIT)struct rlimit rlim;if (getrlimit(RLIMIT_NOFILE, &rlim))return -1;rlim_t lim = rlim.rlim_cur;#if defined(RLIM_SAVED_CUR) && defined(RLIM_SAVED_MAX)if (lim == RLIM_SAVED_CUR || lim == RLIM_SAVED_MAX)lim = RLIM_INFINITY;#endifif (lim == RLIM_INFINITY || lim >= desired)return desired;/* increase the limit */rlim_t hlim = rlim.rlim_max;#if defined(RLIM_SAVED_CUR) && defined(RLIM_SAVED_MAX)if (hlim == RLIM_SAVED_CUR || hlim == RLIM_SAVED_MAX)hlim = RLIM_INFINITY;#endifif (hlim == RLIM_INFINITY || hlim >= desired)rlim.rlim_cur = (rlim_t) desired;elserlim.rlim_cur = hlim;if (setrlimit(RLIMIT_NOFILE, &rlim))return (int) lim; /* also could return error */return (int) rlim.rlim_cur;#elsereturn -1;#endif}