1 /****************************************************************************
2 **
3 **  This file is part of GAP, a system for computational discrete algebra.
4 **
5 **  Copyright of GAP belongs to its developers, whose names are too numerous
6 **  to list here. Please refer to the COPYRIGHT file for details.
7 **
8 **  SPDX-License-Identifier: GPL-2.0-or-later
9 **
10 **  This file implements functions for raising user errors and interacting
11 **  with the break loop.
12 **
13 */
14 
15 #include "error.h"
16 
17 #include "bool.h"
18 #include "code.h"
19 #include "exprs.h"
20 #include "funcs.h"
21 #include "gapstate.h"
22 #include "gaputils.h"
23 #include "io.h"
24 #include "lists.h"
25 #include "modules.h"
26 #include "plist.h"
27 #include "precord.h"
28 #include "records.h"
29 #include "stats.h"
30 #include "stringobj.h"
31 #include "vars.h"
32 
33 #ifdef HPCGAP
34 #include "hpc/thread.h"
35 #endif
36 
37 
38 static Obj ErrorInner;
39 static Obj ERROR_OUTPUT = NULL;
40 static Obj IsOutputStream;
41 
42 
43 /****************************************************************************
44 **
45 *F * * * * * * * * * * * * * * error functions * * * * * * * * * * * * * * *
46 */
47 
48 /****************************************************************************
49 **
50 *F  OpenErrorOutput()  . . . . . . . open the file or stream assigned to the
51 **                                   ERROR_OUTPUT global variable defined in
52 **                                   error.g, or "*errout*" otherwise
53 */
OpenErrorOutput(void)54 UInt OpenErrorOutput( void )
55 {
56     /* Try to print the output to stream. Use *errout* as a fallback. */
57     UInt ret = 0;
58 
59     if (ERROR_OUTPUT != NULL) {
60         if (IsStringConv(ERROR_OUTPUT)) {
61             ret = OpenOutput(CONST_CSTR_STRING(ERROR_OUTPUT));
62         }
63         else {
64             if (CALL_1ARGS(IsOutputStream, ERROR_OUTPUT) == True) {
65                 ret = OpenOutputStream(ERROR_OUTPUT);
66             }
67         }
68     }
69 
70     if (!ret) {
71         /* It may be we already tried and failed to open *errout* above but
72          * but this is an extreme case so it can't hurt to try again
73          * anyways */
74         ret = OpenOutput("*errout*");
75         if (ret) {
76             Pr("failed to open error stream\n", 0, 0);
77         }
78         else {
79             Panic("failed to open *errout*");
80         }
81     }
82 
83     return ret;
84 }
85 
86 
87 /****************************************************************************
88 **
89 *F  FuncDownEnv( <self>, <level> )  . . . . . . . . .  change the environment
90 */
91 
DownEnvInner(Int depth)92 static void DownEnvInner(Int depth)
93 {
94     /* if we are asked to go up ... */
95     if (depth < 0) {
96         /* ... we determine which level we are supposed to end up on ... */
97         depth = STATE(ErrorLLevel) + depth;
98         if (depth < 0) {
99             depth = 0;
100         }
101         /* ... then go back to the top, and later go down to the appropriate
102          * level. */
103         STATE(ErrorLVars) = STATE(BaseShellContext);
104         STATE(ErrorLLevel) = 0;
105         STATE(ShellContext) = STATE(BaseShellContext);
106     }
107 
108     /* now go down */
109     while (0 < depth && STATE(ErrorLVars) != STATE(BottomLVars) &&
110            PARENT_LVARS(STATE(ErrorLVars)) != STATE(BottomLVars)) {
111         STATE(ErrorLVars) = PARENT_LVARS(STATE(ErrorLVars));
112         STATE(ErrorLLevel)++;
113         STATE(ShellContext) = PARENT_LVARS(STATE(ShellContext));
114         depth--;
115     }
116 }
117 
FuncDownEnv(Obj self,Obj args)118 static Obj FuncDownEnv(Obj self, Obj args)
119 {
120     Int depth;
121 
122     if (LEN_PLIST(args) == 0) {
123         depth = 1;
124     }
125     else if (LEN_PLIST(args) == 1 && IS_INTOBJ(ELM_PLIST(args, 1))) {
126         depth = INT_INTOBJ(ELM_PLIST(args, 1));
127     }
128     else {
129         ErrorQuit("usage: DownEnv( [ <depth> ] )", 0L, 0L);
130     }
131     if (STATE(ErrorLVars) == STATE(BottomLVars)) {
132         Pr("not in any function\n", 0L, 0L);
133         return (Obj)0;
134     }
135 
136     DownEnvInner(depth);
137     return (Obj)0;
138 }
139 
FuncUpEnv(Obj self,Obj args)140 static Obj FuncUpEnv(Obj self, Obj args)
141 {
142     Int depth;
143     if (LEN_PLIST(args) == 0) {
144         depth = 1;
145     }
146     else if (LEN_PLIST(args) == 1 && IS_INTOBJ(ELM_PLIST(args, 1))) {
147         depth = INT_INTOBJ(ELM_PLIST(args, 1));
148     }
149     else {
150         ErrorQuit("usage: UpEnv( [ <depth> ] )", 0L, 0L);
151     }
152     if (STATE(ErrorLVars) == STATE(BottomLVars)) {
153         Pr("not in any function\n", 0L, 0L);
154         return (Obj)0;
155     }
156 
157     DownEnvInner(-depth);
158     return (Obj)0;
159 }
160 
FuncCURRENT_STATEMENT_LOCATION(Obj self,Obj context)161 static Obj FuncCURRENT_STATEMENT_LOCATION(Obj self, Obj context)
162 {
163     if (context == STATE(BottomLVars))
164         return Fail;
165 
166     Obj func = FUNC_LVARS(context);
167     GAP_ASSERT(func);
168     Stat call = STAT_LVARS(context);
169     if (IsKernelFunction(func)) {
170         return Fail;
171     }
172     Obj body = BODY_FUNC(func);
173     if (call < OFFSET_FIRST_STAT ||
174         call > SIZE_BAG(body) - sizeof(StatHeader)) {
175         return Fail;
176     }
177 
178     Obj currLVars = STATE(CurrLVars);
179     SWITCH_TO_OLD_LVARS(context);
180     GAP_ASSERT(call == BRK_CALL_TO());
181 
182     Obj retlist = Fail;
183     Int type = TNUM_STAT(call);
184     if ((FIRST_STAT_TNUM <= type && type <= LAST_STAT_TNUM) ||
185         (FIRST_EXPR_TNUM <= type && type <= LAST_EXPR_TNUM)) {
186         Int line = LINE_STAT(call);
187         Obj filename = GET_FILENAME_BODY(body);
188         retlist = NewPlistFromArgs(filename, INTOBJ_INT(line));
189     }
190     SWITCH_TO_OLD_LVARS(currLVars);
191     return retlist;
192 }
193 
FuncPRINT_CURRENT_STATEMENT(Obj self,Obj stream,Obj context)194 static Obj FuncPRINT_CURRENT_STATEMENT(Obj self, Obj stream, Obj context)
195 {
196     if (context == STATE(BottomLVars))
197         return 0;
198 
199     /* HACK: we want to redirect output */
200     /* Try to print the output to stream. Use *errout* as a fallback. */
201     if ((IsStringConv(stream) && !OpenOutput(CONST_CSTR_STRING(stream))) ||
202         (!IS_STRING(stream) && !OpenOutputStream(stream))) {
203         if (OpenOutput("*errout*")) {
204             Pr("PRINT_CURRENT_STATEMENT: failed to open error stream\n", 0, 0);
205         }
206         else {
207             Panic("failed to open *errout*");
208         }
209     }
210 
211     Obj func = FUNC_LVARS(context);
212     GAP_ASSERT(func);
213     Stat call = STAT_LVARS(context);
214     Obj  body = BODY_FUNC(func);
215     if (IsKernelFunction(func)) {
216         PrintKernelFunction(func);
217         Obj funcname = NAME_FUNC(func);
218         if (funcname) {
219             Pr(" in function %g", (Int)funcname, 0);
220         }
221     }
222     else if (call < OFFSET_FIRST_STAT ||
223              call > SIZE_BAG(body) - sizeof(StatHeader)) {
224         Pr("<corrupted statement> ", 0L, 0L);
225     }
226     else {
227         Obj currLVars = STATE(CurrLVars);
228         SWITCH_TO_OLD_LVARS(context);
229         GAP_ASSERT(call == BRK_CALL_TO());
230 
231         Int type = TNUM_STAT(call);
232         Obj filename = GET_FILENAME_BODY(body);
233         if (FIRST_STAT_TNUM <= type && type <= LAST_STAT_TNUM) {
234             PrintStat(call);
235             Pr(" at %g:%d", (Int)filename, LINE_STAT(call));
236         }
237         else if (FIRST_EXPR_TNUM <= type && type <= LAST_EXPR_TNUM) {
238             PrintExpr(call);
239             Pr(" at %g:%d", (Int)filename, LINE_STAT(call));
240         }
241         SWITCH_TO_OLD_LVARS(currLVars);
242     }
243 
244     /* HACK: close the output again */
245     CloseOutput();
246     return 0;
247 }
248 
249 /****************************************************************************
250 **
251 *F  FuncCALL_WITH_CATCH( <self>, <func> )
252 **
253 */
FuncCALL_WITH_CATCH(Obj self,Obj func,Obj args)254 static Obj FuncCALL_WITH_CATCH(Obj self, Obj func, Obj args)
255 {
256     return CALL_WITH_CATCH(func, args);
257 }
258 
CALL_WITH_CATCH(Obj func,volatile Obj args)259 Obj CALL_WITH_CATCH(Obj func, volatile Obj args)
260 {
261     volatile syJmp_buf readJmpError;
262     volatile Obj       res;
263     volatile Obj       currLVars;
264     volatile Obj       tilde;
265     volatile Int       recursionDepth;
266 
267     RequireFunction("CALL_WITH_CATCH", func);
268     if (!IS_LIST(args))
269         RequireArgument("CALL_WITH_CATCH", args, "must be a list");
270 #ifdef HPCGAP
271     if (!IS_PLIST(args)) {
272         args = SHALLOW_COPY_OBJ(args);
273         PLAIN_LIST(args);
274     }
275 #endif
276 
277     memcpy((void *)&readJmpError, (void *)&STATE(ReadJmpError),
278            sizeof(syJmp_buf));
279     currLVars = STATE(CurrLVars);
280 #ifdef GAP_KERNEL_DEBUG
281     volatile Stat currStat = BRK_CALL_TO();
282 #endif
283     recursionDepth = GetRecursionDepth();
284     tilde = STATE(Tilde);
285     res = NEW_PLIST_IMM(T_PLIST_DENSE, 2);
286 #ifdef HPCGAP
287     int      lockSP = RegionLockSP();
288     Region * savedRegion = TLS(currentRegion);
289 #endif
290     if (sySetjmp(STATE(ReadJmpError))) {
291         SET_LEN_PLIST(res, 2);
292         SET_ELM_PLIST(res, 1, False);
293         SET_ELM_PLIST(res, 2, STATE(ThrownObject));
294         CHANGED_BAG(res);
295         STATE(ThrownObject) = 0;
296         SWITCH_TO_OLD_LVARS(currLVars);
297         GAP_ASSERT(currStat == BRK_CALL_TO());
298         SetRecursionDepth(recursionDepth);
299         STATE(Tilde) = tilde;
300 #ifdef HPCGAP
301         PopRegionLocks(lockSP);
302         TLS(currentRegion) = savedRegion;
303         if (TLS(CurrentHashLock))
304             HashUnlock(TLS(CurrentHashLock));
305 #endif
306     }
307     else {
308         Obj result = CallFuncList(func, args);
309         // Make an explicit check if an interrupt occurred
310         // in case func was a kernel function.
311         TakeInterrupt();
312 #ifdef HPCGAP
313         /* There should be no locks to pop off the stack, but better safe than
314          * sorry. */
315         PopRegionLocks(lockSP);
316         TLS(currentRegion) = savedRegion;
317 #endif
318         SET_ELM_PLIST(res, 1, True);
319         if (result) {
320             SET_LEN_PLIST(res, 2);
321             SET_ELM_PLIST(res, 2, result);
322             CHANGED_BAG(res);
323         }
324         else
325             SET_LEN_PLIST(res, 1);
326     }
327     memcpy((void *)&STATE(ReadJmpError), (void *)&readJmpError,
328            sizeof(syJmp_buf));
329     return res;
330 }
331 
FuncJUMP_TO_CATCH(Obj self,Obj payload)332 static Obj FuncJUMP_TO_CATCH(Obj self, Obj payload)
333 {
334     STATE(ThrownObject) = payload;
335     if (STATE(JumpToCatchCallback) != 0) {
336         (*STATE(JumpToCatchCallback))();
337     }
338     syLongjmp(&(STATE(ReadJmpError)), 1);
339     return 0;
340 }
341 
FuncSetUserHasQuit(Obj Self,Obj value)342 static Obj FuncSetUserHasQuit(Obj Self, Obj value)
343 {
344     STATE(UserHasQuit) = INT_INTOBJ(value);
345     if (STATE(UserHasQuit))
346         SetRecursionDepth(0);
347     return 0;
348 }
349 
350 
351 /****************************************************************************
352 **
353 *F RegisterBreakloopObserver( <func> )
354 **
355 ** Register a function which will be called when the break loop is entered
356 ** and left. Function should take a single Int argument which will be 1 when
357 ** break loop is entered, 0 when leaving.
358 **
359 ** Note that it is also possible to leave the break loop (or any GAP code)
360 ** by longjmping. This should be tracked with RegisterSyLongjmpObserver.
361 */
362 
363 static intfunc signalBreakFuncList[16];
364 
RegisterBreakloopObserver(intfunc func)365 Int RegisterBreakloopObserver(intfunc func)
366 {
367     Int i;
368     for (i = 0; i < ARRAY_SIZE(signalBreakFuncList); ++i) {
369         if (signalBreakFuncList[i] == 0) {
370             signalBreakFuncList[i] = func;
371             return 1;
372         }
373     }
374     return 0;
375 }
376 
377 /****************************************************************************
378 **
379 *F  ErrorMessageToGAPString( <msg>, <arg1>, <arg2> )
380 */
381 
ErrorMessageToGAPString(const Char * msg,Int arg1,Int arg2)382 static Obj ErrorMessageToGAPString(const Char * msg, Int arg1, Int arg2)
383 {
384     Char message[1024];
385     Obj  Message;
386     SPrTo(message, sizeof(message), msg, arg1, arg2);
387     message[sizeof(message) - 1] = '\0';
388     Message = MakeString(message);
389     return Message;
390 }
391 
392 
CallErrorInner(const Char * msg,Int arg1,Int arg2,UInt justQuit,UInt mayReturnVoid,UInt mayReturnObj,Obj lateMessage,UInt printThisStatement)393 static Obj CallErrorInner(const Char * msg,
394                           Int          arg1,
395                           Int          arg2,
396                           UInt         justQuit,
397                           UInt         mayReturnVoid,
398                           UInt         mayReturnObj,
399                           Obj          lateMessage,
400                           UInt         printThisStatement)
401 {
402     // Must do this before creating any other GAP objects,
403     // as one of the args could be a pointer into a Bag.
404     Obj EarlyMsg = ErrorMessageToGAPString(msg, arg1, arg2);
405 
406     Obj r = NEW_PREC(0);
407     Obj l;
408     Int i;
409 
410 #ifdef HPCGAP
411     Region * savedRegion = TLS(currentRegion);
412     TLS(currentRegion) = TLS(threadRegion);
413 #endif
414     AssPRec(r, RNamName("context"), STATE(CurrLVars));
415     AssPRec(r, RNamName("justQuit"), justQuit ? True : False);
416     AssPRec(r, RNamName("mayReturnObj"), mayReturnObj ? True : False);
417     AssPRec(r, RNamName("mayReturnVoid"), mayReturnVoid ? True : False);
418     AssPRec(r, RNamName("printThisStatement"),
419             printThisStatement ? True : False);
420     AssPRec(r, RNamName("lateMessage"), lateMessage);
421     l = NEW_PLIST_IMM(T_PLIST_HOM, 1);
422     SET_ELM_PLIST(l, 1, EarlyMsg);
423     SET_LEN_PLIST(l, 1);
424 
425     // Signal functions about entering and leaving break loop
426     for (i = 0; i < ARRAY_SIZE(signalBreakFuncList) && signalBreakFuncList[i];
427          ++i)
428         (signalBreakFuncList[i])(1);
429     Obj res = CALL_2ARGS(ErrorInner, r, l);
430     for (i = 0; i < ARRAY_SIZE(signalBreakFuncList) && signalBreakFuncList[i];
431          ++i)
432         (signalBreakFuncList[i])(0);
433 #ifdef HPCGAP
434     TLS(currentRegion) = savedRegion;
435 #endif
436     return res;
437 }
438 
ErrorQuit(const Char * msg,Int arg1,Int arg2)439 void ErrorQuit(const Char * msg, Int arg1, Int arg2)
440 {
441     CallErrorInner(msg, arg1, arg2, 1, 0, 0, False, 1);
442     Panic("ErrorQuit must not return");
443 }
444 
445 
446 /****************************************************************************
447 **
448 *F  ErrorMayQuitNrArgs( <narg>, <actual> ) . . . .  wrong number of arguments
449 */
ErrorMayQuitNrArgs(Int narg,Int actual)450 void ErrorMayQuitNrArgs(Int narg, Int actual)
451 {
452     ErrorMayQuit("Function: number of arguments must be %d (not %d)",
453                  narg, actual);
454 }
455 
456 /****************************************************************************
457 **
458 *F  ErrorMayQuitNrAtLeastArgs( <narg>, <actual> ) . . .  not enough arguments
459 */
ErrorMayQuitNrAtLeastArgs(Int narg,Int actual)460 void ErrorMayQuitNrAtLeastArgs(Int narg, Int actual)
461 {
462     ErrorMayQuit(
463         "Function: number of arguments must be at least %d (not %d)",
464         narg, actual);
465 }
466 
467 /****************************************************************************
468 **
469 *F  ErrorQuitRange3( <first>, <second>, <last> ) . . divisibility
470 */
ErrorQuitRange3(Obj first,Obj second,Obj last)471 void ErrorQuitRange3(Obj first, Obj second, Obj last)
472 {
473     ErrorQuit("Range expression <last>-<first> must be divisible by "
474               "<second>-<first>, not %d %d",
475               INT_INTOBJ(last) - INT_INTOBJ(first),
476               INT_INTOBJ(second) - INT_INTOBJ(first));
477 }
478 
479 
480 /****************************************************************************
481 **
482 *F  ErrorReturnObj( <msg>, <arg1>, <arg2>, <msg2> ) . .  print and return obj
483 */
ErrorReturnObj(const Char * msg,Int arg1,Int arg2,const Char * msg2)484 Obj ErrorReturnObj(const Char * msg, Int arg1, Int arg2, const Char * msg2)
485 {
486     Obj LateMsg;
487     LateMsg = MakeString(msg2);
488     return CallErrorInner(msg, arg1, arg2, 0, 0, 1, LateMsg, 1);
489 }
490 
491 
492 /****************************************************************************
493 **
494 *F  ErrorReturnVoid( <msg>, <arg1>, <arg2>, <msg2> )  . . .  print and return
495 */
ErrorReturnVoid(const Char * msg,Int arg1,Int arg2,const Char * msg2)496 void ErrorReturnVoid(const Char * msg, Int arg1, Int arg2, const Char * msg2)
497 {
498     Obj LateMsg;
499     LateMsg = MakeString(msg2);
500     CallErrorInner(msg, arg1, arg2, 0, 1, 0, LateMsg, 1);
501     /*    ErrorMode( msg, arg1, arg2, (Obj)0, msg2, 'x' ); */
502 }
503 
504 /****************************************************************************
505 **
506 *F  ErrorMayQuit( <msg>, <arg1>, <arg2> )  . . .  print and return
507 */
ErrorMayQuit(const Char * msg,Int arg1,Int arg2)508 void ErrorMayQuit(const Char * msg, Int arg1, Int arg2)
509 {
510     Obj LateMsg = MakeString("type 'quit;' to quit to outer loop");
511     CallErrorInner(msg, arg1, arg2, 0, 0, 0, LateMsg, 1);
512     Panic("ErrorMayQuit must not return");
513 }
514 
515 /****************************************************************************
516 **
517 *F  CheckIsPossList( <desc>, <poss> ) . . . . . . . . . . check for poss list
518 */
CheckIsPossList(const Char * desc,Obj poss)519 void CheckIsPossList(const Char * desc, Obj poss)
520 {
521     if ( ! IS_POSS_LIST( poss ) ) {
522         ErrorMayQuit("%s: <poss> must be a dense list of positive integers",
523             (Int)desc, 0 );
524     }
525 }
526 
527 /****************************************************************************
528 **
529 *F  CheckIsDenseList( <desc>, <listName>, <list> ) . . . check for dense list
530 */
CheckIsDenseList(const Char * desc,const Char * listName,Obj list)531 void CheckIsDenseList(const Char * desc, const Char * listName, Obj list)
532 {
533     if (!IS_DENSE_LIST(list)) {
534         ErrorMayQuit("%s: <%s> must be a dense list", (Int)desc, (Int)listName);
535     }
536 }
537 
538 /****************************************************************************
539 **
540 *F  CheckSameLength
541 */
CheckSameLength(const Char * desc,const Char * name1,const Char * name2,Obj op1,Obj op2)542 void CheckSameLength(const Char * desc,
543                      const Char * name1,
544                      const Char * name2,
545                      Obj          op1,
546                      Obj          op2)
547 {
548     UInt len1 = LEN_LIST(op1);
549     UInt len2 = LEN_LIST(op2);
550     if (len1 != len2) {
551         Char message[1024];
552         snprintf(message, sizeof(message),
553                  "%s: <%s> must have the same length as <%s> "
554                  "(lengths are %d and %d)",
555                  desc, name1, name2, (int)len1, (int)len2);
556         ErrorMayQuit(message, 0, 0);
557     }
558 }
559 
560 /****************************************************************************
561 **
562 *F  RequireArgumentEx
563 */
RequireArgumentEx(const char * funcname,Obj op,const char * argname,const char * msg)564 Obj RequireArgumentEx(const char * funcname,
565                       Obj          op,
566                       const char * argname,
567                       const char * msg)
568 {
569     char msgbuf[1024] = { 0 };
570     Int  arg1 = 0;
571     Int  arg2 = 0;
572 
573     if (funcname) {
574         strlcat(msgbuf, funcname, sizeof(msgbuf));
575         strlcat(msgbuf, ": ", sizeof(msgbuf));
576     }
577     if (argname) {
578         strlcat(msgbuf, argname, sizeof(msgbuf));
579         strlcat(msgbuf, " ", sizeof(msgbuf));
580     }
581     strlcat(msgbuf, msg, sizeof(msgbuf));
582     if (IS_INTOBJ(op)) {
583         strlcat(msgbuf, " (not the integer %d)", sizeof(msgbuf));
584         arg1 = INT_INTOBJ(op);
585     }
586     else if (op == True)
587         strlcat(msgbuf, " (not the value 'true')", sizeof(msgbuf));
588     else if (op == False)
589         strlcat(msgbuf, " (not the value 'false')", sizeof(msgbuf));
590     else if (op == Fail)
591         strlcat(msgbuf, " (not the value 'fail')", sizeof(msgbuf));
592     else {
593         strlcat(msgbuf, " (not a %s)", sizeof(msgbuf));
594         arg1 = (Int)TNAM_OBJ(op);
595     }
596 
597     ErrorMayQuit(msgbuf, arg1, arg2);
598 }
599 
AssertionFailure(void)600 void AssertionFailure(void)
601 {
602     ErrorReturnVoid("Assertion failure", 0, 0, "you may 'return;'");
603 }
604 
605 
606 /****************************************************************************
607 **
608 *V  GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
609 */
610 static StructGVarFunc GVarFuncs[] = {
611 
612     GVAR_FUNC(DownEnv, -1, "args"),
613     GVAR_FUNC(UpEnv, -1, "args"),
614 
615     GVAR_FUNC(CALL_WITH_CATCH, 2, "func, args"),
616     GVAR_FUNC(JUMP_TO_CATCH, 1, "payload"),
617 
618     GVAR_FUNC(PRINT_CURRENT_STATEMENT, 2, "stream, context"),
619     GVAR_FUNC(CURRENT_STATEMENT_LOCATION, 1, "context"),
620 
621     GVAR_FUNC(SetUserHasQuit, 1, "value"),
622 
623     { 0, 0, 0, 0, 0 }
624 
625 };
626 
627 
628 /****************************************************************************
629 **
630 *F  InitKernel( <module> )  . . . . . . . . initialise kernel data structures
631 */
InitKernel(StructInitInfo * module)632 static Int InitKernel(StructInitInfo * module)
633 {
634     // init filters and functions
635     InitHdlrFuncsFromTable(GVarFuncs);
636 
637     ImportFuncFromLibrary("ErrorInner", &ErrorInner);
638     ImportFuncFromLibrary("IsOutputStream", &IsOutputStream);
639     ImportGVarFromLibrary("ERROR_OUTPUT", &ERROR_OUTPUT);
640 
641     // return success
642     return 0;
643 }
644 
645 
646 /****************************************************************************
647 **
648 *F  InitLibrary( <module> ) . . . . . . .  initialise library data structures
649 */
InitLibrary(StructInitInfo * module)650 static Int InitLibrary(StructInitInfo * module)
651 {
652     // init filters and functions
653     InitGVarFuncsFromTable(GVarFuncs);
654 
655     // return success
656     return 0;
657 }
658 
659 
660 /****************************************************************************
661 **
662 *F  InitInfoError() . . . . . . . . . . . . . . . . . table of init functions
663 */
664 static StructInitInfo module = {
665     // init struct using C99 designated initializers; for a full list of
666     // fields, please refer to the definition of StructInitInfo
667     .type = MODULE_BUILTIN,
668     .name = "error",
669     .initKernel = InitKernel,
670     .initLibrary = InitLibrary,
671 };
672 
InitInfoError(void)673 StructInitInfo * InitInfoError(void)
674 {
675     return &module;
676 }
677