diff --git a/lib/error.g b/lib/error.g index 34addd9eb20..b6bd051c8e3 100644 --- a/lib/error.g +++ b/lib/error.g @@ -15,6 +15,12 @@ CallAndInstallPostRestore( function() ASS_GVAR( "QUITTING", false ); end); +# This global variable is used by ErrorInner, Where, and WHERE to determine +# which stream they should print their output to. +# If an interactive SHELL is started by a break loop it still listens +# and prints to "*errin*" and "*errout*" respectively. +GAP_ERROR_STREAM := "*errout*"; + ############################################################################# ## @@ -50,17 +56,17 @@ BIND_GLOBAL("WHERE", function( context, depth, outercontext) bottom := GetBottomLVars(); lastcontext := outercontext; while depth > 0 and context <> bottom do - PRINT_CURRENT_STATEMENT(context); - Print(" called from\n"); + PRINT_CURRENT_STATEMENT(GAP_ERROR_STREAM, context); + PrintTo(GAP_ERROR_STREAM, " called from\n"); lastcontext := context; context := ParentLVars(context); depth := depth-1; od; if depth = 0 then - Print("... "); + PrintTo(GAP_ERROR_STREAM, "... "); else f := ContentsLVars(lastcontext).func; - Print("( )\n called from read-eval loop "); fi; end); @@ -75,11 +81,11 @@ BIND_GLOBAL("Where", function(arg) fi; if ErrorLVars = fail or ErrorLVars = GetBottomLVars() then - Print("not in any function "); + PrintTo(GAP_ERROR_STREAM, "not in any function "); else WHERE(ParentLVars(ErrorLVars),depth, ErrorLVars); fi; - Print("at ",INPUT_FILENAME(),":",INPUT_LINENUMBER(),"\n"); + PrintTo(GAP_ERROR_STREAM, "at ",INPUT_FILENAME(),":",INPUT_LINENUMBER(),"\n"); end); OnBreak := Where; @@ -115,7 +121,7 @@ BIND_GLOBAL("ErrorInner", context := arg[1].context; if not IsLVarsBag(context) then - PrintTo("*errout*", "ErrorInner: option context must be a local variables bag\n"); + PrintTo(GAP_ERROR_STREAM, "ErrorInner: option context must be a local variables bag\n"); LEAVE_ALL_NAMESPACES(); JUMP_TO_CATCH(1); fi; @@ -123,7 +129,7 @@ BIND_GLOBAL("ErrorInner", if IsBound(arg[1].justQuit) then justQuit := arg[1].justQuit; if not justQuit in [false, true] then - PrintTo("*errout*", "ErrorInner: option justQuit must be true or false\n"); + PrintTo(GAP_ERROR_STREAM, "ErrorInner: option justQuit must be true or false\n"); LEAVE_ALL_NAMESPACES(); JUMP_TO_CATCH(1); fi; @@ -134,7 +140,7 @@ BIND_GLOBAL("ErrorInner", if IsBound(arg[1].mayReturnVoid) then mayReturnVoid := arg[1].mayReturnVoid; if not mayReturnVoid in [false, true] then - PrintTo("*errout*", "ErrorInner: option mayReturnVoid must be true or false\n"); + PrintTo(GAP_ERROR_STREAM, "ErrorInner: option mayReturnVoid must be true or false\n"); LEAVE_ALL_NAMESPACES(); JUMP_TO_CATCH(1); fi; @@ -145,7 +151,7 @@ BIND_GLOBAL("ErrorInner", if IsBound(arg[1].mayReturnObj) then mayReturnObj := arg[1].mayReturnObj; if not mayReturnObj in [false, true] then - PrintTo("*errout*", "ErrorInner: option mayReturnObj must be true or false\n"); + PrintTo(GAP_ERROR_STREAM, "ErrorInner: option mayReturnObj must be true or false\n"); LEAVE_ALL_NAMESPACES(); JUMP_TO_CATCH(1); fi; @@ -156,7 +162,7 @@ BIND_GLOBAL("ErrorInner", if IsBound(arg[1].printThisStatement) then printThisStatement := arg[1].printThisStatement; if not printThisStatement in [false, true] then - PrintTo("*errout*", "ErrorInner: option printThisStatement must be true or false\n"); + PrintTo(GAP_ERROR_STREAM, "ErrorInner: option printThisStatement must be true or false\n"); LEAVE_ALL_NAMESPACES(); JUMP_TO_CATCH(1); fi; @@ -167,7 +173,7 @@ BIND_GLOBAL("ErrorInner", if IsBound(arg[1].lateMessage) then lateMessage := arg[1].lateMessage; if not lateMessage in [false, true] and not IsString(lateMessage) then - PrintTo("*errout*", "ErrorInner: option lateMessage must be a string or false\n"); + PrintTo(GAP_ERROR_STREAM, "ErrorInner: option lateMessage must be a string or false\n"); LEAVE_ALL_NAMESPACES(); JUMP_TO_CATCH(1); fi; @@ -177,7 +183,7 @@ BIND_GLOBAL("ErrorInner", earlyMessage := arg[2]; if Length(arg) <> 2 then - PrintTo("*errout*","ErrorInner: new format takes exactly two arguments\n"); + PrintTo(GAP_ERROR_STREAM, "ErrorInner: new format takes exactly two arguments\n"); LEAVE_ALL_NAMESPACES(); JUMP_TO_CATCH(1); fi; @@ -187,34 +193,33 @@ BIND_GLOBAL("ErrorInner", errorLVars := ErrorLVars; ErrorLVars := context; if QUITTING or not BreakOnError then - PrintTo("*errout*","Error, "); + PrintTo(GAP_ERROR_STREAM, "Error, "); for x in earlyMessage do - PrintTo("*errout*",x); + PrintTo(GAP_ERROR_STREAM, x); od; - PrintTo("*errout*","\n"); + PrintTo(GAP_ERROR_STREAM, "\n"); ErrorLevel := ErrorLevel-1; ErrorLVars := errorLVars; if ErrorLevel = 0 then LEAVE_ALL_NAMESPACES(); fi; JUMP_TO_CATCH(0); fi; - PrintTo("*errout*","Error, "); + PrintTo(GAP_ERROR_STREAM, "Error, "); for x in earlyMessage do - PrintTo("*errout*",x); + PrintTo(GAP_ERROR_STREAM, x); od; if printThisStatement then if context <> GetBottomLVars() then - PrintTo("*errout*"," in\n \c"); - PRINT_CURRENT_STATEMENT(context); - Print("\c"); - PrintTo("*errout*"," called from \n"); + PrintTo(GAP_ERROR_STREAM, " in\n "); + PRINT_CURRENT_STATEMENT(GAP_ERROR_STREAM, context); + PrintTo(GAP_ERROR_STREAM, " called from \n"); else - PrintTo("*errout*","\c\n"); + PrintTo(GAP_ERROR_STREAM, "\n"); fi; else location := CURRENT_STATEMENT_LOCATION(context); - if location <> fail then PrintTo("*errout*", " at ", location[1], ":", location[2]); + if location <> fail then PrintTo(GAP_ERROR_STREAM, " at ", location[1], ":", location[2]); fi; - PrintTo("*errout*"," called from\c\n"); + PrintTo(GAP_ERROR_STREAM, " called from\n"); fi; if SHOULD_QUIT_ON_BREAK() then @@ -225,7 +230,7 @@ BIND_GLOBAL("ErrorInner", OnBreak(); fi; if IsString(lateMessage) then - PrintTo("*errout*",lateMessage,"\n"); + PrintTo(GAP_ERROR_STREAM, lateMessage,"\n"); elif lateMessage then if IsBound(OnBreakMessage) and IsFunction(OnBreakMessage) then OnBreakMessage(); diff --git a/src/error.c b/src/error.c index 443072d9875..bc9d7a5ad71 100644 --- a/src/error.c +++ b/src/error.c @@ -152,22 +152,44 @@ Obj FuncCURRENT_STATEMENT_LOCATION(Obj self, Obj context) return retlist; } -Obj FuncPRINT_CURRENT_STATEMENT(Obj self, Obj context) + +Obj FuncPRINT_CURRENT_STATEMENT(Obj self, Obj stream, Obj context) { if (context == STATE(BottomLVars)) return 0; + /* HACK: we want to redirect output */ + /* Try to print the output to stream. Use *errout* as a fallback. */ + if ((IsStringConv(stream) && !OpenOutput(CSTR_STRING(stream))) || + (!IS_STRING(stream) && !OpenOutputStream(stream))) { + if (OpenOutput("*errout*")) { + Pr("PRINT_CURRENT_STATEMENT: can not open error stream\n", 0, 0); + } + else { + int ret = fputs("gap: panic, can not open *errout*!\n", stderr); + /* If that failed, try printing to stdout */ + if (ret == EOF) { + fputs("gap: panic, can not open *errout*!\n", stdout); + } + SyExit(1); + } + } + Obj func = FUNC_LVARS(context); GAP_ASSERT(func); Stat call = STAT_LVARS(context); if (IsKernelFunction(func)) { Pr(" ", 0L, 0L); + /* HACK: close the output again */ + CloseOutput(); return 0; } Obj body = BODY_FUNC(func); if (call < OFFSET_FIRST_STAT || call > SIZE_BAG(body) - sizeof(StatHeader)) { Pr(" ", 0L, 0L); + /* HACK: close the output again */ + CloseOutput(); return 0; } @@ -186,6 +208,8 @@ Obj FuncPRINT_CURRENT_STATEMENT(Obj self, Obj context) Pr(" at %g:%d", (Int)filename, LINE_STAT(call)); } SWITCH_TO_OLD_LVARS(currLVars); + /* HACK: close the output again */ + CloseOutput(); return 0; } @@ -578,7 +602,7 @@ static StructGVarFunc GVarFuncs[] = { GVAR_FUNC(CALL_WITH_CATCH, 2, "func, args"), GVAR_FUNC(JUMP_TO_CATCH, 1, "payload"), - GVAR_FUNC(PRINT_CURRENT_STATEMENT, 1, "context"), + GVAR_FUNC(PRINT_CURRENT_STATEMENT, 2, "stream, context"), GVAR_FUNC(CURRENT_STATEMENT_LOCATION, 1, "context"), GVAR_FUNC(SetUserHasQuit, 1, "value"), diff --git a/tst/testinstall/kernel/gap.tst b/tst/testinstall/kernel/gap.tst index 8fbf66f3e6a..14148c6e26e 100644 --- a/tst/testinstall/kernel/gap.tst +++ b/tst/testinstall/kernel/gap.tst @@ -92,9 +92,9 @@ Error, usage: UpEnv( [ ] ) # gap> CURRENT_STATEMENT_LOCATION(GetCurrentLVars()); fail -gap> PRINT_CURRENT_STATEMENT(GetCurrentLVars()); -gap> f:=function() PRINT_CURRENT_STATEMENT(GetCurrentLVars()); Print("\n"); end;; f(); -PRINT_CURRENT_STATEMENT( GetCurrentLVars( ) ); at stream:1 +gap> PRINT_CURRENT_STATEMENT("*errout*", GetCurrentLVars()); +gap> f:=function() PRINT_CURRENT_STATEMENT("*errout*", GetCurrentLVars()); Print("\n"); end;; f(); +PRINT_CURRENT_STATEMENT( "*errout*", GetCurrentLVars( ) ); at stream:1 # gap> CALL_WITH_CATCH(fail,fail);