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