]>
git.zerfleddert.de Git - micropolis/blob - src/tcl/tclproc.c
eb94c3b8297651db103366ff65d8f220b9a1de11
4 * This file contains routines that implement Tcl procedures,
5 * including the "proc" and "uplevel" commands.
7 * Copyright 1987-1991 Regents of the University of California
8 * Permission to use, copy, modify, and distribute this
9 * software and its documentation for any purpose and without
10 * fee is hereby granted, provided that the above copyright
11 * notice appear in all copies. The University of California
12 * makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without
14 * express or implied warranty.
18 static char rcsid
[] = "$Header: /sprite/src/lib/tcl/RCS/tclProc.c,v 1.59 91/09/30 16:59:54 ouster Exp $ SPRITE (Berkeley)";
24 * Forward references to procedures defined later in this file:
27 static int InterpProc
_ANSI_ARGS_((ClientData clientData
,
28 Tcl_Interp
*interp
, int argc
, char **argv
));
29 static void ProcDeleteProc
_ANSI_ARGS_((ClientData clientData
));
32 *----------------------------------------------------------------------
36 * This procedure is invoked to process the "proc" Tcl command.
37 * See the user documentation for details on what it does.
40 * A standard Tcl result value.
43 * A new procedure gets created.
45 *----------------------------------------------------------------------
50 Tcl_ProcCmd(dummy
, interp
, argc
, argv
)
51 ClientData dummy
; /* Not used. */
52 Tcl_Interp
*interp
; /* Current interpreter. */
53 int argc
; /* Number of arguments. */
54 char **argv
; /* Argument strings. */
56 register Interp
*iPtr
= (Interp
*) interp
;
57 register Proc
*procPtr
;
58 int result
, argCount
, i
;
59 char **argArray
= NULL
;
61 register Arg
*argPtr
= NULL
; /* Initialization not needed, but
62 * prevents compiler warning. */
65 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
66 " name args body\"", (char *) NULL
);
70 procPtr
= (Proc
*) ckalloc(sizeof(Proc
));
72 procPtr
->command
= (char *) ckalloc((unsigned) strlen(argv
[3]) + 1);
73 strcpy(procPtr
->command
, argv
[3]);
74 procPtr
->argPtr
= NULL
;
77 * Break up the argument list into argument specifiers, then process
78 * each argument specifier.
81 result
= Tcl_SplitList(interp
, argv
[2], &argCount
, &argArray
);
82 if (result
!= TCL_OK
) {
86 for (i
= 0; i
< argCount
; i
++) {
87 int fieldCount
, nameLength
, valueLength
;
91 * Now divide the specifier up into name and default.
94 result
= Tcl_SplitList(interp
, argArray
[i
], &fieldCount
,
96 if (result
!= TCL_OK
) {
100 ckfree((char *) fieldValues
);
101 Tcl_AppendResult(interp
,
102 "too many fields in argument specifier \"",
103 argArray
[i
], "\"", (char *) NULL
);
107 if ((fieldCount
== 0) || (*fieldValues
[0] == 0)) {
108 ckfree((char *) fieldValues
);
109 Tcl_AppendResult(interp
, "procedure \"", argv
[1],
110 "\" has argument with no name", (char *) NULL
);
114 nameLength
= strlen(fieldValues
[0]) + 1;
115 if (fieldCount
== 2) {
116 valueLength
= strlen(fieldValues
[1]) + 1;
120 argPtr
= (Arg
*) ckalloc((unsigned)
121 (sizeof(Arg
) - sizeof(argPtr
->name
) + nameLength
123 if (lastArgPtr
== NULL
) {
124 procPtr
->argPtr
= argPtr
;
126 lastArgPtr
->nextPtr
= argPtr
;
129 argPtr
->nextPtr
= NULL
;
130 strcpy(argPtr
->name
, fieldValues
[0]);
131 if (fieldCount
== 2) {
132 argPtr
->defValue
= argPtr
->name
+ nameLength
;
133 strcpy(argPtr
->defValue
, fieldValues
[1]);
135 argPtr
->defValue
= NULL
;
137 ckfree((char *) fieldValues
);
140 Tcl_CreateCommand(interp
, argv
[1], InterpProc
, (ClientData
) procPtr
,
142 ckfree((char *) argArray
);
146 ckfree(procPtr
->command
);
147 while (procPtr
->argPtr
!= NULL
) {
148 argPtr
= procPtr
->argPtr
;
149 procPtr
->argPtr
= argPtr
->nextPtr
;
150 ckfree((char *) argPtr
);
152 ckfree((char *) procPtr
);
153 if (argArray
!= NULL
) {
154 ckfree((char *) argArray
);
160 *----------------------------------------------------------------------
164 * Given a description of a procedure frame, such as the first
165 * argument to an "uplevel" or "upvar" command, locate the
166 * call frame for the appropriate level of procedure.
169 * The return value is -1 if an error occurred in finding the
170 * frame (in this case an error message is left in interp->result).
171 * 1 is returned if string was either a number or a number preceded
172 * by "#" and it specified a valid frame. 0 is returned if string
173 * isn't one of the two things above (in this case, the lookup
174 * acts as if string were "1"). The variable pointed to by
175 * framePtrPtr is filled in with the address of the desired frame
176 * (unless an error occurs, in which case it isn't modified).
181 *----------------------------------------------------------------------
185 TclGetFrame(interp
, string
, framePtrPtr
)
186 Tcl_Interp
*interp
; /* Interpreter in which to find frame. */
187 char *string
; /* String describing frame. */
188 CallFrame
**framePtrPtr
; /* Store pointer to frame here (or NULL
189 * if global frame indicated). */
191 register Interp
*iPtr
= (Interp
*) interp
;
195 if (iPtr
->varFramePtr
== NULL
) {
196 iPtr
->result
= "already at top level";
201 * Parse string to figure out which level number to go to.
205 if (*string
== '#') {
206 if (Tcl_GetInt(interp
, string
+1, &level
) != TCL_OK
) {
211 Tcl_AppendResult(interp
, "bad level \"", string
, "\"",
215 } else if (isdigit(*string
)) {
216 if (Tcl_GetInt(interp
, string
, &level
) != TCL_OK
) {
219 level
= iPtr
->varFramePtr
->level
- level
;
221 level
= iPtr
->varFramePtr
->level
- 1;
226 * Figure out which frame to use, and modify the interpreter so
227 * its variables come from that frame.
233 for (framePtr
= iPtr
->varFramePtr
; framePtr
!= NULL
;
234 framePtr
= framePtr
->callerVarPtr
) {
235 if (framePtr
->level
== level
) {
239 if (framePtr
== NULL
) {
243 *framePtrPtr
= framePtr
;
248 *----------------------------------------------------------------------
252 * This procedure is invoked to process the "uplevel" Tcl command.
253 * See the user documentation for details on what it does.
256 * A standard Tcl result value.
259 * See the user documentation.
261 *----------------------------------------------------------------------
266 Tcl_UplevelCmd(dummy
, interp
, argc
, argv
)
267 ClientData dummy
; /* Not used. */
268 Tcl_Interp
*interp
; /* Current interpreter. */
269 int argc
; /* Number of arguments. */
270 char **argv
; /* Argument strings. */
272 register Interp
*iPtr
= (Interp
*) interp
;
274 CallFrame
*savedVarFramePtr
, *framePtr
;
278 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
279 " ?level? command ?command ...?\"", (char *) NULL
);
284 * Find the level to use for executing the command.
287 result
= TclGetFrame(interp
, argv
[1], &framePtr
);
295 * Modify the interpreter state to execute in the given frame.
298 savedVarFramePtr
= iPtr
->varFramePtr
;
299 iPtr
->varFramePtr
= framePtr
;
302 * Execute the residual arguments as a command.
309 result
= Tcl_Eval(interp
, argv
[0], 0, (char **) NULL
);
313 cmd
= Tcl_Concat(argc
, argv
);
314 result
= Tcl_Eval(interp
, cmd
, 0, (char **) NULL
);
317 if (result
== TCL_ERROR
) {
319 sprintf(msg
, "\n (\"uplevel\" body line %d)", interp
->errorLine
);
320 Tcl_AddErrorInfo(interp
, msg
);
324 * Restore the variable frame, and return.
327 iPtr
->varFramePtr
= savedVarFramePtr
;
332 *----------------------------------------------------------------------
336 * Given the name of a procedure, return a pointer to the
337 * record describing the procedure.
340 * NULL is returned if the name doesn't correspond to any
341 * procedure. Otherwise the return value is a pointer to
342 * the procedure's record.
347 *----------------------------------------------------------------------
351 TclFindProc(iPtr
, procName
)
352 Interp
*iPtr
; /* Interpreter in which to look. */
353 char *procName
; /* Name of desired procedure. */
358 hPtr
= Tcl_FindHashEntry(&iPtr
->commandTable
, procName
);
362 cmdPtr
= (Command
*) Tcl_GetHashValue(hPtr
);
363 if (cmdPtr
->proc
!= InterpProc
) {
366 return (Proc
*) cmdPtr
->clientData
;
370 *----------------------------------------------------------------------
374 * Tells whether a command is a Tcl procedure or not.
377 * If the given command is actuall a Tcl procedure, the
378 * return value is the address of the record describing
379 * the procedure. Otherwise the return value is 0.
384 *----------------------------------------------------------------------
389 Command
*cmdPtr
; /* Command to test. */
391 if (cmdPtr
->proc
== InterpProc
) {
392 return (Proc
*) cmdPtr
->clientData
;
398 *----------------------------------------------------------------------
402 * When a Tcl procedure gets invoked, this routine gets invoked
403 * to interpret the procedure.
406 * A standard Tcl result value, usually TCL_OK.
409 * Depends on the commands in the procedure.
411 *----------------------------------------------------------------------
415 InterpProc(clientData
, interp
, argc
, argv
)
416 ClientData clientData
; /* Record describing procedure to be
418 Tcl_Interp
*interp
; /* Interpreter in which procedure was
420 int argc
; /* Count of number of arguments to this
422 char **argv
; /* Argument values. */
424 register Proc
*procPtr
= (Proc
*) clientData
;
425 register Arg
*argPtr
;
426 register Interp
*iPtr
= (Interp
*) interp
;
433 * Set up a call frame for the new procedure invocation.
436 iPtr
= procPtr
->iPtr
;
437 Tcl_InitHashTable(&frame
.varTable
, TCL_STRING_KEYS
);
438 if (iPtr
->varFramePtr
!= NULL
) {
439 frame
.level
= iPtr
->varFramePtr
->level
+ 1;
445 frame
.callerPtr
= iPtr
->framePtr
;
446 frame
.callerVarPtr
= iPtr
->varFramePtr
;
447 iPtr
->framePtr
= &frame
;
448 iPtr
->varFramePtr
= &frame
;
451 * Match the actual arguments against the procedure's formal
452 * parameters to compute local variables.
455 for (argPtr
= procPtr
->argPtr
, args
= argv
+1, argc
-= 1;
457 argPtr
= argPtr
->nextPtr
, args
++, argc
--) {
460 * Handle the special case of the last formal being "args". When
461 * it occurs, assign it a list consisting of all the remaining
465 if ((argPtr
->nextPtr
== NULL
)
466 && (strcmp(argPtr
->name
, "args") == 0)) {
470 value
= Tcl_Merge(argc
, args
);
471 Tcl_SetVar(interp
, argPtr
->name
, value
, 0);
475 } else if (argc
> 0) {
477 } else if (argPtr
->defValue
!= NULL
) {
478 value
= argPtr
->defValue
;
480 Tcl_AppendResult(interp
, "no value given for parameter \"",
481 argPtr
->name
, "\" to \"", argv
[0], "\"",
486 Tcl_SetVar(interp
, argPtr
->name
, value
, 0);
489 Tcl_AppendResult(interp
, "called \"", argv
[0],
490 "\" with too many arguments", (char *) NULL
);
496 * Invoke the commands in the procedure's body.
499 result
= Tcl_Eval(interp
, procPtr
->command
, 0, &end
);
500 if (result
== TCL_RETURN
) {
502 } else if (result
== TCL_ERROR
) {
506 * Record information telling where the error occurred.
509 sprintf(msg
, "\n (procedure \"%.50s\" line %d)", argv
[0],
511 Tcl_AddErrorInfo(interp
, msg
);
512 } else if (result
== TCL_BREAK
) {
513 iPtr
->result
= "invoked \"break\" outside of a loop";
515 } else if (result
== TCL_CONTINUE
) {
516 iPtr
->result
= "invoked \"continue\" outside of a loop";
521 * Delete the call frame for this procedure invocation (it's
522 * important to remove the call frame from the interpreter
523 * before deleting it, so that traces invoked during the
524 * deletion don't see the partially-deleted frame).
528 iPtr
->framePtr
= frame
.callerPtr
;
529 iPtr
->varFramePtr
= frame
.callerVarPtr
;
530 TclDeleteVars(iPtr
, &frame
.varTable
);
535 *----------------------------------------------------------------------
539 * This procedure is invoked just before a command procedure is
540 * removed from an interpreter. Its job is to release all the
541 * resources allocated to the procedure.
549 *----------------------------------------------------------------------
553 ProcDeleteProc(clientData
)
554 ClientData clientData
; /* Procedure to be deleted. */
556 register Proc
*procPtr
= (Proc
*) clientData
;
557 register Arg
*argPtr
;
559 ckfree((char *) procPtr
->command
);
560 for (argPtr
= procPtr
->argPtr
; argPtr
!= NULL
; ) {
561 Arg
*nextPtr
= argPtr
->nextPtr
;
563 ckfree((char *) argPtr
);
566 ckfree((char *) procPtr
);