1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
|
/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
*/
#define import_spp
#define import_libc
#define import_fset
#define import_main
#define import_stdio
#define import_error
#define import_setjmp
#define import_knames
#define import_prtype
#define import_xwhen
#define import_xnames
#include <iraf.h>
#include "config.h"
#include "grammar.h"
#include "opcodes.h"
#include "operand.h"
#include "param.h"
#include "clmodes.h"
#include "task.h"
#include "errs.h"
#include "mem.h"
#include "proto.h"
#define CLDIR "cl$"
#define HOSTLIB "hlib$"
/*
* MAIN -- The main program of the CL.
*
* Repetitively call yyparse() and run() until hit eof (or "bye") during
* the lowest cl. The instructions exec and bye change the pc so that
* new code is compiled and run in a recursive fashion without having to
* call run() itself recursively.
*
* TODO:
* check access rights of file-type params in inspect.
* add < and > chars to mode param.
* all the other TODO's and more i'm sure...
*/
#define FOREGROUND 0
#define BACKGROUND 1
#define BKG_QUANTUM 30 /* period(sec) bkgjob checkup */
#define MAX_INTERRUPTS 5 /* max interrupts of a task */
#define LEN_INTRSTK 10 /* max nesting of saved interrupts */
typedef int (*PFI)();
extern int yydebug; /* print each parser state if set */
extern FILE *yyin; /* where parser reads from */
extern yeof; /* set when yacc sees eof */
extern dobkg; /* set when code is to be done in bkg */
extern bkgno; /* job number if bkg job */
int cldebug = 0; /* print out lots of goodies if > 0 */
int cltrace = 0; /* trace instruction execution if > 0 */
static PFI old_onipc; /* X_IPC handler chained to onint() */
static long *jumpcom; /* IRAF Main setjmp/longjmp buffer */
static jmp_buf jmp_save; /* save IRAF Main jump vector */
static jmp_buf jmp_clexit; /* clexit() jumps here */
static int intr_sp; /* interrupt save stack pointer */
static XINT intr_save[LEN_INTRSTK]; /* the interrupt save stack */
memel cl_dictbuf[DICTSIZE]; /* the dictionary area */
jmp_buf errenv; /* cl_error() jumps here */
jmp_buf intenv; /* X_INT during process jumps here */
int validerrenv; /* stays 0 until errenv gets set */
int loggingout; /* set while processing logout file */
int gologout; /* set when logout() is typed */
int alldone; /* set by oneof when popping firstask */
int recursion; /* detect error recursion in ONERROR */
int errlev; /* detect error recursion in CL_ERROR */
int ninterrupts; /* number of onint() calls per task */
int currentline; /* current line being executed */
int errorline; /* error line being recovered */
long cpustart, clkstart; /* starting cpu, clock times if bkg */
int logout_status = 0; /* optional status arg to logout() */
static void execute();
static void login(), logout();
static void startup(), shutdown();
static char *file_concat (char *in1, char *in2);
static char *tmpfile = NULL;
extern char epar_cmdbuf[];
/* C_MAIN -- Called by the SPP procedure in cl.x to fire up the CL.
* In effect we are chained to the IRAF Main, being called immediately after
* the file system, etc. is initialized. When we exit we signal that the
* interpreter be skipped, proceeding directly to process shutdown.
*/
c_main (prtype, bkgfile, cmd)
int *prtype; /* process type (connected, detached) */
PKCHAR *bkgfile; /* bkgfile filename if detached */
PKCHAR *cmd; /* host command line */
{
XINT bp;
/* Save the setjmp vector of the IRAF Main for restoration at clexit
* time. We need to intercept all errors and do error recovery
* ourselves during normal execution, but when the CL exits we are
* not prepared to deal with errors occuring during shutdown.
*/
XMJBUF (&bp); jumpcom = (long *)&Memc[bp];
cl_amovi ((int *)jumpcom, (int *)jmp_save, LEN_JUMPBUF);
/* Init clexit() in case we have to panic stop. */
if (setjmp (jmp_clexit))
goto exit_;
/* Set up dictionary and catch signals. If we are background, read in
* file and jump right into run, else hand craft first task. Die if
* these fail.
*/
startup ();
if (*prtype == PR_DETACHED) {
bkg_startup ((char *)bkgfile);
cpustart = c_cputime (0L);
clkstart = c_clktime (0L);
execute (BACKGROUND);
} else {
login ((char *) cmd);
execute (FOREGROUND);
logout();
execute (FOREGROUND);
}
shutdown();
exit_:
/* Return to the IRAF Main. The PR_EXIT code commands the main to
* skip the interpreter loop and shutdown. Restore the error
* jump vector in the IRAF Main so that it can handle errors occuring
* during shutdown; we are turning control back over to the Main.
* This is ugly, but the real problem is the jump vectors. There
* seems to be no alternative to this sort of thing...
*/
cl_amovi ((int *)jmp_save, (int *)jumpcom, LEN_JUMPBUF);
return (PR_EXIT | (logout_status << 1));
}
/* CLEXIT -- Called on fatal error from error() when get an error so bad that we
* should commit suicide.
*/
int
clexit (void)
{
longjmp (jmp_clexit, 1);
}
/* CLSHUTDOWN -- Public entry for shutdown.
*/
int
clshutdown (void)
{
shutdown();
}
/* STARTUP -- CL startup code. Called by onentry() at process startup.
* Allocate space for the dictionary, post exception handlers, initialize
* error recovery.
*
* NOTE: in the current implementation a fixed size buffer is allocated for
* the dictionary due to the difficulty of passing the dictionary to the
* bkg CL if a dynamically allocated dictionary is used. The problem is
* that the dictionary is full of pointers to absolute addresses, and
* we cannot control where the memory allocator in the bkg CL will allocate
* a buffer. A simple binary copy of the dictionary to different region
* of memory in the bkg CL will leave the pointers pointing into limbo.
*
* TODO: Write a pair of procedures for each major data structure to dump
* and restore the data structure in a binary array. Passing the CL context
* to the bkg CL would then be a matter of calling the dump procedure for
* each major data structure to dump the structure into the bkgfile, then
* doing a matching restore in the bkg CL to restore the data structure
* to a different region of memory. The ENV package does this already.
* The only alternative would be to use indices rather than pointers in
* the dictionary, which is not what C likes to do.
*/
static void
startup (void)
{
int onint(), onipc();
/* Set up pointers to dictionary buffer.
*/
dictionary = cl_dictbuf;
topd = 0;
maxd = DICTSIZE;
if (cldebug)
printf ("dictionary starts at %d (0%o)\n", dictionary, dictionary);
/* Post exception handlers for interrupt and write to IPC with no
* reader. The remaining exceptions use the standard handler.
*/
c_xwhen (X_IPC, onipc, &old_onipc);
intr_reset();
/* The following is a temporary solution to an initialization problem
* with pseudofile i/o.
*/
PRPSINIT();
}
/* SHUTDOWN -- Call this to exit gracefully from the whole cl; never return.
* Write out any remaining PF_UPDATE'd pfiles by restoring topd to just above
* first task unless we are in batch mode, then just flush io and die..
* So that the restor will include the cl's pfile and any other pfiles that
* might have been cached or assigned into, we force its topd to be
* below its pfile head. See the "pfp < topdp" loop in restor().
* Don't bother with restor'ing if BATCH since we don't want to write out
* anything then anyway.
*/
static void
shutdown (void)
{
float cpu, clk;
pr_dumpcache (0, YES); /* flush process cache */
clgflush(); /* flush graphics output */
if (firstask->t_flags & T_BATCH) {
iofinish (currentask);
if (notify()) {
cpu = (float)c_cputime(cpustart) / 1000.;
clk = (float)c_clktime(clkstart);
fprintf (stderr, "\n[%d] done %.1f %.0m %d%%\n", bkgno,
cpu, clk/60., (int)((clk > 0 ? cpu / clk : 0.) * 100.));
}
} else {
firstask->t_topd = dereference (firstask->t_ltp) + LTASKSIZ;
restor (firstask);
}
/* Clean up and temp file created for startup.
*/
if (tmpfile)
c_delete (tmpfile);
yy_startblock (LOG); /* flush and close log */
close_logfile (logfile());
clexit();
}
/* EXECUTE -- Each loop corresponds to an exec in the interpreted code.
* This occurs when a script task or process is ready to run. In background
* mode, we skip the preliminaries and jump right in and interpret the
* compiled code.
*/
static void
execute (int mode)
{
int parsestat;
XINT old_parhead;
char *curcmd();
extern char *onerr_handler;
alldone = 0;
gologout = 0;
if (mode == BACKGROUND) {
if (setjmp (jumpcom))
onerr();
goto bkg;
}
/* Called when control stack contains only the firsttask. ONEOF sets
* alldone true when eof/bye is seen and currentask=firstask,
* terminating the loop and returning to main.
*/
do {
/* Bkg_update() checks for blocked or finished bkg jobs and prints
* a message if it finds one. This involves one or more access()
* calls so don't call it more than every 5 seconds. The errenv
* jump vector is used by cl_error() for error restart. The JUMPCOM
* vector is used to intercept system errors which would otherwise
* restart the CL.
*/
if (currentask->t_flags & T_INTERACTIVE) {
static long last_clktime;
if (c_clktime (last_clktime) > BKG_QUANTUM) {
last_clktime = c_clktime (0L);
bkg_update (1);
}
validerrenv = 1;
setjmp (errenv);
ninterrupts = 0;
if (setjmp (jumpcom))
onerr();
} else if (!(currentask->t_flags & T_SCRIPT))
setjmp (intenv);
pc = currentask->t_bascode;
currentask->t_topd = topd;
currentask->t_topcs = topcs;
recursion = 0;
errlev = 0;
c_erract (OK);
yeof = 0;
/* In the new CL the parser needs to know more about parameters
* than before. Hence param files may be read in during parsing.
* Since we discard the dictionary after parsing we must unlink
* these param files, and re-read them when the
* program is run. This is inefficient but appears to work.
*/
old_parhead = parhead;
if (gologout)
yeof++;
else {
if (cltrace > 1)
eprintf ("main.c: start of yyparse\n");
yy_startblock (LOG); /* start new history blk */
parsestat = yyparse(); /* parse command block */
if (cltrace > 1)
eprintf ("main.c: end of yyparse\n");
topd = currentask->t_topd; /* discard addconst()'s */
topcs = currentask->t_topcs; /* discard compiler temps */
parhead = old_parhead; /* forget param files. */
if (parsestat != 0)
cl_error (E_IERR, "parser gagged");
}
if (dobkg) {
bkg_spawn (curcmd());
} else {
bkg:
if (yeof)
oneof(); /* restores previous task */
else {
/* set stack above pc, point pc back to code */
topos = basos = pc - 1;
pc = currentask->t_bascode;
}
if (!alldone) {
run(); /* run code starting at pc */
/* Save the last line executed from this run. In the
* event the task failed, this will be the line number
* of the failure we'll need during error recovery.
*/
if (currentline < currentask->t_scriptln)
errorline = currentline;
}
}
} until (alldone);
}
/* LOGIN -- Hand-craft the first cl process. Push the first task to become
* currentask, set up clpackage at pachead and set cl as its first ltask.
* Add the builtin function ltasks. Run the startup file as the stdin of cl.
* If any of this fails, we die.
*/
static void
login (char *cmd)
{
register struct task *tp;
register char *ip, *op, *arg;
register struct param *pp;
struct ltask *ltp;
struct operand o;
char *loginfile = LOGINFILE;
char alt_loginfile[SZ_PATHNAME];
char init_envfile[SZ_PATHNAME];
char clstartup[SZ_PATHNAME];
char clprocess[SZ_PATHNAME];
char ebuf[FAKEPARAMLEN];
char arglist[SZ_LINE], *ap;
/* Initialize.
*/
memset (alt_loginfile, 0, SZ_PATHNAME);
memset (init_envfile, 0, SZ_PATHNAME);
memset (clstartup, 0, SZ_PATHNAME);
memset (clprocess, 0, SZ_PATHNAME);
memset (arglist, 0, SZ_LINE);
memset (ebuf, 0, FAKEPARAMLEN);
strcpy (clstartup, HOSTLIB);
strcat (clstartup, CLSTARTUP);
strcpy (clprocess, CLDIR);
strcat (clprocess, CLPROCESS);
tp = firstask = currentask = pushtask();
tp->t_in = tp->t_stdin = stdin;
tp->t_out = tp->t_stdout = stdout;
tp->t_stderr = stderr;
tp->t_stdgraph = fdopen (STDGRAPH, "w");
tp->t_stdimage = fdopen (STDIMAGE, "w");
tp->t_stdplot = fdopen (STDPLOT, "w");
tp->t_pid = -1;
tp->t_flags |= (T_INTERACTIVE|T_CL);
/* Make root package. Avoid use of newpac() since pointers are not
* yet set right.
*/
pachead = topd;
curpack = (struct package *) memneed (PACKAGESIZ);
curpack->pk_name = comdstr (ROOTPACKAGE);
curpack->pk_ltp = NULL;
curpack->pk_pfp = NULL;
curpack->pk_npk = NULL;
curpack->pk_flags = 0;
/* Initialize the input buffers. */
strcpy (epar_cmdbuf, "");
/* Make first ltask.
*/
ltp = newltask (curpack, "cl", clprocess, (struct ltask *) NULL);
tp->t_ltp = ltp;
ltp->lt_flags |= (LT_PFILE|LT_CL);
tp->t_pfp = pfileload (ltp); /* call newpfile(), read cl.par */
tp->t_pfp->pf_npf = NULL;
setclmodes (tp); /* uses cl's params */
setbuiltins (curpack); /* add more ltasks off clpackage*/
/* Define the second package, the "clpackage", and make it the
* current package (default package at startup). Tasks subsequently
* defined by the startup script will get put in clpackage.
*/
curpack = newpac (CLPACKAGE, "bin$");
/* Compile code that will run the startup script then, if it exists
* in the current directory, a login.cl script. We need to do as
* much by hand here as the forever loop in main would have if this
* code came from calling yyparse().
*/
if (c_access (clstartup,0,0) == NO)
cl_error (E_FERR, "Cannot find startup file `%s'", clstartup);
currentask->t_bascode = 0;
pc = 0;
o.o_type = OT_STRING;
o.o_val.v_s = clstartup;
compile (CALL, "cl");
compile (PUSHCONST, &o);
compile (REDIRIN);
compile (EXEC);
compile (FIXLANGUAGE);
/* The following is to permit error recovery in the event that an
* error occurs while reading the user's LOGIN.CL file.
*/
validerrenv = 1;
if (setjmp (errenv)) {
eprintf ("Error while reading login.cl file");
eprintf (" - may need to rebuild with mkiraf\n");
eprintf ("Fatal startup error. CL dies.\n");
clexit();
}
ninterrupts = 0;
if (setjmp (jumpcom))
onerr();
/* Nondestructively decompose the host command line into the startup
* filename and/or the argument string.
*/
ap = &arglist[0];
arg = cmd;
while ( *arg ) {
if (strncmp (arg, "-i ", 3) == 0) {
for (ip=arg+2; *ip && isspace(*ip); ip++) ;
for (op=init_envfile; *ip && *ip != ' '; *op++ = *ip++) ;
*op = EOS;
for ( ; *ip && isspace(*ip); ip++) ;
arg = ip;
} else if (strncmp (arg, "-f ", 3) == 0) {
for (ip=arg+2; *ip && isspace(*ip); ip++) ;
for (op=alt_loginfile; *ip && *ip != ' '; *op++ = *ip++) ;
*op = EOS;
for ( ; *ip && isspace(*ip); ip++) ;
arg = ip;
} else {
while (*arg && *arg != ' ')
*ap++ = *arg++;
if (*arg == ' ')
*ap++ = *arg++;
}
}
/* Copy any user supplied host command line arguments into the
* CL parameter $args to use in the startup script (for instance).
*/
o.o_type = OT_STRING;
strcpy (o.o_val.v_s, arglist);
compile (PUSHCONST, &o);
compile (ASSIGN, "args");
if (alt_loginfile[0] || init_envfile[0]) {
if (init_envfile[0] && (c_access (init_envfile,0,0) == YES)) {
/* Concatentate init and login files.
*/
tmpfile = file_concat (init_envfile, alt_loginfile);
o.o_val.v_s = tmpfile;
} else
o.o_val.v_s = alt_loginfile; /* no init, use alt */
/* Execute the file.
*/
compile (CALL, "cl");
compile (PUSHCONST, &o);
compile (REDIRIN);
compile (EXEC);
} else if (c_access (loginfile,0,0) == NO) {
char *home = envget ("HOME");
char global[SZ_LINE];
memset (global, 0, SZ_LINE);
sprintf (global, "%s/.iraf/login.cl", home);
if (c_access (global, 0, 0) == YES) {
o.o_val.v_s = global;
compile (CALL, "cl");
compile (PUSHCONST, &o);
compile (REDIRIN);
compile (EXEC);
} else {
printf ("Warning: no login.cl found in login directory\n");
}
} else {
o.o_val.v_s = loginfile;
compile (CALL, "cl");
compile (PUSHCONST, &o);
compile (REDIRIN);
compile (EXEC);
}
/* Initialize the fake error params.
*/
pp = addparam (firstask->t_pfp, "$errno,i,h,0\n", NULL);
pp->p_mode |= M_FAKE;
pp = addparam (firstask->t_pfp, "$errmsg,s,h,\"\"\n", NULL);
pp->p_mode |= M_FAKE;
pp = addparam (firstask->t_pfp, "$errtask,s,h,\"\"\n", NULL);
pp->p_mode |= M_FAKE;
pp = addparam (firstask->t_pfp, "$err_dzvalue,i,h,1\n", NULL);
pp->p_mode |= M_FAKE;
/* Initialize the error action. */
erract_init();
compile (END);
topos = basos = pc - 1;
pc = 0;
run(); /* returns after doing the first EXEC */
/* Add nothing here that will effect the dictionary or the stacks.
*/
if (cldebug)
printf ("topd, pachead, parhead: %u, %u, %u\n",
topd, pachead, parhead);
}
/* FILE_CONCAT -- Concatenate two files to a temporary output file. Return
* the name of the output file created.
*/
static char *
file_concat (char *in1, char *in2)
{
FILE *fd1, *fd2, *out;
static char *tmpfile, buf[SZ_LINE];
strcpy (buf, "/tmp/envcl");
tmpfile = mktemp (buf);
if (c_access (tmpfile, 0, 0) == YES)
c_delete (tmpfile);
if ((out = fopen (tmpfile, "wt")) == NULL)
printf ("Warning: tmp output file '%s' not found\n", tmpfile);
if (c_access (in1, 0, 0) == YES) {
if ((fd1 = fopen (in1, "rt")) == NULL)
printf ("Warning: file1 '%s' not found\n", in1);
while (fgets (buf, SZ_LINE, fd1))
fputs (buf, out);
fclose (fd1);
}
if (c_access (in2, 0, 0) == YES) {
if ((fd2 = fopen (in2, "rt")) == NULL)
printf ("Warning: file2 '%s' not found\n", in2);
while (fgets (buf, SZ_LINE, fd2))
fputs (buf, out);
fclose (fd2);
}
fclose (out);
return (tmpfile);
}
/* LOGOUT -- Process the system logout file. Called when the user logs
* off in an interactive CL (not called by bkg cl's). The standard input
* of the CL is hooked to the system logout file and when the eof of the
* logout file is seen the CL really does exit.
*/
static void
logout (void)
{
register struct task *tp;
char logoutfile[SZ_PATHNAME];
FILE *fp;
strcpy(logoutfile, HOSTLIB);
strcat(logoutfile, CLLOGOUT);
if ((fp = fopen (logoutfile, "r")) == NULL)
cl_error (E_FERR,
"Cannot open system logout file `%s'", logoutfile);
tp = firstask;
tp->t_in = tp->t_stdin = fp;
yyin = fp;
tp->t_flags = (T_CL|T_SCRIPT);
loggingout = 1;
gologout = 0;
}
/* MEMNEED -- Increase topd by incr INT's. Since at present the dictionary
* is fixed in size, abort if the dictionary overflows.
*/
char *
memneed (
int incr /* amount of space desired in ints, not bytes */
)
{
memel *old;
old = daddr (topd);
topd += incr;
/* Quad alignment is desirable for some architectures. */
if (topd & 1)
topd++;
if (topd > maxd)
cl_error (E_IERR, "dictionary full");
return ((char *)old);
}
/* ONINT -- Called when the interrupt exception occurs, i.e., the usual user
* attention-getter. (cntrl-c on dec, delete on unix, etc.). Also called
* when we are killed as a bkg job.
* If the current task is a script or the terminal, abort execution and
* initiate error recovery. If the task is in a child process merely send
* interrupt to the child and continue execution (giving the child a chance
* to cleanup before calling error, or to ignore the interrupt entirely).
* If the task wants to terminate it will send the ERROR statement to the CL.
* If we are a bkg job, call bkg_abort to clean up (delete temp files, etc.)
* before shutting down.
*/
/* ARGSUSED */
int
onint (
int *vex, /* virtual exception code */
int (**next_handler)(void) /* next handler to be called */
)
{
if (firstask->t_flags & T_BATCH) {
/* Batch task.
*/
iofinish (currentask);
bkg_abort();
clexit();
} else if (currentask->t_flags & (T_SCRIPT|T_CL|T_BUILTIN)) {
/* CL task.
*/
cl_error (E_UERR, "interrupt!!!");
} else {
/* External task connected via IPC. Pass the interrupt on to
* the child.
*/
c_prsignal (currentask->t_pid, X_INT);
/* Cancel any output and disable i/o on the tasks pseudofiles.
* This is necessary to cancel any i/o still buffered in the
* IPC channel. Commonly when the task is writing to STDOUT,
* for example, the CL will be writing the last buffer sent
* to the terminal, while the task waits after having already
* pushed the next buffer into the IPC. When we resume reading
* from the task we will see this buffered output on the next
* read and we wish to discard it. Leave STDERR connected to
* give a path to the terminal for recovery actions such as
* turning standout or graphics mode off. This gives the task
* a chance to cleanup but does not permit full recovery. The
* pseudofiles will be reconnected for the next task run.
*/
c_fseti (fileno(stdout), F_CANCEL, OK);
c_fseti (fileno(currentask->t_in), F_CANCEL, OK);
c_fseti (fileno(currentask->t_out), F_CANCEL, OK);
c_prredir (currentask->t_pid, STDIN, 0);
c_prredir (currentask->t_pid, STDOUT, 0);
/* If a subprocess is repeatedly interrupted we assume that it
* is hung in a loop and abort, advising the user to kill the
* process.
*/
if (++ninterrupts >= MAX_INTERRUPTS)
cl_error (E_UERR, "subprocess is hung; should be killed");
else
longjmp (intenv, 1);
}
*next_handler = NULL;
}
/* INTR_DISABLE -- Disable interrupts, e.g., to protect a critical section
* of code.
*/
int
intr_disable (void)
{
PFI junk;
if (intr_sp >= LEN_INTRSTK)
cl_error (E_IERR, "interrupt save stack overflow");
c_xwhen (X_INT, X_IGNORE, &junk);
intr_save[intr_sp++] = (XINT) junk;
}
/* INTR_ENABLE -- Reenable interrupts, reposting the interrupt vector saved
* in a prior call to INTR_DISABLE.
*/
int
intr_enable (void)
{
PFI junk;
if (--intr_sp < 0)
cl_error (E_IERR, "interrupt save stack underflow");
c_xwhen (X_INT, intr_save[intr_sp], &junk);
}
/* INTR_RESET -- Post the interrupt handler and clear the interrupt vector
* save stack.
*/
int
intr_reset (void)
{
PFI junk;
int onint();
c_xwhen (X_INT, onint, &junk);
intr_sp = 0;
}
/* ONERR -- Called when system error recovery takes place. The setjmp in
* execute() overrides the setjmp (ZSVJMP) in the IRAF Main. When system error
* recovery takes place, c_erract() calls ZDOJMP to restart the IRAF Main.
* We do not want to lose the runtime context of the CL, so we restart the
* CL main instead by intercepting the vector. We get the error message from
* the system and call cl_error() which eventually does a longjmp back to
* the errenv in execute().
*/
int
onerr (void)
{
char errmsg[SZ_LINE];
extern int do_error;
c_erract (EA_RESTART);
c_errget (errmsg, SZ_LINE);
errorline = currentline;
if (recursion++)
longjmp (errenv, 1);
else
cl_error (E_UERR, errmsg);
}
/* CL_AMOVI -- Copy an integer sized block of memory.
*/
int
cl_amovi (register int *ip, register int *op, register int len)
{
while (--len)
*op++ = *ip++;
}
|