]>
cvs.zerfleddert.de Git - micropolis/blob - src/tcl/tclproc.c
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 *----------------------------------------------------------------------
51 ClientData dummy
, /* Not used. */
52 Tcl_Interp
*interp
, /* Current interpreter. */
53 int argc
, /* Number of arguments. */
54 char **argv
/* Argument strings. */
57 register Interp
*iPtr
= (Interp
*) interp
;
58 register Proc
*procPtr
;
59 int result
, argCount
, i
;
60 char **argArray
= NULL
;
62 register Arg
*argPtr
= NULL
; /* Initialization not needed, but
63 * prevents compiler warning. */
66 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
67 " name args body\"", (char *) NULL
);
71 procPtr
= (Proc
*) ckalloc(sizeof(Proc
));
73 procPtr
->command
= (char *) ckalloc((unsigned) strlen(argv
[3]) + 1);
74 strcpy(procPtr
->command
, argv
[3]);
75 procPtr
->argPtr
= NULL
;
78 * Break up the argument list into argument specifiers, then process
79 * each argument specifier.
82 result
= Tcl_SplitList(interp
, argv
[2], &argCount
, &argArray
);
83 if (result
!= TCL_OK
) {
87 for (i
= 0; i
< argCount
; i
++) {
88 int fieldCount
, nameLength
, valueLength
;
92 * Now divide the specifier up into name and default.
95 result
= Tcl_SplitList(interp
, argArray
[i
], &fieldCount
,
97 if (result
!= TCL_OK
) {
100 if (fieldCount
> 2) {
101 ckfree((char *) fieldValues
);
102 Tcl_AppendResult(interp
,
103 "too many fields in argument specifier \"",
104 argArray
[i
], "\"", (char *) NULL
);
108 if ((fieldCount
== 0) || (*fieldValues
[0] == 0)) {
109 ckfree((char *) fieldValues
);
110 Tcl_AppendResult(interp
, "procedure \"", argv
[1],
111 "\" has argument with no name", (char *) NULL
);
115 nameLength
= strlen(fieldValues
[0]) + 1;
116 if (fieldCount
== 2) {
117 valueLength
= strlen(fieldValues
[1]) + 1;
121 argPtr
= (Arg
*) ckalloc((unsigned)
122 (sizeof(Arg
) - sizeof(argPtr
->name
) + nameLength
124 if (lastArgPtr
== NULL
) {
125 procPtr
->argPtr
= argPtr
;
127 lastArgPtr
->nextPtr
= argPtr
;
130 argPtr
->nextPtr
= NULL
;
131 strcpy(argPtr
->name
, fieldValues
[0]);
132 if (fieldCount
== 2) {
133 argPtr
->defValue
= argPtr
->name
+ nameLength
;
134 strcpy(argPtr
->defValue
, fieldValues
[1]);
136 argPtr
->defValue
= NULL
;
138 ckfree((char *) fieldValues
);
141 Tcl_CreateCommand(interp
, argv
[1], InterpProc
, (ClientData
) procPtr
,
143 ckfree((char *) argArray
);
147 ckfree(procPtr
->command
);
148 while (procPtr
->argPtr
!= NULL
) {
149 argPtr
= procPtr
->argPtr
;
150 procPtr
->argPtr
= argPtr
->nextPtr
;
151 ckfree((char *) argPtr
);
153 ckfree((char *) procPtr
);
154 if (argArray
!= NULL
) {
155 ckfree((char *) argArray
);
161 *----------------------------------------------------------------------
165 * Given a description of a procedure frame, such as the first
166 * argument to an "uplevel" or "upvar" command, locate the
167 * call frame for the appropriate level of procedure.
170 * The return value is -1 if an error occurred in finding the
171 * frame (in this case an error message is left in interp->result).
172 * 1 is returned if string was either a number or a number preceded
173 * by "#" and it specified a valid frame. 0 is returned if string
174 * isn't one of the two things above (in this case, the lookup
175 * acts as if string were "1"). The variable pointed to by
176 * framePtrPtr is filled in with the address of the desired frame
177 * (unless an error occurs, in which case it isn't modified).
182 *----------------------------------------------------------------------
187 Tcl_Interp
*interp
, /* Interpreter in which to find frame. */
188 char *string
, /* String describing frame. */
189 CallFrame
**framePtrPtr
/* Store pointer to frame here (or NULL
190 * if global frame indicated). */
193 register Interp
*iPtr
= (Interp
*) interp
;
197 if (iPtr
->varFramePtr
== NULL
) {
198 iPtr
->result
= "already at top level";
203 * Parse string to figure out which level number to go to.
207 if (*string
== '#') {
208 if (Tcl_GetInt(interp
, string
+1, &level
) != TCL_OK
) {
213 Tcl_AppendResult(interp
, "bad level \"", string
, "\"",
217 } else if (isdigit(*string
)) {
218 if (Tcl_GetInt(interp
, string
, &level
) != TCL_OK
) {
221 level
= iPtr
->varFramePtr
->level
- level
;
223 level
= iPtr
->varFramePtr
->level
- 1;
228 * Figure out which frame to use, and modify the interpreter so
229 * its variables come from that frame.
235 for (framePtr
= iPtr
->varFramePtr
; framePtr
!= NULL
;
236 framePtr
= framePtr
->callerVarPtr
) {
237 if (framePtr
->level
== level
) {
241 if (framePtr
== NULL
) {
245 *framePtrPtr
= framePtr
;
250 *----------------------------------------------------------------------
254 * This procedure is invoked to process the "uplevel" Tcl command.
255 * See the user documentation for details on what it does.
258 * A standard Tcl result value.
261 * See the user documentation.
263 *----------------------------------------------------------------------
269 ClientData dummy
, /* Not used. */
270 Tcl_Interp
*interp
, /* Current interpreter. */
271 int argc
, /* Number of arguments. */
272 char **argv
/* Argument strings. */
275 register Interp
*iPtr
= (Interp
*) interp
;
277 CallFrame
*savedVarFramePtr
, *framePtr
;
281 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
282 " ?level? command ?command ...?\"", (char *) NULL
);
287 * Find the level to use for executing the command.
290 result
= TclGetFrame(interp
, argv
[1], &framePtr
);
298 * Modify the interpreter state to execute in the given frame.
301 savedVarFramePtr
= iPtr
->varFramePtr
;
302 iPtr
->varFramePtr
= framePtr
;
305 * Execute the residual arguments as a command.
312 result
= Tcl_Eval(interp
, argv
[0], 0, (char **) NULL
);
316 cmd
= Tcl_Concat(argc
, argv
);
317 result
= Tcl_Eval(interp
, cmd
, 0, (char **) NULL
);
320 if (result
== TCL_ERROR
) {
322 sprintf(msg
, "\n (\"uplevel\" body line %d)", interp
->errorLine
);
323 Tcl_AddErrorInfo(interp
, msg
);
327 * Restore the variable frame, and return.
330 iPtr
->varFramePtr
= savedVarFramePtr
;
335 *----------------------------------------------------------------------
339 * Given the name of a procedure, return a pointer to the
340 * record describing the procedure.
343 * NULL is returned if the name doesn't correspond to any
344 * procedure. Otherwise the return value is a pointer to
345 * the procedure's record.
350 *----------------------------------------------------------------------
355 Interp
*iPtr
, /* Interpreter in which to look. */
356 char *procName
/* Name of desired procedure. */
362 hPtr
= Tcl_FindHashEntry(&iPtr
->commandTable
, procName
);
366 cmdPtr
= (Command
*) Tcl_GetHashValue(hPtr
);
367 if (cmdPtr
->proc
!= InterpProc
) {
370 return (Proc
*) cmdPtr
->clientData
;
374 *----------------------------------------------------------------------
378 * Tells whether a command is a Tcl procedure or not.
381 * If the given command is actuall a Tcl procedure, the
382 * return value is the address of the record describing
383 * the procedure. Otherwise the return value is 0.
388 *----------------------------------------------------------------------
393 Command
*cmdPtr
/* Command to test. */
396 if (cmdPtr
->proc
== InterpProc
) {
397 return (Proc
*) cmdPtr
->clientData
;
403 *----------------------------------------------------------------------
407 * When a Tcl procedure gets invoked, this routine gets invoked
408 * to interpret the procedure.
411 * A standard Tcl result value, usually TCL_OK.
414 * Depends on the commands in the procedure.
416 *----------------------------------------------------------------------
421 ClientData clientData
, /* Record describing procedure to be
423 Tcl_Interp
*interp
, /* Interpreter in which procedure was
425 int argc
, /* Count of number of arguments to this
427 char **argv
/* Argument values. */
430 register Proc
*procPtr
= (Proc
*) clientData
;
431 register Arg
*argPtr
;
432 register Interp
*iPtr
= (Interp
*) interp
;
439 * Set up a call frame for the new procedure invocation.
442 iPtr
= procPtr
->iPtr
;
443 Tcl_InitHashTable(&frame
.varTable
, TCL_STRING_KEYS
);
444 if (iPtr
->varFramePtr
!= NULL
) {
445 frame
.level
= iPtr
->varFramePtr
->level
+ 1;
451 frame
.callerPtr
= iPtr
->framePtr
;
452 frame
.callerVarPtr
= iPtr
->varFramePtr
;
453 iPtr
->framePtr
= &frame
;
454 iPtr
->varFramePtr
= &frame
;
457 * Match the actual arguments against the procedure's formal
458 * parameters to compute local variables.
461 for (argPtr
= procPtr
->argPtr
, args
= argv
+1, argc
-= 1;
463 argPtr
= argPtr
->nextPtr
, args
++, argc
--) {
466 * Handle the special case of the last formal being "args". When
467 * it occurs, assign it a list consisting of all the remaining
471 if ((argPtr
->nextPtr
== NULL
)
472 && (strcmp(argPtr
->name
, "args") == 0)) {
476 value
= Tcl_Merge(argc
, args
);
477 Tcl_SetVar(interp
, argPtr
->name
, value
, 0);
481 } else if (argc
> 0) {
483 } else if (argPtr
->defValue
!= NULL
) {
484 value
= argPtr
->defValue
;
486 Tcl_AppendResult(interp
, "no value given for parameter \"",
487 argPtr
->name
, "\" to \"", argv
[0], "\"",
492 Tcl_SetVar(interp
, argPtr
->name
, value
, 0);
495 Tcl_AppendResult(interp
, "called \"", argv
[0],
496 "\" with too many arguments", (char *) NULL
);
502 * Invoke the commands in the procedure's body.
505 result
= Tcl_Eval(interp
, procPtr
->command
, 0, &end
);
506 if (result
== TCL_RETURN
) {
508 } else if (result
== TCL_ERROR
) {
512 * Record information telling where the error occurred.
515 sprintf(msg
, "\n (procedure \"%.50s\" line %d)", argv
[0],
517 Tcl_AddErrorInfo(interp
, msg
);
518 } else if (result
== TCL_BREAK
) {
519 iPtr
->result
= "invoked \"break\" outside of a loop";
521 } else if (result
== TCL_CONTINUE
) {
522 iPtr
->result
= "invoked \"continue\" outside of a loop";
527 * Delete the call frame for this procedure invocation (it's
528 * important to remove the call frame from the interpreter
529 * before deleting it, so that traces invoked during the
530 * deletion don't see the partially-deleted frame).
534 iPtr
->framePtr
= frame
.callerPtr
;
535 iPtr
->varFramePtr
= frame
.callerVarPtr
;
536 TclDeleteVars(iPtr
, &frame
.varTable
);
541 *----------------------------------------------------------------------
545 * This procedure is invoked just before a command procedure is
546 * removed from an interpreter. Its job is to release all the
547 * resources allocated to the procedure.
555 *----------------------------------------------------------------------
560 ClientData clientData
/* Procedure to be deleted. */
563 register Proc
*procPtr
= (Proc
*) clientData
;
564 register Arg
*argPtr
;
566 ckfree((char *) procPtr
->command
);
567 for (argPtr
= procPtr
->argPtr
; argPtr
!= NULL
; ) {
568 Arg
*nextPtr
= argPtr
->nextPtr
;
570 ckfree((char *) argPtr
);
573 ckfree((char *) procPtr
);