OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [expect/] [exp_trap.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/* exp_trap.c - Expect's trap command
2
 
3
Written by: Don Libes, NIST, 9/1/93
4
 
5
Design and implementation of this program was paid for by U.S. tax
6
dollars.  Therefore it is public domain.  However, the author and NIST
7
would appreciate credit if this program or parts of it are used.
8
 
9
*/
10
 
11
#include "expect_cf.h"
12
 
13
#include <stdio.h>
14
#include <signal.h>
15
#include <sys/types.h>
16
 
17
#ifdef HAVE_SYS_WAIT_H
18
#include <sys/wait.h>
19
#endif
20
 
21
/* Use _NSIG if NSIG not present */
22
#ifndef NSIG
23
#ifdef _NSIG
24
#define NSIG _NSIG
25
#endif
26
#endif
27
 
28
#if defined(SIGCLD) && !defined(SIGCHLD)
29
#define SIGCHLD SIGCLD
30
#endif
31
 
32
#include "tcl.h"
33
 
34
#include "exp_rename.h"
35
#include "exp_prog.h"
36
#include "exp_command.h"
37
#include "exp_log.h"
38
 
39
#ifdef TCL_DEBUGGER
40
#include "Dbg.h"
41
#endif
42
 
43
#define NO_SIG 0
44
 
45
static struct trap {
46
        char *action;           /* Tcl command to execute upon sig */
47
                                /* Each is handled by the eval_trap_action */
48
        int mark;               /* TRUE if signal has occurred */
49
        Tcl_Interp *interp;     /* interp to use or 0 if we should use the */
50
                                /* interpreter active at the time the sig */
51
                                /* is processed */
52
        int code;               /* return our new code instead of code */
53
                                /* available when signal is processed */
54
        char *name;             /* name of signal */
55
        int reserved;           /* if unavailable for trapping */
56
} traps[NSIG];
57
 
58
int sigchld_count = 0;   /* # of sigchlds caught but not yet processed */
59
 
60
static int eval_trap_action();
61
 
62
static int got_sig;             /* this records the last signal received */
63
                                /* it is only a hint and can be wiped out */
64
                                /* by multiple signals, but it will always */
65
                                /* be left with a valid signal that is */
66
                                /* pending */
67
 
68
static Tcl_AsyncHandler async_handler;
69
 
70
static char *
71
signal_to_string(sig)
72
int sig;
73
{
74
        if (sig <= 0 || sig > NSIG) return("SIGNAL OUT OF RANGE");
75
        return(traps[sig].name);
76
}
77
 
78
/* current sig being processed by user sig handler */
79
static int current_sig = NO_SIG;
80
 
81
int exp_nostack_dump = FALSE;   /* TRUE if user has requested unrolling of */
82
                                /* stack with no trace */
83
 
84
 
85
 
86
/*ARGSUSED*/
87
static int
88
tophalf(clientData,interp,code)
89
ClientData clientData;
90
Tcl_Interp *interp;
91
int code;
92
{
93
        struct trap *trap;      /* last trap processed */
94
        int rc;
95
        int i;
96
        Tcl_Interp *sig_interp;
97
/*      extern Tcl_Interp *exp_interp;*/
98
 
99
        exp_debuglog("sighandler: handling signal(%d)\r\n",got_sig);
100
 
101
        if (got_sig <= 0 || got_sig >= NSIG) {
102
                errorlog("caught impossible signal %d\r\n",got_sig);
103
                abort();
104
        }
105
 
106
        /* start to work on this sig.  got_sig can now be overwritten */
107
        /* and it won't cause a problem */
108
        current_sig = got_sig;
109
        trap = &traps[current_sig];
110
 
111
        trap->mark = FALSE;
112
 
113
        /* decrement below looks dangerous */
114
        /* Don't we need to temporarily block bottomhalf? */
115
        if (current_sig == SIGCHLD) {
116
                sigchld_count--;
117
                exp_debuglog("sigchld_count-- == %d\n",sigchld_count);
118
        }
119
 
120
        if (!trap->action) {
121
                /* In this one case, we let ourselves be called when no */
122
                /* signaler predefined, since we are calling explicitly */
123
                /* from another part of the program, and it is just simpler */
124
                if (current_sig == 0) return code;
125
                errorlog("caught unexpected signal: %s (%d)\r\n",
126
                        signal_to_string(current_sig),current_sig);
127
                abort();
128
        }
129
 
130
        if (trap->interp) {
131
                /* if trap requested original interp, use it */
132
                sig_interp = trap->interp;
133
        } else if (!interp) {
134
                /* else if another interp is available, use it */
135
                sig_interp = interp;
136
        } else {
137
                /* fall back to exp_interp */
138
                sig_interp = exp_interp;
139
        }
140
 
141
        rc = eval_trap_action(sig_interp,current_sig,trap,code);
142
        current_sig = NO_SIG;
143
 
144
        /*
145
         * scan for more signals to process
146
         */
147
 
148
        /* first check for additional SIGCHLDs */
149
        if (sigchld_count) {
150
                got_sig = SIGCHLD;
151
                traps[SIGCHLD].mark = TRUE;
152
                Tcl_AsyncMark(async_handler);
153
        } else {
154
                got_sig = -1;
155
                for (i=1;i<NSIG;i++) {
156
                        if (traps[i].mark) {
157
                                got_sig = i;
158
                                Tcl_AsyncMark(async_handler);
159
                                break;
160
                        }
161
                }
162
        }
163
        return rc;
164
}
165
 
166
#ifdef REARM_SIG
167
int sigchld_sleep;
168
static int rearm_sigchld = FALSE;       /* TRUE if sigchld needs to be */
169
                                        /* rearmed (i.e., because it has
170
                                        /* just gone off) */
171
static int rearming_sigchld = FALSE;
172
#endif
173
 
174
/* called upon receipt of a user-declared signal */
175
static void
176
bottomhalf(sig)
177
int sig;
178
{
179
#ifdef REARM_SIG
180
        /*
181
         * tiny window of death if same signal should arrive here
182
         * before we've reinstalled it
183
         */
184
 
185
        /* In SV, sigchld must be rearmed after wait to avoid recursion */
186
        if (sig != SIGCHLD) {
187
                signal(sig,bottomhalf);
188
        } else {
189
                /* request rearm */
190
                rearm_sigchld = TRUE;
191
                if (rearming_sigchld) sigchld_sleep = TRUE;
192
        }
193
#endif
194
 
195
        traps[sig].mark = TRUE;
196
        got_sig = sig;          /* just a hint - can be wiped out by another */
197
        Tcl_AsyncMark(async_handler);
198
 
199
        /* if we are called while this particular async is being processed */
200
        /* original async_proc will turn off "mark" so that when async_proc */
201
        /* is recalled, it will see that nothing was left to do */
202
 
203
        /* In case of SIGCHLD though, we must recall it as many times as
204
         * we have received it.
205
         */
206
        if (sig == SIGCHLD) {
207
                sigchld_count++;
208
/*              exp_debuglog(stderr,"sigchld_count++ == %d\n",sigchld_count);*/
209
        }
210
#if 0
211
        /* if we are doing an i_read, restart it */
212
        if (env_valid && (sig != 0)) longjmp(env,2);
213
#endif
214
}
215
 
216
/*ARGSUSED*/
217
void
218
exp_rearm_sigchld(interp)
219
Tcl_Interp *interp;
220
{
221
#ifdef REARM_SIG
222
        if (rearm_sigchld) {
223
                rearm_sigchld = FALSE;
224
                rearming_sigchld = TRUE;
225
                signal(SIGCHLD,bottomhalf);
226
        }
227
 
228
        rearming_sigchld = FALSE;
229
 
230
        /* if the rearming immediately caused another SIGCHLD, slow down */
231
        /* It's probably one of Tcl's intermediary pipeline processes that */
232
        /* Tcl hasn't caught up with yet. */
233
        if (sigchld_sleep) {
234
                exp_dsleep(interp,0.2);
235
                sigchld_sleep = FALSE;
236
        }
237
#endif
238
}
239
 
240
 
241
void
242
exp_init_trap()
243
{
244
        int i;
245
 
246
        for (i=1;i<NSIG;i++) {
247
                traps[i].name = Tcl_SignalId(i);
248
                traps[i].action = 0;
249
                traps[i].reserved = FALSE;
250
        }
251
 
252
        /*
253
         * fix up any special cases
254
         */
255
 
256
#if defined(SIGCLD)
257
        /* Tcl names it SIGCLD, not good for portable scripts */
258
        traps[SIGCLD].name = "SIGCHLD";
259
#endif
260
#if defined(SIGALRM)
261
        traps[SIGALRM].reserved = TRUE;
262
#endif
263
#if defined(SIGKILL)
264
        traps[SIGKILL].reserved = TRUE;
265
#endif
266
#if defined(SIGSTOP)
267
        traps[SIGSTOP].reserved = TRUE;
268
#endif
269
 
270
        async_handler = Tcl_AsyncCreate(tophalf,(ClientData)0);
271
 
272
}
273
 
274
/* given signal index or name as string, */
275
/* returns signal index or -1 if bad arg */
276
int
277
exp_string_to_signal(interp,s)
278
Tcl_Interp *interp;
279
char *s;
280
{
281
        int sig;
282
        char *name;
283
 
284
        /* try interpreting as an integer */
285
        if (1 == sscanf(s,"%d",&sig)) {
286
                if (sig > 0 && sig < NSIG) return sig;
287
        } else {
288
                /* try interpreting as a string */
289
                for (sig=1;sig<NSIG;sig++) {
290
                        name = traps[sig].name;
291
                        if (streq(s,name) || streq(s,name+3)) return(sig);
292
                }
293
        }
294
 
295
        exp_error(interp,"invalid signal %s",s);
296
 
297
        return -1;
298
}
299
 
300
/*ARGSUSED*/
301
int
302
Exp_TrapCmd(clientData, interp, argc, argv)
303
ClientData clientData;
304
Tcl_Interp *interp;
305
int argc;
306
char **argv;
307
{
308
        char *action = 0;
309
        int n;          /* number of signals in list */
310
        char **list;    /* list of signals */
311
        int len;        /* length of action */
312
        int i;
313
        int show_name = FALSE;  /* if user asked for current sig by name */
314
        int show_number = FALSE;/* if user asked for current sig by number */
315
        int show_max = FALSE;   /* if user asked for NSIG-1 */
316
        int rc = TCL_OK;
317
        int new_code = FALSE;   /* if action result should overwrite orig */
318
        Tcl_Interp *new_interp = interp;/* interp in which to evaluate */
319
                                        /* action when signal occurs */
320
 
321
        argc--; argv++;
322
 
323
        while (*argv) {
324
                if (streq(*argv,"-code")) {
325
                        argc--; argv++;
326
                        new_code = TRUE;
327
                } else if (streq(*argv,"-interp")) {
328
                        argc--; argv++;
329
                        new_interp = 0;
330
                } else if (streq(*argv,"-name")) {
331
                        argc--; argv++;
332
                        show_name = TRUE;
333
                } else if (streq(*argv,"-number")) {
334
                        argc--; argv++;
335
                        show_number = TRUE;
336
                } else if (streq(*argv,"-max")) {
337
                        argc--; argv++;
338
                        show_max = TRUE;
339
                } else break;
340
        }
341
 
342
        if (show_name || show_number || show_max) {
343
                if (argc > 0) goto usage_error;
344
                if (show_max) {
345
                        sprintf(interp->result,"%d",NSIG-1);
346
                        return TCL_OK;
347
                }
348
 
349
                if (current_sig == NO_SIG) {
350
                        exp_error(interp,"no signal in progress");
351
                        return TCL_ERROR;
352
                }
353
                if (show_name) {
354
                        /* skip over "SIG" */
355
                        interp->result = signal_to_string(current_sig) + 3;
356
                } else {
357
                        sprintf(interp->result,"%d",current_sig);
358
                }
359
                return TCL_OK;
360
        }
361
 
362
        if (argc == 0 || argc > 2) goto usage_error;
363
 
364
        if (argc == 1) {
365
                int sig = exp_string_to_signal(interp,*argv);
366
                if (sig == -1) return TCL_ERROR;
367
 
368
                if (traps[sig].action) {
369
                        Tcl_AppendResult(interp,traps[sig].action,(char *)0);
370
                } else {
371
                        interp->result = "SIG_DFL";
372
                }
373
                return TCL_OK;
374
        }
375
 
376
        action = *argv;
377
 
378
        /* argv[1] is the list of signals - crack it open */
379
        if (TCL_OK != Tcl_SplitList(interp,argv[1],&n,&list)) {
380
                errorlog("%s\r\n",interp->result);
381
                goto usage_error;
382
        }
383
 
384
        for (i=0;i<n;i++) {
385
                int sig = exp_string_to_signal(interp,list[i]);
386
                if (sig == -1) {
387
                        rc = TCL_ERROR;
388
                        break;
389
                }
390
 
391
                if (traps[sig].reserved) {
392
                        exp_error(interp,"cannot trap %s",signal_to_string(sig));
393
                        rc = TCL_ERROR;
394
                        break;
395
                }
396
 
397
#if 0
398
#ifdef TCL_DEBUGGER
399
                if (sig == SIGINT && exp_tcl_debugger_available) {
400
                        exp_debuglog("trap: cannot trap SIGINT while using debugger\r\n");
401
                        continue;
402
                }
403
#endif /* TCL_DEBUGGER */
404
#endif
405
 
406
                exp_debuglog("trap: setting up signal %d (\"%s\")\r\n",sig,list[i]);
407
 
408
                if (traps[sig].action) ckfree(traps[sig].action);
409
 
410
                if (streq(action,"SIG_DFL")) {
411
                        /* should've been free'd by now if nec. */
412
                        traps[sig].action = 0;
413
                        signal(sig,SIG_DFL);
414
#ifdef REARM_SIG
415
                        if (sig == SIGCHLD)
416
                                rearm_sigchld = FALSE;
417
#endif /*REARM_SIG*/
418
                } else {
419
                        len = 1 + strlen(action);
420
                        traps[sig].action = ckalloc(len);
421
                        memcpy(traps[sig].action,action,len);
422
                        traps[sig].interp = new_interp;
423
                        traps[sig].code = new_code;
424
                        if (streq(action,"SIG_IGN")) {
425
                                signal(sig,SIG_IGN);
426
                        } else signal(sig,bottomhalf);
427
                }
428
        }
429
        ckfree((char *)list);
430
        return(rc);
431
 usage_error:
432
        exp_error(interp,"usage: trap [command or SIG_DFL or SIG_IGN] {list of signals}");
433
        return TCL_ERROR;
434
}
435
 
436
/* called by tophalf() to process the given signal */
437
static int
438
eval_trap_action(interp,sig,trap,oldcode)
439
Tcl_Interp *interp;
440
int sig;
441
struct trap *trap;
442
int oldcode;
443
{
444
        int code_flag;
445
        int newcode;
446
        Tcl_DString ei; /* errorInfo */
447
        char *eip;
448
        Tcl_DString ec; /* errorCode */
449
        char *ecp;
450
        Tcl_DString ir; /* interp->result */
451
 
452
        exp_debuglog("async event handler: Tcl_Eval(%s)\r\n",trap->action);
453
 
454
        /* save to prevent user from redefining trap->code while trap */
455
        /* is executing */
456
        code_flag = trap->code;
457
 
458
        if (!code_flag) {
459
                /*
460
                 * save return values
461
                 */
462
                eip = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
463
                if (eip) {
464
                        Tcl_DStringInit(&ei);
465
                        eip = Tcl_DStringAppend(&ei,eip,-1);
466
                }
467
                ecp = Tcl_GetVar(interp,"errorCode",TCL_GLOBAL_ONLY);
468
                if (ecp) {
469
                        Tcl_DStringInit(&ec);
470
                        ecp = Tcl_DStringAppend(&ec,ecp,-1);
471
                }
472
                /* I assume interp->result is always non-zero, right? */
473
                Tcl_DStringInit(&ir);
474
                Tcl_DStringAppend(&ir,interp->result,-1);
475
        }
476
 
477
        newcode = Tcl_GlobalEval(interp,trap->action);
478
 
479
        /*
480
         * if new code is to be ignored (usual case - see "else" below)
481
         *      allow only OK/RETURN from trap, otherwise complain
482
         */
483
 
484
        if (code_flag) {
485
                exp_debuglog("return value = %d for trap %s, action %s\r\n",
486
                                newcode,signal_to_string(sig),trap->action);
487
                if (*interp->result != 0) {
488
                        errorlog("%s\r\n",interp->result);
489
 
490
                        /*
491
                         * Check errorinfo and see if it contains -nostack.
492
                         * This shouldn't be necessary, but John changed the
493
                         * top level interp so that it distorts arbitrary
494
                         * return values into TCL_ERROR, so by the time we
495
                         * get back, we'll have lost the value of errorInfo
496
                         */
497
 
498
                        eip = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
499
                        exp_nostack_dump =
500
                                (eip && (0 == strncmp("-nostack",eip,8)));
501
                }
502
        } else if (newcode != TCL_OK && newcode != TCL_RETURN) {
503
                if (newcode != TCL_ERROR) {
504
                        exp_error(interp,"return value = %d for trap %s, action %s\r\n",newcode,signal_to_string(sig),trap->action);
505
                }
506
                Tcl_BackgroundError(interp);
507
        }
508
 
509
        if (!code_flag) {
510
                /*
511
                 * restore values
512
                 */
513
                Tcl_ResetResult(interp);        /* turns off Tcl's internal */
514
                                /* flags: ERR_IN_PROGRESS, ERROR_CODE_SET */
515
 
516
                if (eip) {
517
                        Tcl_AddErrorInfo(interp,eip);
518
                        Tcl_DStringFree(&ei);
519
                } else {
520
                        Tcl_UnsetVar(interp,"errorInfo",0);
521
                }
522
 
523
                /* restore errorCode.  Note that Tcl_AddErrorInfo (above) */
524
                /* resets it to NONE.  If the previous value is NONE, it's */
525
                /* important to avoid calling Tcl_SetErrorCode since this */
526
                /* with cause Tcl to set its internal ERROR_CODE_SET flag. */
527
                if (ecp) {
528
                        if (!streq("NONE",ecp))
529
                                Tcl_SetErrorCode(interp,ecp,(char *)0);
530
                        Tcl_DStringFree(&ec);
531
                } else {
532
                        Tcl_UnsetVar(interp,"errorCode",0);
533
                }
534
 
535
                Tcl_DStringResult(interp,&ir);
536
                Tcl_DStringFree(&ir);
537
 
538
                newcode = oldcode;
539
 
540
                /* note that since newcode gets overwritten here by old code */
541
                /* it is possible to return in the middle of a trap by using */
542
                /* "return" (or "continue" for that matter)! */
543
        }
544
        return newcode;
545
}
546
 
547
static struct exp_cmd_data
548
cmd_data[]  = {
549
{"trap",        exp_proc(Exp_TrapCmd),  (ClientData)EXP_SPAWN_ID_BAD,   0},
550
{0}};
551
 
552
void
553
exp_init_trap_cmds(interp)
554
Tcl_Interp *interp;
555
{
556
        exp_create_commands(interp,cmd_data);
557
}
558
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.