0022627: Change OCCT memory management defaults
[occt.git] / src / OSD / OSD_signal.cxx
CommitLineData
7fd59977 1
2#include <OSD.ixx>
3
4#ifndef WNT
5
6//---------- All Systems except Windows NT : ----------------------------------
7
8#ifdef HAVE_CONFIG_H
9# include <config.h>
10#endif
11
12# include <stdio.h>
13
14#include <OSD_WhoAmI.hxx>
15#include <OSD_SIGHUP.hxx>
16#include <OSD_SIGINT.hxx>
17#include <OSD_SIGQUIT.hxx>
18#include <OSD_SIGILL.hxx>
19#include <OSD_SIGKILL.hxx>
20#include <OSD_SIGBUS.hxx>
21#include <OSD_SIGSEGV.hxx>
22#include <OSD_SIGSYS.hxx>
23#include <Standard_NumericError.hxx>
24#include <Standard_NullObject.hxx>
25#include <Standard_DivideByZero.hxx>
26#include <Standard_Overflow.hxx>
27
28#include <Standard_ErrorHandler.hxx>
29
30// POSIX threads
31#ifdef HAVE_PTHREAD_H
32 #include <pthread.h>
33#endif
34
35#ifdef HAVE_PTHREAD_H
36static pthread_t getOCCThread () {
37 static pthread_t TheOCCThread = 0;
38 return TheOCCThread ;
39}
40#endif
41
42#ifdef linux
43#include <fenv.h>
44#include <fpu_control.h>
45static Standard_Boolean fFltExceptions = Standard_False;
46#endif
47
48//const OSD_WhoAmI Iam = OSD_WPackage;
49
50typedef void (ACT_SIGIO_HANDLER)(void) ;
51ACT_SIGIO_HANDLER *ADR_ACT_SIGIO_HANDLER = NULL ;
52
53#if defined(HAVE_FLOATINGPOINT_H) && defined(HAVE_SYS_MACHSIG_H)
54# include <floatingpoint.h>
55# include <sys/machsig.h>
56// JPT : Difference between SUN/SUNOS and SUN/SOLARIS
57# define FPE_FLTDIV_TRAP FPE_FLTDIV
58# define FPE_INTDIV_TRAP FPE_INTDIV
59# define FPE_FLTOVF_TRAP FPE_FLTOVF
60# define FPE_INTOVF_TRAP FPE_INTOVF
61# define FPE_FLTUND_TRAP FPE_FLTUND
62
63#define FPE_FLTRES_TRAP FPE_FLTRES /* floating point inexact result */
64#define FPE_FLTINV_TRAP FPE_FLTINV /* invalid floating point operation */
65#define FPE_FLTSUB_TRAP FPE_FLTSUB /* subscript out of range */
66
67extern "C" {int ieee_handler(char *,char *, sigfpe_handler_type&);}
68# include <stdlib.h>
69#endif
70
71#ifdef DECOSF1
72typedef void (* SIG_PFV) (int);
73#endif
74
75#if defined(HAVE_SIGFPE_H) && defined(HAVE_SYS_SIGINFO_H)
76# include <sigfpe.h>
77# include <sys/siginfo.h>
78# define FPE_FLTDIV_TRAP FPE_FLTDIV
79# define FPE_INTDIV_TRAP FPE_INTDIV
80# define FPE_FLTOVF_TRAP FPE_FLTOVF
81# define FPE_INTOVF_TRAP FPE_INTOVF
82# define FPE_FLTUND_TRAP FPE_FLTUND
83#endif
84
85#ifdef __GNUC__
86# include <stdlib.h>
87# include <stdio.h>
88#else
89# ifdef SA_SIGINFO
90# ifndef _AIX
91# include <sys/siginfo.h>
92# endif
93# endif
94#endif
95typedef void (* SIG_PFV) (int);
96
97#ifdef HAVE_SIGNAL_H
98# include <signal.h>
99#endif
100
101#ifdef HAVE_SYS_SIGNAL_H
102# include <sys/signal.h>
103#endif
104
105#if defined(HAVE_PTHREAD_H) && defined(NO_CXX_EXCEPTION)
106//============================================================================
107//==== GetOldSigAction
108//==== get previous
109//============================================================================
110
111static struct sigaction *GetOldSigAction()
112{
113 static struct sigaction oldSignals[NSIG];
114 return oldSignals;
115}
116
117#ifdef SOLARIS
118static sigfpe_handler_type *GetOldFPE()
119{
120 static sigfpe_handler_type aIEEEHandler[5] = { NULL, NULL, NULL, NULL, NULL } ;
121 return aIEEEHandler;
122}
123#endif
124#endif
125
126
127//============================================================================
128//==== SetSignal
129//==== Set the differents signals:
130//============================================================================
131
132void OSD::SetSignal(const Standard_Boolean aFloatingSignal)
133{
134 static int first_time = 3 ;
135 struct sigaction act, oact;
136 int stat = 0;
137
138 if( aFloatingSignal ) {
139 //==== Enable the floating point exceptions ===============
140#if defined (__sun) || defined (SOLARIS)
141 sigfpe_handler_type PHandler = (sigfpe_handler_type) Handler ;
142 stat = ieee_handler("set", "invalid", PHandler);
143 stat = ieee_handler("set", "division", PHandler) || stat;
144 stat = ieee_handler("set", "overflow", PHandler) || stat;
145 //stat = ieee_handler("set", "underflow", PHandler) || stat;
146 //stat = ieee_handler("set", "inexact", PHandler) || stat;
147 if (stat) {
148 cerr << "ieee_handler does not work !!! KO " << endl;
149 }
150#elif defined (linux)
151 feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
152 //feenableexcept (FE_INVALID | FE_DIVBYZERO);
153 fFltExceptions = Standard_True;
154#endif
155 }
156 else if ( first_time & 1 ) {
157// cout << "SetSignal( Standard_False ) is not implemented..." << endl ;
158 first_time = first_time & (~ 1) ;
159 }
160
161#if defined (sgi) || defined (IRIX )
162 if ( first_time & 2 ) {
163 char *TRAP_FPE = getenv("TRAP_FPE") ;
164 if ( TRAP_FPE == NULL ) {
165 cout << "On SGI you must set TRAP_FPE environment variable : " << endl ;
166 cout << "set env(TRAP_FPE) \"UNDERFL=FLUSH_ZERO;OVERFL=DEFAULT;DIVZERO=DEFAULT;INT_OVERFL=DEFAULT\" or" << endl ;
167 cout << "setenv TRAP_FPE \"UNDERFL=FLUSH_ZERO;OVERFL=DEFAULT;DIVZERO=DEFAULT;INT_OVERFL=DEFAULT\"" << endl ;
168// exit(1) ;
169 first_time = first_time & (~ 2) ;
170 }
171 }
172#endif
173
174 //==== Save the old Signal Handler, and set the new one ===================
175
176 sigemptyset(&act.sa_mask) ;
177
178#ifdef SA_RESTART
179 act.sa_flags = SA_RESTART ;
180#else
181 act.sa_flags = 0 ;
182#endif
183#ifdef SA_SIGINFO
184 act.sa_flags = act.sa_flags | SA_SIGINFO ;
185 act.sa_sigaction = (void(*)(int, siginfo_t *, void*)) &Handler ;
186#else
187 act.sa_handler = (SIG_PFV) &Handler ;
188#endif
189
190 //==== Always detected the signal "SIGFPE" =================================
191 stat = sigaction(SIGFPE,&act,&oact); // ...... floating point exception
192 if (stat) {
193 cerr << "sigaction does not work !!! KO " << endl;
194 perror("sigaction ");
195 }
196
197 //==== Detected the only the "free" signals ================================
198 sigaction(SIGHUP,&act,&oact); // ...... hangup
199
200#ifdef OBJS
201 if(oact.sa_handler)
202 sigaction(SIGHUP,&oact,&oact);
203#endif
204
205 sigaction(SIGINT,&act,&oact); // ...... interrupt
206
207#ifdef OBJS
208 if(oact.sa_handler)
209 sigaction(SIGINT,&oact,&oact);
210#endif
211
212 sigaction(SIGQUIT,&act,&oact); // ...... quit
213
214#ifdef OBJS
215 if(oact.sa_handler)
216 sigaction(SIGQUIT,&oact,&oact);
217#endif
218
219 sigaction(SIGILL,&act,&oact); // ...... illegal instruction
220
221#ifdef OBJS
222 if(oact.sa_handler)
223 sigaction(SIGILL,&oact,&oact);
224#endif
225
226 sigaction(SIGBUS,&act,&oact); // ...... bus error
227
228#ifdef OBJS
229 if(oact.sa_handler)
230 sigaction(SIGBUS,&oact,&oact);
231#endif
232
233#if (!defined (linux)) && (!defined(LININTEL))
234 sigaction(SIGSYS,&act,&oact); // ...... bad argument to system call
235
236# ifdef OBJS
237 if(oact.sa_handler)
238 sigaction(SIGSYS,&oact,&oact);
239# endif
240#endif
241
242#if defined (__sgi) || defined(IRIX)
243 sigaction(SIGTRAP,&act,&oact); // Integer Divide By Zero (IRIX)
244
245# ifdef OBJS
246 if(oact.sa_handler)
247 sigaction(SIGTRAP,&oact,&oact);
248# endif
249#endif
250
251#ifdef SA_SIGINFO
252 act.sa_sigaction = (void(*)(int, siginfo_t *, void*)) &SegvHandler ;
253#else
254 act.sa_handler = (SIG_PFV) &SegvHandler ;
255#endif
256
257 if ( sigaction( SIGSEGV , &act , &oact ) ) // ...... segmentation violation
258 perror("OSD::SetSignal sigaction( SIGSEGV , &act , &oact ) ") ;
259
260#ifdef OBJS
261 if(oact.sa_handler)
262 sigaction(SIGSEGV,&oact,&oact);
263#endif
264#if defined(__osf__) || defined(DECOSF1)
265 struct sigaction action, prev_action;
266 action.sa_handler = SIG_IGN;
267 action.sa_mask = 0;
268 action.sa_flags = 0;
269
270 if (sigaction (SIGFPE, &action, &prev_action) == -1) {
271 perror ("sigaction");
272 exit (1);
273 }
274#endif
275
276}
277//============================================================================
278//==== Handler
279//==== Catche the differents signals:
280//==== 1- The Fatal signals, which cause the end of process:
281//==== 2- The exceptions which are "signaled" by Raise.
282//==== The Fatal Signals:
283//==== SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGKILL, SIGBUS, SIGSYS
284//==== The Exceptions:
285//==== SIGFPE
286//==== (SUN versions)
287//==== FPE_INTOVF_TRAP // ..... integer overflow
288//==== FPE_INTDIV_TRAP // ..... integer divide by zero
289//==== FPE_FLTINEX_TRAP // ..... [floating inexact result]
290//==== FPE_FLTDIV_TRAP // ..... [floating divide by zero]
291//==== FPE_FLTUND_TRAP // ..... [floating underflow]
292//==== FPE_FLTOPER_TRAP // ..... [floating inexact result]
293//==== FPE_FLTOVF_TRAP // ..... [floating overflow]
294//==== SIGSEGV is handled by "SegvHandler()"
295//============================================================================
296
297
298void OSD::Handler(const OSD_Signals theSignal,
299 const Standard_Address
300#ifdef SA_SIGINFO
301 theSigInfo
302#endif
303 ,const Standard_Address
304#if defined(HAVE_PTHREAD_H) && defined(NO_CXX_EXCEPTION)
305 theContext
306#endif
307 )
308{
309 struct sigaction oldact, act;
310
311 // re-install the signal
312
313 if ( ! sigaction (theSignal, NULL, &oldact) ) {
314 // cout << " signal is " << theSignal << " handler is " << oldact.sa_handler << endl;
315 if (sigaction (theSignal, &oldact, &act)) perror ("sigaction");
316 }
317 else
318 perror ("sigaction");
319
320 siginfo_t * aSigInfo = NULL;
321#ifdef SA_SIGINFO
322 aSigInfo = (siginfo_t *) theSigInfo;
323#endif
324
325#if defined(HAVE_PTHREAD_H) && defined(NO_CXX_EXCEPTION)
326
327//#ifdef DEB
328// cout << " current thread " << pthread_self() << endl;
329//#endif
330
331
332 if (pthread_self() != getOCCThread() || !Standard_ErrorHandler::IsInTryBlock()) {
333 // use the previous signal handler
334 // cout << "OSD::Handler: signal " << (int) theSignal << " occured outside a try block " << endl ;
335
336 struct sigaction *oldSignals = GetOldSigAction();
337 struct sigaction asigacthandler = oldSignals[(int) theSignal];
338
339 if (asigacthandler.sa_flags & SA_SIGINFO) {
340 void (*aCurInfoHandle)(int, siginfo_t *, void *) = asigacthandler.sa_sigaction;
341 if (aSigInfo) {
342 switch (aSigInfo->si_signo) {
343 case SIGFPE:
344 {
345#ifdef SOLARIS
346 sigfpe_handler_type *aIEEEHandlerTab = GetOldFPE();
347 sigfpe_handler_type aIEEEHandler = NULL;
348
349 switch (aSigInfo->si_code) {
350 case FPE_INTDIV_TRAP :
351 case FPE_FLTDIV_TRAP :
352 aIEEEHandler = aIEEEHandlerTab[1];
353 break;
354 case FPE_INTOVF_TRAP :
355 case FPE_FLTOVF_TRAP :
356 aIEEEHandler = aIEEEHandlerTab[2];
357 break;
358 case FPE_FLTUND_TRAP :
359 aIEEEHandler = aIEEEHandlerTab[4];
360 break;
361 case FPE_FLTRES_TRAP:
362 aIEEEHandler = aIEEEHandlerTab[3];
363 break;
364 case FPE_FLTINV_TRAP :
365 aIEEEHandler = aIEEEHandlerTab[0];
366 break;
367 case FPE_FLTSUB_TRAP :
368 default:
369 break;
370 }
371 if (aIEEEHandler) {
372 // cout << "OSD::Handler: calling previous IEEE signal handler with info" << endl ;
373 void (*aFPEHandler)(int, siginfo_t *, void *) = (void(*)(int, siginfo*, void*)) aIEEEHandler;
374 (*aFPEHandler) (theSignal, aSigInfo, theContext);
375 return;
376 }
377#endif
378 }
379 break;
380 case SIGSEGV:
381 switch (aSigInfo->si_code) {
382 case SEGV_MAPERR:
383 // cout << "OSD::Handler: SIGSEGV signal : address not mapped to object";
384 break;
385 case SEGV_ACCERR:
386 // cout << "OSD::Handler: SIGSEGV signal : invalid permissions for mapped object";
387 break;
388 default:
389 // cout << "OSD::Handler: SIGSEGV signal : unknown segv";
390 break;
391 }
392 // cout << " at address " << (void *) aSigInfo->si_addr << endl;
393 break;
394 case SIGBUS:
395 switch (aSigInfo->si_code) {
396 case BUS_ADRALN:
397 // cout << "OSD::Handler: SIGBUS signal : invalid address alignment";
398 break;
399 case BUS_ADRERR:
400 // cout << "OSD::Handler: SIGBUS signal : non-existent physical address";
401 break;
402 case BUS_OBJERR:
403 // cout << "OSD::Handler: SIGBUS signal : object specific hardware error";
404 break;
405 default:
406 // cout << "OSD::Handler: SIGBUS signal : unknown sig bus";
407 break;
408 }
409 // cout << " at " << (void *) aSigInfo->si_addr << endl;
410 break;
411 case SIGILL:
412 // cout << "OSD::Handler: illegal instruction signal " << endl;
413 break;
414#ifdef SIGSYS
415 case SIGSYS:
416 // cout << "OSD::Handler: bad argument to system call signal"<< endl ;
417 break;
418#endif
419 case SIGINT:
420 // cout << "OSD::Handler: interrupt signal" << endl;
421 break;
422 default:
423 break;
424 }
425 }
426 if (aCurInfoHandle) {
427 // cout << "OSD::Handler: calling previous signal handler with info " << aCurInfoHandle << endl ;
428 (*aCurInfoHandle) (theSignal, aSigInfo, theContext);
429 cerr << " previous signal handler return" << endl ;
430 return;
431 }
432 else {
433 // cout << "OSD::Handler: no handler with info for the signal" << endl;
434 }
435 } else {
436 // no siginfi needed for the signal
437 void (*aCurHandler) (int) = asigacthandler.sa_handler;
438 if(aCurHandler) {
439 // cout << "OSD::Handler: calling previous signal handler" << endl ;
440 (*aCurHandler) (theSignal);
441 cerr << " previous signal handler return" << endl ;
442 return;
443 }
444 }
445 // cout << " Signal occured outside a try block, but no handler for it" <<endl;
446 return;
447 }
448#endif
449
450
451 // cout << "OSD::Handler: signal " << (int) theSignal << " occured inside a try block " << endl ;
452
453 if ( ADR_ACT_SIGIO_HANDLER != NULL ) (*ADR_ACT_SIGIO_HANDLER)() ;
454
455#ifdef linux
456 if (fFltExceptions)
457 feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
458 //feenableexcept (FE_INVALID | FE_DIVBYZERO);
459#endif
460
461 sigset_t set;
462 sigemptyset(&set);
463
464 switch(theSignal) {
465
466 case SIGHUP:
467 OSD_SIGHUP::NewInstance("SIGHUP 'hangup' detected.")->Jump();
468 exit(SIGHUP);
469 break;
470
471 case SIGINT:
472 OSD_SIGINT::NewInstance("SIGINT 'interrupt' detected.")->Jump();
473 exit(SIGINT);
474 break;
475
476 case SIGQUIT:
477 OSD_SIGQUIT::NewInstance("SIGQUIT 'quit' detected.")->Jump();
478 exit(SIGQUIT);
479 break;
480
481 case SIGILL:
482 OSD_SIGILL::NewInstance("SIGILL 'illegal instruction' detected.")->Jump();
483 exit(SIGILL);
484 break;
485
486 case SIGKILL:
487 OSD_SIGKILL::NewInstance("SIGKILL 'kill' detected.")->Jump();
488 exit(SIGKILL);
489 break;
490
491 case SIGBUS:
492 sigaddset(&set, SIGBUS);
493 sigprocmask(SIG_UNBLOCK, &set, NULL) ;
494 OSD_SIGBUS::NewInstance("SIGBUS 'bus error' detected.")->Jump();
495 exit(SIGBUS);
496 break;
497
498 case SIGSEGV:
499 OSD_SIGSEGV::NewInstance("SIGSEGV 'segmentation violation' detected.")->Jump();
500 exit(SIGSEGV);
501 break;
502
503#ifdef SIGSYS
504 case SIGSYS:
505 OSD_SIGSYS::NewInstance("SIGSYS 'bad argument to system call' detected.")->Jump();
506 exit(SIGSYS);
507 break;
508#endif
509
510 case SIGFPE:
511 {
512 sigaddset(&set, SIGFPE);
513 sigprocmask(SIG_UNBLOCK, &set, NULL) ;
514#ifdef DECOSF1
515// Pour DEC/OSF1 SIGFPE = Division par zero.
516// should be clarified why in debug mode only?
517#ifdef DEBUG
518 Standard_DivideByZero::NewInstance('')->Jump;
519#endif
520 break;
521#endif
522#if (!defined (__sun)) && (!defined(SOLARIS))
523 Standard_NumericError::NewInstance("SIGFPE Arithmetic exception detected")->Jump();
524 break;
525#else
526 // Reste SOLARIS
527 if (aSigInfo ) {
528 switch(aSigInfo->si_code) {
529 case FPE_FLTDIV_TRAP :
530 Standard_DivideByZero::NewInstance("Floating Divide By Zero")->Jump(); break;
531 case FPE_INTDIV_TRAP :
532 Standard_DivideByZero::NewInstance("Integer Divide By Zero")->Jump(); break;
533 case FPE_FLTOVF_TRAP :
534 Standard_Overflow::NewInstance("Floating Overflow")->Jump(); break;
535 case FPE_INTOVF_TRAP :
536 Standard_Overflow::NewInstance("Integer Overflow")->Jump(); break;
537 case FPE_FLTUND_TRAP :
538 Standard_NumericError::NewInstance("Floating Underflow")->Jump(); break;
539 case FPE_FLTRES_TRAP:
540 Standard_NumericError::NewInstance("Floating Point Inexact Result")->Jump(); break;
541 case FPE_FLTINV_TRAP :
542 Standard_NumericError::NewInstance("Invalid Floating Point Operation")->Jump(); break;
543 default:
544 Standard_NumericError::NewInstance("Numeric Error")->Jump(); break;
545 }
546 }
547 else {
548 Standard_NumericError::NewInstance("SIGFPE Arithmetic exception detected")->Jump();
549 }
550#endif
551 break;
552 }
553#if defined (__sgi) || defined(IRIX)
554 case SIGTRAP:
555 sigaddset(&set, SIGTRAP);
556 sigprocmask(SIG_UNBLOCK, &set, NULL) ;
557 Standard_DivideByZero::NewInstance("SIGTRAP IntegerDivideByZero")->Jump(); break;
558#endif
559 default:
560 cout << "Unexpected signal " << (Standard_Integer ) theSignal << endl ;
561 }
562}
563
564//============================================================================
565//==== SegvHandler
566//============================================================================
567
568#ifdef SA_SIGINFO
569
570#ifdef NO_CXX_EXCEPTION
571void OSD::SegvHandler(const OSD_Signals theSig,
572 const Standard_Address ip,
573 const Standard_Address theContext)
574{
575 if (!Standard_ErrorHandler::IsInTryBlock()) {
576 Handler(theSig, ip, theContext);
577 return;
578 }
579#else
580void OSD::SegvHandler(const OSD_Signals,
581 const Standard_Address ip,
582 const Standard_Address)
583{
584#endif
585
586#ifdef linux
587 if (fFltExceptions)
588 feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
589 //feenableexcept (FE_INVALID | FE_DIVBYZERO);
590#endif
591
592// cout << "OSD::SegvHandler activated(SA_SIGINFO)" << endl ;
593 if ( ip != NULL ) {
594 sigset_t set;
595 sigemptyset(&set);
596 sigaddset(&set, SIGSEGV);
597 sigprocmask (SIG_UNBLOCK, &set, NULL) ;
598 void *address = ((siginfo_t *)ip)->si_addr ;
599 if ( (((long) address )& ~0xffff) == (long) UndefinedHandleAddress ) {
600 Standard_NullObject::NewInstance("Attempt to access to null object")->Jump();
601 }
602 else {
603 char Msg[100];
604 sprintf(Msg,"SIGSEGV 'segmentation violation' detected. Address %lx",
605 (long ) address ) ;
606 OSD_SIGSEGV::NewInstance(Msg)->Jump();
607 }
608 }
609 else
610 cout << "Wrong undefined address." << endl ;
611 exit(SIGSEGV);
612}
613
614#if defined (_hpux) || defined(HPUX)
615//============================================================================
616//==== SegvHandler
617//============================================================================
618
619// Not ACTIVE ? SA_SIGINFO is defined on SUN, OSF, SGI and HP (and Linux) !
620// pour version 09.07
621void OSD::SegvHandler(const OSD_Signals aSig, const Standard_Address code,
622 const Standard_Address scp)
623//void OSD::SegvHandler(const OSD_Signals aSig, int code, const Standard_Address scp)
624{
625 unsigned long Space ;
626 unsigned long Offset ;
627 char Msg[100] ;
628
629 if ( scp != NULL ) {
630 Space = ((struct sigcontext *)scp)->sc_sl.sl_ss.ss_cr20 ;
631 Offset = ((struct sigcontext *)scp)->sc_sl.sl_ss.ss_cr21 ;
632// cout << "Wrong address = " << hex(Offset) << endl ;
633 if ((Offset & ~0xffff) == (long)UndefinedHandleAddress)
634 Standard_NullObject::Jump("Attempt to access to null object") ;
635 else {
636 sprintf(Msg,"SIGSEGV 'segmentation violation' detected. Address %lx",Offset) ;
637 OSD_SIGSEGV::Jump(Msg);
638// scp->sc_pcoq_head = scp->sc_pcoq_tail ; Permettrait de continuer a
639// scp->sc_pcoq_tail = scp->sc_pcoq_tail + 0x4 ; l'intruction suivant le segv.
640 }
641 }
642 else
643 cout << "Wrong undefined address." << endl ;
644 exit(SIGSEGV);
645}
646
647#endif
648#else
649// Must be there for compatibility with Windows NT system ---------------
650
651Standard_Integer OSD :: WntHandler ( const Standard_Address )
652 {return 0 ;}
653
654#endif
655#endif