1 /*
2 * tclTestProcBodyObj.c --
3 *
4 * Implements the "procbodytest" package, which contains commands
5 * to test creation of Tcl procedures whose body argument is a
6 * Tcl_Obj of type "procbody" rather than a string.
7 *
8 * Copyright (c) 1998 by Scriptics Corporation.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tclTestProcBodyObj.c,v 1.2 1998/11/10 06:54:44 jingham Exp $
14 */
15
16 #include "tclInt.h"
17
18 /*
19 * name and version of this package
20 */
21
22 static char packageName[] = "procbodytest";
23 static char packageVersion[] = "1.0";
24
25 /*
26 * Name of the commands exported by this package
27 */
28
29 static char procCommand[] = "proc";
30
31 /*
32 * this struct describes an entry in the table of command names and command
33 * procs
34 */
35
36 typedef struct CmdTable
37 {
38 char *cmdName; /* command name */
39 Tcl_ObjCmdProc *proc; /* command proc */
40 int exportIt; /* if 1, export the command */
41 } CmdTable;
42
43 /*
44 * Declarations for functions defined in this file.
45 */
46
47 static int ProcBodyTestProcObjCmd _ANSI_ARGS_((ClientData dummy,
48 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
49 static int ProcBodyTestInitInternal _ANSI_ARGS_((Tcl_Interp *interp,
50 int isSafe));
51 static int RegisterCommand _ANSI_ARGS_((Tcl_Interp* interp,
52 char *namespace, CONST CmdTable *cmdTablePtr));
53 int Procbodytest_Init _ANSI_ARGS_((Tcl_Interp * interp));
54 int Procbodytest_SafeInit _ANSI_ARGS_((Tcl_Interp * interp));
55
56 /*
57 * List of commands to create when the package is loaded; must go after the
58 * declarations of the enable command procedure.
59 */
60
61 static CONST CmdTable commands[] =
62 {
63 { procCommand, ProcBodyTestProcObjCmd, 1 },
64
65 { 0, 0, 0 }
66 };
67
68 static CONST CmdTable safeCommands[] =
69 {
70 { procCommand, ProcBodyTestProcObjCmd, 1 },
71
72 { 0, 0, 0 }
73 };
74
75 /*
76 *----------------------------------------------------------------------
77 *
78 * Procbodytest_Init --
79 *
80 * This procedure initializes the "procbodytest" package.
81 *
82 * Results:
83 * A standard Tcl result.
84 *
85 * Side effects:
86 * None.
87 *
88 *----------------------------------------------------------------------
89 */
90
91 int
Procbodytest_Init(interp)92 Procbodytest_Init(interp)
93 Tcl_Interp *interp; /* the Tcl interpreter for which the package
94 * is initialized */
95 {
96 return ProcBodyTestInitInternal(interp, 0);
97 }
98
99 /*
100 *----------------------------------------------------------------------
101 *
102 * Procbodytest_SafeInit --
103 *
104 * This procedure initializes the "procbodytest" package.
105 *
106 * Results:
107 * A standard Tcl result.
108 *
109 * Side effects:
110 * None.
111 *
112 *----------------------------------------------------------------------
113 */
114
115 int
Procbodytest_SafeInit(interp)116 Procbodytest_SafeInit(interp)
117 Tcl_Interp *interp; /* the Tcl interpreter for which the package
118 * is initialized */
119 {
120 return ProcBodyTestInitInternal(interp, 1);
121 }
122
123 /*
124 *----------------------------------------------------------------------
125 *
126 * RegisterCommand --
127 *
128 * This procedure registers a command in the context of the given namespace.
129 *
130 * Results:
131 * A standard Tcl result.
132 *
133 * Side effects:
134 * None.
135 *
136 *----------------------------------------------------------------------
137 */
138
RegisterCommand(interp,namespace,cmdTablePtr)139 static int RegisterCommand(interp, namespace, cmdTablePtr)
140 Tcl_Interp* interp; /* the Tcl interpreter for which the
141 * operation is performed */
142 char *namespace; /* the namespace in which the command
143 * is registered */
144 CONST CmdTable *cmdTablePtr; /* the command to register */
145 {
146 char buf[128];
147
148 if (cmdTablePtr->exportIt) {
149 sprintf(buf, "namespace eval %s { namespace export %s }",
150 namespace, cmdTablePtr->cmdName);
151 if (Tcl_Eval(interp, buf) != TCL_OK)
152 return TCL_ERROR;
153 }
154
155 sprintf(buf, "%s::%s", namespace, cmdTablePtr->cmdName);
156 Tcl_CreateObjCommand(interp, buf, cmdTablePtr->proc, 0, 0);
157
158 return TCL_OK;
159 }
160
161 /*
162 *----------------------------------------------------------------------
163 *
164 * ProcBodyTestInitInternal --
165 *
166 * This procedure initializes the Loader package.
167 * The isSafe flag is 1 if the interpreter is safe, 0 otherwise.
168 *
169 * Results:
170 * A standard Tcl result.
171 *
172 * Side effects:
173 * None.
174 *
175 *----------------------------------------------------------------------
176 */
177
178 static int
ProcBodyTestInitInternal(interp,isSafe)179 ProcBodyTestInitInternal(interp, isSafe)
180 Tcl_Interp *interp; /* the Tcl interpreter for which the package
181 * is initialized */
182 int isSafe; /* 1 if this is a safe interpreter */
183 {
184 CONST CmdTable *cmdTablePtr;
185
186 cmdTablePtr = (isSafe) ? &safeCommands[0] : &commands[0];
187 for ( ; cmdTablePtr->cmdName ; cmdTablePtr++) {
188 if (RegisterCommand(interp, packageName, cmdTablePtr) != TCL_OK) {
189 return TCL_ERROR;
190 }
191 }
192
193 return Tcl_PkgProvide(interp, packageName, packageVersion);
194 }
195
196 /*
197 *----------------------------------------------------------------------
198 *
199 * ProcBodyTestProcObjCmd --
200 *
201 * Implements the "procbodytest::proc" command. Here is the command
202 * description:
203 * procbodytest::proc newName argList bodyName
204 * Looks up a procedure called $bodyName and, if the procedure exists,
205 * constructs a Tcl_Obj of type "procbody" and calls Tcl_ProcObjCmd.
206 * Arguments:
207 * newName the name of the procedure to be created
208 * argList the argument list for the procedure
209 * bodyName the name of an existing procedure from which the
210 * body is to be copied.
211 * This command can be used to trigger the branches in Tcl_ProcObjCmd that
212 * construct a proc from a "procbody", for example:
213 * proc a {x} {return $x}
214 * a 123
215 * procbodytest::proc b {x} a
216 * Note the call to "a 123", which is necessary so that the Proc pointer
217 * for "a" is filled in by the internal compiler; this is a hack.
218 *
219 * Results:
220 * Returns a standard Tcl code.
221 *
222 * Side effects:
223 * A new procedure is created.
224 * Leaves an error message in the interp's result on error.
225 *
226 *----------------------------------------------------------------------
227 */
228
229 static int
ProcBodyTestProcObjCmd(dummy,interp,objc,objv)230 ProcBodyTestProcObjCmd (dummy, interp, objc, objv)
231 ClientData dummy; /* context; not used */
232 Tcl_Interp *interp; /* the current interpreter */
233 int objc; /* argument count */
234 Tcl_Obj *CONST objv[]; /* arguments */
235 {
236 char *fullName;
237 Tcl_Command procCmd;
238 Command *cmdPtr;
239 Proc *procPtr = (Proc *) NULL;
240 Tcl_Obj *bodyObjPtr;
241 Tcl_Obj *myobjv[5];
242 int result;
243
244 if (objc != 4) {
245 Tcl_WrongNumArgs(interp, 1, objv, "newName argsList bodyName");
246 return TCL_ERROR;
247 }
248
249 /*
250 * Find the Command pointer to this procedure
251 */
252
253 fullName = Tcl_GetStringFromObj(objv[3], (int *) NULL);
254 procCmd = Tcl_FindCommand(interp, fullName, (Tcl_Namespace *) NULL,
255 TCL_LEAVE_ERR_MSG);
256 if (procCmd == NULL) {
257 return TCL_ERROR;
258 }
259
260 cmdPtr = (Command *) procCmd;
261
262 /*
263 * check that this is a procedure and not a builtin command:
264 * If a procedure, cmdPtr->objProc is either 0 or TclObjInterpProc,
265 * and cmdPtr->proc is either 0 or TclProcInterpProc.
266 * Also, the compile proc should be 0, but we don't check for that.
267 */
268
269 if (((cmdPtr->objProc != NULL)
270 && (cmdPtr->objProc != TclGetObjInterpProc()))
271 || ((cmdPtr->proc != NULL)
272 && (cmdPtr->proc != TclGetInterpProc()))) {
273 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
274 "command \"", fullName,
275 "\" is not a Tcl procedure", (char *) NULL);
276 return TCL_ERROR;
277 }
278
279 /*
280 * it is a Tcl procedure: the client data is the Proc structure
281 */
282
283 if (cmdPtr->objProc != NULL) {
284 procPtr = (Proc *) cmdPtr->objClientData;
285 } else if (cmdPtr->proc != NULL) {
286 procPtr = (Proc *) cmdPtr->clientData;
287 }
288
289 if (procPtr == NULL) {
290 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
291 "procedure \"", fullName,
292 "\" does not have a Proc struct!", (char *) NULL);
293 return TCL_ERROR;
294 }
295
296 /*
297 * create a new object, initialize our argument vector, call into Tcl
298 */
299
300 bodyObjPtr = TclNewProcBodyObj(procPtr);
301 if (bodyObjPtr == NULL) {
302 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
303 "failed to create a procbody object for procedure \"",
304 fullName, "\"", (char *) NULL);
305 return TCL_ERROR;
306 }
307 Tcl_IncrRefCount(bodyObjPtr);
308
309 myobjv[0] = objv[0];
310 myobjv[1] = objv[1];
311 myobjv[2] = objv[2];
312 myobjv[3] = bodyObjPtr;
313 myobjv[4] = (Tcl_Obj *) NULL;
314
315 result = Tcl_ProcObjCmd((ClientData) NULL, interp, objc, myobjv);
316 Tcl_DecrRefCount(bodyObjPtr);
317
318 return result;
319 }
320