1 /* $LAAS: loop.c,v 1.11 2005/10/04 07:25:12 matthieu Exp $ */
4 * Copyright (c) 2001-2004 LAAS/CNRS -- Tue Oct 16 2001
5 * All rights reserved. Anthony Mallet
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
42 char *avt_gettcldistpath();
44 static jmp_buf interactive_exit
;
46 //static char copyright[] = " - Copyright (C) 2001-2005 LAAS-CNRS";
47 //static char *version = ELTCLSH_VERSION;
49 avt_interactive_exit()
51 longjmp(interactive_exit
, 1);
55 * elTclshLoop ----------------------------------------------------------
57 * Main loop: it reads commands and execute them
60 #if TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4
62 elTclshLoop(int argc
, const char **argv
, ElTclAppInitProc appInitProc
)
65 elTclshLoop(int argc
, char **argv
, ElTclAppInitProc appInitProc
)
66 #endif /* TCL_VERSION */
68 ElTclInterpInfo
*iinfo
;
71 Tcl_Obj
*resultPtr
, *command
;
73 #if TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4
74 const char *fileName
, *args
;
75 const char *eltclLibrary
[2];
78 char *fileName
, *args
;
79 char *eltclLibrary
[2];
81 #endif /* TCL_VERSION */
83 char buffer
[1000], *bytes
;
84 int code
, tty
, length
;
87 Tcl_Channel inChannel
, outChannel
, errChannel
;
89 /* create main data structure */
90 iinfo
= calloc(1, sizeof(*iinfo
));
92 fputs("cannot alloc %d bytes\n", stderr
);
96 /* initialize interpreter */
97 iinfo
->interp
= Tcl_CreateInterp();
98 if (iinfo
->interp
== NULL
) {
99 fputs("cannot create tcl interpreter\n", stderr
);
105 * Make command-line arguments available in the Tcl variables "argc"
106 * and "argv". If the first argument doesn't start with a "-" then
107 * strip it off and use it as the name of a script file to process.
111 if ((argc
> 1) && (argv
[1][0] != '-')) {
116 args
= Tcl_Merge(argc
- 1, argv
+ 1);
117 Tcl_SetVar(iinfo
->interp
, "argv", args
, TCL_GLOBAL_ONLY
);
118 Tcl_Free((char *) args
);
119 snprintf(buffer
, sizeof(buffer
), "%d", argc
- 1);
120 Tcl_SetVar(iinfo
->interp
, "argc", buffer
, TCL_GLOBAL_ONLY
);
121 args
= (fileName
!= NULL
) ? fileName
: argv
[0];
122 Tcl_SetVar(iinfo
->interp
, "argv0", args
, TCL_GLOBAL_ONLY
);
123 iinfo
->argv0
= basename((char *) args
);
126 /* Set the "tcl_interactive" variable. */
128 Tcl_SetVar(iinfo
->interp
, "tcl_interactive", ((fileName
== NULL
) && tty
) ? "1" : "0", TCL_GLOBAL_ONLY
);
130 /* Invoke application-specific initialization. */
131 if ((*appInitProc
) (iinfo
) != TCL_OK
) {
132 errChannel
= Tcl_GetStdChannel(TCL_STDERR
);
134 #if TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 4
138 #endif /* TCL_VERSION */
140 msg
= Tcl_GetVar(iinfo
->interp
, "errorInfo", TCL_GLOBAL_ONLY
);
142 Tcl_Write(errChannel
, msg
, strlen(msg
));
143 Tcl_Write(errChannel
, "\n", 1);
145 resultPtr
= Tcl_GetObjResult(iinfo
->interp
);
146 bytes
= Tcl_GetStringFromObj(resultPtr
, &length
);
147 Tcl_Write(errChannel
, bytes
, length
);
148 Tcl_Write(errChannel
, "\n", 1);
155 /* source standard eltclsh libraries */
156 eltclLibrary
[0] = avt_gettcldistpath();
157 if (eltclLibrary
[0] == NULL
) {
158 eltclLibrary
[0] = ".";
160 eltclLibrary
[1] = "el_init.tcl";
161 Tcl_SetVar(iinfo
->interp
, "eltcl_library", eltclLibrary
[0], TCL_GLOBAL_ONLY
);
162 Tcl_DStringInit(&initFile
);
163 if (Tcl_EvalFile(iinfo
->interp
, Tcl_JoinPath(2, eltclLibrary
, &initFile
)) != TCL_OK
) {
164 Tcl_AppendResult(iinfo
->interp
, "\nThe directory ", eltclLibrary
[0], " does not contain a valid ", eltclLibrary
[1], " file.\nPlease check your installation.\n", NULL
);
165 Tcl_DStringFree(&initFile
);
167 errChannel
= Tcl_GetStdChannel(TCL_STDERR
);
169 Tcl_AddErrorInfo(iinfo
->interp
, "");
170 Tcl_Write(errChannel
, Tcl_GetStringResult(iinfo
->interp
), -1);
175 Tcl_DStringFree(&initFile
);
177 (void) Tcl_SourceRCFile(iinfo
->interp
);
178 Tcl_Flush(Tcl_GetStdChannel(TCL_STDERR
));
182 /* If a script file was specified then just source that file and quit. */
183 if (fileName
!= NULL
) {
184 code
= Tcl_EvalFile(iinfo
->interp
, fileName
);
185 if (code
!= TCL_OK
) {
186 errChannel
= Tcl_GetStdChannel(TCL_STDERR
);
188 /* The following statement guarantees that the errorInfo
189 * variable is set properly. */
190 Tcl_AddErrorInfo(iinfo
->interp
, "");
191 Tcl_Write(errChannel
, Tcl_GetVar(iinfo
->interp
, "errorInfo", TCL_GLOBAL_ONLY
), -1);
192 Tcl_Write(errChannel
, "\n", 1);
200 /* Print the copyright message in interactive mode */
202 avt_banner("AvtShell", "Timing & Signal Integrity Analysis Platform", "2000");
206 * Process commands from stdin until there's an end-of-file. Note
207 * that we need to fetch the standard channels again after every
208 * eval, since they may have been changed.
211 iinfo
->command
= Tcl_NewObj();
212 Tcl_IncrRefCount(iinfo
->command
);
214 inChannel
= Tcl_GetStdChannel(TCL_STDIN
);
215 outChannel
= Tcl_GetStdChannel(TCL_STDOUT
);
216 iinfo
->gotPartial
= 0;
218 for (; /* eternity */ ;) {
223 line
= el_gets(iinfo
->el
, &length
);
224 if (line
== NULL
) goto done
;
226 command
= Tcl_NewStringObj(line
, length
);
227 Tcl_AppendObjToObj(iinfo
->command
, command
);
230 /* using libedit but not a tty - must use gets */
231 if (!inChannel
) goto done
;
233 length
= Tcl_GetsObj(inChannel
, iinfo
->command
);
234 if (length
< 0) goto done
;
235 if ((length
== 0) && Tcl_Eof(inChannel
) && (!iinfo
->gotPartial
)) goto done
;
237 /* Add the newline back to the string */
238 Tcl_AppendToObj(iinfo
->command
, "\n", 1);
241 if (!Tcl_CommandComplete(Tcl_GetStringFromObj(iinfo
->command
, &length
))) {
242 iinfo
->gotPartial
= 1;
246 if (tty
&& length
> 1) {
247 /* add the command line to history */
248 history(iinfo
->history
, &ev
, H_ENTER
, Tcl_GetStringFromObj(iinfo
->command
, NULL
));
251 /* tricky: if the command calls el::get[sc], the completion engine
252 * will think that iinfo->command is the beginning of an incomplete
253 * command. Thus we must reset it before the Tcl_Eval call... */
254 command
= iinfo
->command
;
256 iinfo
->command
= Tcl_NewObj();
257 Tcl_IncrRefCount(iinfo
->command
);
259 iinfo
->gotPartial
= 0;
263 if (setjmp(interactive_exit
) == 0) {
264 MBK_EXIT_FUNCTION
= avt_interactive_exit
;
265 code
= Tcl_EvalObj(iinfo
->interp
, command
);
271 else code
= Tcl_EvalObj(iinfo
->interp
, command
);
273 Tcl_DecrRefCount(command
);
275 inChannel
= Tcl_GetStdChannel(TCL_STDIN
);
276 outChannel
= Tcl_GetStdChannel(TCL_STDOUT
);
277 errChannel
= Tcl_GetStdChannel(TCL_STDERR
);
278 if (code
!= TCL_OK
) {
280 resultPtr
= Tcl_GetObjResult(iinfo
->interp
);
281 bytes
= Tcl_GetStringFromObj(resultPtr
, &length
);
282 Tcl_Write(errChannel
, bytes
, length
);
283 Tcl_Write(errChannel
, "\n", 1);
287 resultPtr
= Tcl_GetObjResult(iinfo
->interp
);
288 bytes
= Tcl_GetStringFromObj(resultPtr
, &length
);
290 if ((length
> 0) && outChannel
) {
291 Tcl_Write(outChannel
, bytes
, length
);
292 Tcl_Write(outChannel
, "\n", 1);
295 if (command_exit
) Tcl_Write(outChannel
, "PROCEED WITH CAUTION\n", 21);
299 * Rather than calling exit, invoke the "exit" command so that
300 * users can replace "exit" with some other command to do additional
301 * cleanup on exit. The Tcl_Eval call should never return.
305 if (iinfo
->command
!= NULL
) Tcl_DecrRefCount(iinfo
->command
);
306 snprintf(buffer
, sizeof(buffer
), "exit %d", exitCode
);
307 Tcl_Eval(iinfo
->interp
, buffer
);
312 * elTclExit ------------------------------------------------------------
314 * Destroy global info structure
318 elTclExit(ClientData data
,
319 Tcl_Interp
* interp
, int objc
, Tcl_Obj
* const objv
[])
321 ElTclInterpInfo
*iinfo
= data
;
324 if ((objc
!= 1) && (objc
!= 2)) {
325 Tcl_WrongNumArgs(interp
, 1, objv
, "?returnCode?");
332 else if (Tcl_GetIntFromObj(interp
, objv
[1], &value
) != TCL_OK
) {
337 history_end(iinfo
->history
);
338 history_end(iinfo
->askaHistory
);
340 elTclHandlersExit(iinfo
);
341 Tcl_DecrRefCount(iinfo
->prompt1Name
);
342 Tcl_DecrRefCount(iinfo
->prompt2Name
);
343 Tcl_DecrRefCount(iinfo
->matchesName
);