Rev 14977 | Rev 17741 | Go to most recent revision | 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--2001 The R Development 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, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <Defn.h>/* -> Errormsg.h */#include <Startup.h> /* rather cleanup ..*/#include <Rconnections.h>/* limit on call length at which errorcall/warningcall is split overtwo lines */#define LONGCALL 30/* This is now non-static so that other applications that link or load libR.socan override it by defining their own version and hence by-pass the longjmpback into the standard R event loop.*/void jump_now();/*Method that resets the global state of the evaluator in the eventof an error. Was in jump_now(), but is now a separate method so thatwe can call it without invoking the longjmp. This is needed when embeddingR in other applications.*/void Rf_resetStack(int topLevel);/*Different values of inError are used to indicate different placesin the error handling.*/static int inError = 0;static int inWarning = 0;/* Interface / Calling Hierarchy :R__stop() -> do_error -> errorcall --> jump_to_toplevel --> jump_now/errorR__warning()-> do_warning -> warningcall -> if(warn >= 2) errorcall/warning /ErrorMessage()-> errorcall (but with message from ErrorDB[])WarningMessage()-> warningcall (but with message from WarningDB[]).*/void onintr(){REprintf("\n");jump_to_toplevel();}/* SIGUSR1: save and quitSIGUSR2: save and quit, don't run .Last or on.exit().*/void onsigusr1(){RCNTXT *c;inError = 1;if( R_CollectWarnings ) {inError = 2;REprintf("In addition: ");PrintWarnings();inError = 1;}R_ResetConsole();R_FlushConsole();R_ClearerrConsole();R_ParseError = 0;/* Bail out if there is a CTXT_RESTART on the stack--do we reallywant this? */for (c = R_GlobalContext; c; c = c->nextcontext) {if (IS_RESTART_BIT_SET(c->callflag)) {inError=0;findcontext(CTXT_RESTART, c->cloenv, R_RestartToken);}}/* Run all onexit/cend code on the stack (without stopping atintervening CTXT_TOPLEVEL's. Since intervening CTXT_TOPLEVEL'sget used by what are conceptually concurrent computations, thisis a bit like telling all active threads to terminate and cleanup on the way out. */R_run_onexits(NULL);R_CleanUp(SA_SAVE, 2, 1); /* quit, save, .Last, status=2 */}void onsigusr2(){inError = 1;if( R_CollectWarnings ) {inError = 2;REprintf("In addition: ");PrintWarnings();inError = 1;}R_ResetConsole();R_FlushConsole();R_ClearerrConsole();R_ParseError = 0;R_CleanUp(SA_SAVE, 0, 0);}static void setupwarnings(void){R_Warnings = allocVector(VECSXP, 50);setAttrib(R_Warnings, R_NamesSymbol, allocVector(STRSXP, 50));}/* Rvsnprintf: like vsnprintf, but guaranteed to null-terminate. */static int Rvsnprintf(char *buf, size_t size, const char *format, va_list ap){int val;#ifdef HAVE_VSNPRINTFval = vsnprintf(buf, size, format, ap);buf[size-1] = '\0';#elseval = vsprintf(buf, format, ap);#endifreturn val;}#define BUFSIZE 8192void warning(const char *format, ...){char buf[BUFSIZE], *p;va_list(ap);va_start(ap, format);Rvsnprintf(buf, BUFSIZE, format, ap);va_end(ap);p = buf + strlen(buf) - 1;if(strlen(buf) > 0 && *p == '\n') *p = '\0';warningcall(R_NilValue, buf);}/* temporary hook to allow experimenting with alternate warning mechanisms */static void (*R_WarningHook)(SEXP, char *) = NULL;void warningcall(SEXP call, const char *format, ...){int w;SEXP names, s;char *dcall, buf[BUFSIZE];RCNTXT *cptr;if (R_WarningHook != NULL) {va_list(ap);va_start(ap, format);Rvsnprintf(buf, BUFSIZE, format, ap);va_end(ap);R_WarningHook(call, buf);return;}s = GetOption(install("warning.expression"), R_NilValue);if( s!= R_NilValue ) {if( !isLanguage(s) && ! isExpression(s) )error("invalid option \"warning.expression\"");cptr = R_GlobalContext;while ( !(cptr->callflag & CTXT_FUNCTION) && cptr->callflag )cptr = cptr->nextcontext;eval(s, cptr->cloenv);return;}w = asInteger(GetOption(install("warn"), R_NilValue));if( w == NA_INTEGER ) /* set to a sensible value */w = 0;if(w < 0 || inWarning || inError) {/* ignore if w<0 or already in here*/return;}inWarning = 1;if(w >= 2) { /* make it an error */va_list(ap);va_start(ap, format);Rvsnprintf(buf, BUFSIZE, format, ap);va_end(ap);errorcall(call, "(converted from warning) %s", buf);}else if(w == 1) { /* print as they happen */va_list(ap);if( call != R_NilValue ) {dcall = CHAR(STRING_ELT(deparse1(call, 0), 0));REprintf("Warning in %s : ", dcall);if (strlen(dcall) > LONGCALL) REprintf("\n ");}elseREprintf("Warning: ");va_start(ap, format);REvprintf(format, ap);va_end(ap);REprintf("\n");}else if(w == 0) { /* collect them */va_list(ap);va_start(ap, format);if(!R_CollectWarnings)setupwarnings();if( R_CollectWarnings > 49 )return;SET_VECTOR_ELT(R_Warnings, R_CollectWarnings, call);Rvsnprintf(buf, BUFSIZE, format, ap);va_end(ap);names = CAR(ATTRIB(R_Warnings));SET_STRING_ELT(names, R_CollectWarnings++, mkChar(buf));}/* else: w <= -1 */inWarning = 0;}void PrintWarnings(void){int i;SEXP names, s, t;inWarning = 1;if( R_CollectWarnings == 1 ) {REprintf("Warning message: \n");names = CAR(ATTRIB(R_Warnings));if( VECTOR_ELT(R_Warnings, 0) == R_NilValue )REprintf("%s \n", CHAR(STRING_ELT(names, 0)));elseREprintf("%s in: %s \n", CHAR(STRING_ELT(names, 0)),CHAR(STRING_ELT(deparse1(VECTOR_ELT(R_Warnings, 0),0), 0)));}else if( R_CollectWarnings <= 10 ) {REprintf("Warning messages: \n");names = CAR(ATTRIB(R_Warnings));for(i=0; i<R_CollectWarnings; i++) {if( STRING_ELT(R_Warnings, i) == R_NilValue )REprintf("%d: %s \n",i+1, CHAR(STRING_ELT(names, i)));elseREprintf("%d: %s in: %s \n", i+1, CHAR(STRING_ELT(names, i)),CHAR(STRING_ELT(deparse1(VECTOR_ELT(R_Warnings,i), 0), 0)));}}else {if (R_CollectWarnings < 50)REprintf("There were %d warnings (use warnings() to see them)\n",R_CollectWarnings);elseREprintf("There were 50 or more warnings (use warnings() to see the first 50)\n");}/* now truncate and install last.warning */PROTECT(s = allocVector(VECSXP, R_CollectWarnings));PROTECT(t = allocVector(STRSXP, R_CollectWarnings));names = CAR(ATTRIB(R_Warnings));for(i=0; i<R_CollectWarnings; i++) {SET_VECTOR_ELT(s, i, VECTOR_ELT(R_Warnings, i));SET_VECTOR_ELT(t, i, VECTOR_ELT(names, i));}setAttrib(s, R_NamesSymbol, t);defineVar(install("last.warning"), s, R_GlobalEnv);UNPROTECT(2);inWarning = 0;R_CollectWarnings = 0;R_Warnings = R_NilValue;return;}static char errbuf[BUFSIZE];/* temporary hook to allow experimenting with alternate error mechanisms */static void (*R_ErrorHook)(SEXP, char *) = NULL;void errorcall(SEXP call, const char *format,...){char *p, *dcall;va_list(ap);if (R_ErrorHook != NULL) {char buf[BUFSIZE];void (*hook)(SEXP, char *) = R_ErrorHook;R_ErrorHook = NULL; /* to avoid recursion */va_start(ap, format);Rvsnprintf(buf, BUFSIZE, format, ap);va_end(ap);hook(call, buf);}if (inError) {if(inError == 3) {/* Can REprintf generate an error? If so we should guard for it */REprintf("Error during wrapup: ");/* this does NOT try to print the call since that couldcause a cascade of error calls */va_start(ap, format);Rvsnprintf(errbuf, sizeof(errbuf), format, ap);va_end(ap);REprintf("%s\n", errbuf);}jump_now();}if(call != R_NilValue) {char *head = "Error in ";char *mid = " : ";char *tail = "\n\t";/* <- TAB */int len = strlen(head) + strlen(mid) + strlen(tail);inError = 1;dcall = CHAR(STRING_ELT(deparse1(call, 0), 0));if (strlen(dcall) + len < BUFSIZE) {sprintf(errbuf, "%s%s%s", head, dcall, mid);if (strlen(dcall) > LONGCALL) strcat(errbuf, tail);}elsesprintf(errbuf, "Error: ");}elsesprintf(errbuf, "Error: ");p = errbuf + strlen(errbuf);va_start(ap, format);Rvsnprintf(p, BUFSIZE - strlen(errbuf), format, ap);va_end(ap);p = errbuf + strlen(errbuf) - 1;if(*p != '\n') strcat(errbuf, "\n");if (R_ShowErrorMessages) REprintf("%s", errbuf);jump_to_toplevel();}SEXP do_geterrmessage(SEXP call, SEXP op, SEXP args, SEXP env){SEXP res;checkArity(op, args);PROTECT(res = allocVector(STRSXP, 1));SET_STRING_ELT(res, 0, mkChar(errbuf));UNPROTECT(1);return res;}void error(const char *format, ...){char buf[BUFSIZE];va_list(ap);va_start(ap, format);Rvsnprintf(buf, BUFSIZE, format, ap);va_end(ap);errorcall(R_GlobalContext->call, "%s", buf);}/* Unwind the call stack in an orderly fashion *//* calling the code installed by on.exit along the way *//* and finally longjmping to the innermost TOPLEVEL context */void jump_to_toplevel(){RCNTXT *c;SEXP s, t;int haveHandler;int nback = 0;inError = 1;if( R_ShowErrorMessages && R_CollectWarnings ) {inError = 2;REprintf("In addition: ");PrintWarnings();inError = 1;}/*now see if options("error") is set */s = GetOption(install("error"), R_NilValue);haveHandler = ( s != R_NilValue );if (haveHandler) {if( !isLanguage(s) && ! isExpression(s) ) /* shouldn't happen */REprintf("invalid option \"error\"\n");else {inError = 3;if (isLanguage(s))eval(s, R_GlobalEnv);else /* expression */{int i, n = LENGTH(s);for (i = 0 ; i < n ; i++)eval(VECTOR_ELT(s, i), R_GlobalEnv);}inError = 1;}}/* reset some stuff--not sure (all) this belongs here */R_ResetConsole();R_FlushConsole();R_ClearerrConsole();R_ParseError = 0;/* find the jump target; do the jump if target is a CTXT_RESTART */for (c = R_GlobalContext; c; c = c->nextcontext) {if (c->callflag & CTXT_FUNCTION )nback++;if (IS_RESTART_BIT_SET(c->callflag)) {inError=0;findcontext(CTXT_RESTART, c->cloenv, R_RestartToken);}if (c->callflag == CTXT_TOPLEVEL)break;}/* run onexit/cend code for all contexts down to but not includingthe jump target */R_run_onexits(c);if ( !R_Interactive && !haveHandler && inError ) {REprintf("Execution halted\n");R_CleanUp(SA_NOSAVE, 1, 0); /* quit, no save, no .Last, status=1 */}PROTECT(s = allocList(nback));t = s;for (c = R_GlobalContext ;c != NULL && c->callflag != CTXT_TOPLEVEL;c = c->nextcontext)if (c->callflag & CTXT_FUNCTION ) {SETCAR(t, deparse1(c->call, 0));t = CDR(t);}setVar(install(".Traceback"), s, R_GlobalEnv);UNPROTECT(1);jump_now();}/*Absolutely no allocation can be triggered in jump_now exceptwhatever happens in R_run_onexits. The error could be an out ofmemory error and any allocation could result in an infinite-loopcondition. All you can do is reset things and exit. */void jump_now(){RCNTXT *c;/* find the jump target; do the jump if target is a CTXT_RESTART */for (c = R_GlobalContext; c; c = c->nextcontext) {if (IS_RESTART_BIT_SET(c->callflag)) {inError=0;findcontext(CTXT_RESTART, c->cloenv, R_RestartToken);}if (c->callflag == CTXT_TOPLEVEL)break;}/* at this point we should have c == R_TopLevelContext *//* Run onexit/cend code for all contexts down to but not includingthe jump target. Ordinarily this will already have been done,but it may not have been completed in a recursive errorsituation. This may cause recursive calls to jump_now, but thepossible number of such recursive calls is limited since eachexit function is removed before it its executed. This is not agreat design because we could run out of other resources thatare on the stack (like C stack for example). The right thingto do is arrange execute exit *after* the LONGJMP, but thatrequires a more extensive redesign of the non-local transfer ofcontrol mechanism. LT. */R_run_onexits(R_ToplevelContext);Rf_resetStack(0);LONGJMP(R_ToplevelContext->cjmpbuf, 0);}/*The topLevelReset argument controls whether the R_GlobalContext isreset to its initial condition.In regular stand-alone R, this is not needed (see jump_now() above).But when R is embedded in an application, this must be set or otherwisesubsequent errors get caught in an infinite loop when iterating overthe contexts in the error handling routine jump_to_toplevel() above.*/void Rf_resetStack(int topLevelReset) {if( inError == 2 )REprintf("Lost warning messages\n");inError=0;inWarning=0;R_Warnings = R_NilValue;R_CollectWarnings = 0;R_restore_globals(R_ToplevelContext);if(topLevelReset) {R_GlobalContext = R_ToplevelContext;}}#ifdef OLD_Macintosh#include <signal.h>#include <errno.h>void isintrpt(){register EvQElPtr q;for (q = (EvQElPtr) GetEvQHdr()->qHead; q; q = (EvQElPtr) q->qLink)if (q->evtQWhat == keyDown && (char) q->evtQMessage == '.')if (q->evtQModifiers & cmdKey) {FlushEvents(keyDownMask, 0);raise(SIGINT);/* errno = EINTR; */return;}}#endifSEXP do_stop(SEXP call, SEXP op, SEXP args, SEXP rho){/* error(.) : really doesn't return anything; but all do_foo() must be SEXP */RCNTXT *cptr;SEXP c_call;if(asLogical(CAR(args))) {/* find context -> "Error in ..:" */cptr = R_GlobalContext->nextcontext;while ( !(cptr->callflag & CTXT_FUNCTION) && cptr->nextcontext != NULL)cptr = cptr->nextcontext;c_call = cptr->call;}elsec_call = R_NilValue;args = CDR(args);if (CAR(args) != R_NilValue) { /* message */SETCAR(args, coerceVector(CAR(args), STRSXP));if(!isValidString(CAR(args)))errorcall(c_call, " [invalid string in stop(.)]");errorcall(c_call, "%s", CHAR(STRING_ELT(CAR(args), 0)));}elseerrorcall(c_call, "");/* never called: */return c_call;}SEXP do_warning(SEXP call, SEXP op, SEXP args, SEXP rho){RCNTXT *cptr;cptr = R_GlobalContext->nextcontext;while ( !(cptr->callflag & CTXT_FUNCTION) && cptr->nextcontext != NULL)cptr = cptr->nextcontext;if (CAR(args) != R_NilValue) {SETCAR(args, coerceVector(CAR(args), STRSXP));if(!isValidString(CAR(args)))warningcall(cptr->call, " [invalid string in warning(.)]");elsewarningcall(cptr->call,"%s", CHAR(STRING_ELT(CAR(args), 0)));}elsewarningcall(cptr->call,"");return CAR(args);}/* Error recovery for incorrect argument count error. */void WrongArgCount(const char *s){error("incorrect number of arguments to \"%s\"", s);}void UNIMPLEMENTED(const char *s){error("Unimplemented feature in %s", s);}/* ERROR_.. codes in Errormsg.h */static struct {const R_WARNING code;const char* const format;}const ErrorDB[] = {{ ERROR_NUMARGS, "invalid number of arguments" },{ ERROR_ARGTYPE, "invalid argument type" },{ ERROR_TSVEC_MISMATCH, "time-series/vector length mismatch" },{ ERROR_INCOMPAT_ARGS, "incompatible arguments" },{ ERROR_UNIMPLEMENTED, "unimplemented feature in %s" },{ ERROR_UNKNOWN, "unknown error (report this!)" }};static struct {R_WARNING code;char* format;}WarningDB[] = {{ WARNING_coerce_NA, "NAs introduced by coercion" },{ WARNING_coerce_INACC, "inaccurate integer conversion in coercion" },{ WARNING_coerce_IMAG, "imaginary parts discarded in coercion" },{ WARNING_UNKNOWN, "unknown warning (report this!)" },};void ErrorMessage(SEXP call, int which_error, ...){int i;char buf[BUFSIZE];va_list(ap);i = 0;while(ErrorDB[i].code != ERROR_UNKNOWN) {if (ErrorDB[i].code == which_error)break;i++;}va_start(ap, which_error);Rvsnprintf(buf, BUFSIZE, ErrorDB[i].format, ap);va_end(ap);errorcall(call, "%s", buf);}void WarningMessage(SEXP call, R_WARNING which_warn, ...){int i;char buf[BUFSIZE];va_list(ap);i = 0;while(WarningDB[i].code != WARNING_UNKNOWN) {if (WarningDB[i].code == which_warn)break;i++;}va_start(ap, which_warn);Rvsnprintf(buf, BUFSIZE, WarningDB[i].format, ap);va_end(ap);warningcall(call, "%s", buf);}/* Temporary hooks to allow experimenting with alternate error andwarning mechanisms. They are not in the header files for now, butthe following snippet can serve as a header file: */void R_ReturnOrRestart(SEXP val, SEXP env, Rboolean restart);void R_PrintDeferredWarnings(void);SEXP R_GetTraceback(int);void R_SetErrmessage(char *s);void R_SetErrorHook(void (*hook)(SEXP, char *));void R_SetWarningHook(void (*hook)(SEXP, char *));void R_JumpToToplevel(Rboolean restart);void R_SetWarningHook(void (*hook)(SEXP, char *)){R_WarningHook = hook;}void R_SetErrorHook(void (*hook)(SEXP, char *)){R_ErrorHook = hook;}void R_ReturnOrRestart(SEXP val, SEXP env, Rboolean restart){int mask;RCNTXT *c;mask = CTXT_BROWSER | CTXT_FUNCTION;for (c = R_GlobalContext; c; c = c->nextcontext) {if (c->callflag & mask && c->cloenv == env)findcontext(mask, env, val);else if (restart && IS_RESTART_BIT_SET(c->callflag))findcontext(CTXT_RESTART, c->cloenv, R_RestartToken);else if (c->callflag == CTXT_TOPLEVEL)error("No function to return from, jumping to top level");}}void R_JumpToToplevel(Rboolean restart){RCNTXT *c;/* Find the target for the jump */for (c = R_GlobalContext; c != NULL; c = c->nextcontext) {if (restart && IS_RESTART_BIT_SET(c->callflag))findcontext(CTXT_RESTART, c->cloenv, R_RestartToken);else if (c->callflag == CTXT_TOPLEVEL)break;}if (c != R_ToplevelContext)warning("top level inconsistency?");/* Run onexit/cend code for everything above the target. */R_run_onexits(c);R_restore_globals(c);R_ToplevelContext = R_GlobalContext = c;LONGJMP(c->cjmpbuf, CTXT_TOPLEVEL);}void R_SetErrmessage(char *s){strncpy(errbuf, s, sizeof(errbuf));errbuf[sizeof(errbuf) - 1] = 0;}void R_PrintDeferredWarnings(void){if( R_ShowErrorMessages && R_CollectWarnings ) {REprintf("In addition: ");PrintWarnings();}}/* doesn't stop at TOPLEVEL--should once browser is changed to use RESTART */SEXP R_GetTraceback(int skip){int nback = 0, ns;RCNTXT *c;SEXP s, t;for (c = R_GlobalContext, ns = skip;c != NULL && c->callflag != CTXT_TOPLEVEL;c = c->nextcontext)if (c->callflag & CTXT_FUNCTION ) {if (ns > 0)ns--;elsenback++;}PROTECT(s = allocList(nback));t = s;for (c = R_GlobalContext ;c != NULL && c->callflag != CTXT_TOPLEVEL;c = c->nextcontext)if (c->callflag & CTXT_FUNCTION ) {if (skip > 0)skip--;else {SETCAR(t, deparse1(c->call, 0));t = CDR(t);}}UNPROTECT(1);return s;}