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