From: <jul...@pr...> - 2024-08-18 16:17:11
|
Hi All, There are occasionally requests for how to drop into the tclsh repl from a script - perhaps after an error for debugging. The usual advice seems to be to create a small repl in script. This is at least inconvenient and probably not compatible with uparrow history etc(?) The first inclination probably is to play with ::tcl_interactive to see if that will help - but it doesn't. In Tcl_MainEx - ::tcl_interactive is mapped via Tcl_Linkvar to the is.tty c value - but unfortunately (namewise at least) this isn't a direct correspondence to what people would consider isatty. This is because in Tcl it is set to zero when a script is run (as documented) - even though (when not part of a pipeline) there can still be a terminal. As I also have a usecase for dropping into the *real* repl - I've developed a patch for tclMain.c to experiment with this. (developed against latest tcl9) I'm referring to this patch project as: piperepl It is only active if ::env(TCLSH_PIPEREPL) is set true. (with minor effects re tclsh array noted below) It uses some linked variables in a global ::tclsh array to allow a script to very simply set whether the repl should run after the script. (and possibly whether stdin should be evaluated as commands) In the standard 'tclsh <script>' case It does this by dropping through to the normal repl code, and in the case of a pipeline, by reopening stdin on the console after we get eof on stdin. For example error.tcl - the first 2 lines enable the behaviour. ------- error.tcl ------- set ::tclsh(dorepl) 1 set ::tcl_interactive 1 ;#Not required - but not as pretty without it set myval whatever error "something is wrong" -------------------------- With ::env(TCLSH_PIPEREPL) = 1 you can now drop straight into the repl and examine variables errors etc. If you use: printf "puts test" | tclsh Like standard tclsh it will just evaluate "puts test" printf "set ::tclsh(dorepl) 1; puts test" | tclsh will evaluate the script from stdin and also drop into the repl - but unless you also set ::tcl_interactive 1 in the printf statement - you'll get it without prompts. For security reasons.. (and consistency with standard tclsh) printf "puts test" | tclsh <script> will not evaluate the printf script from stdin - and in this case you can't of course influence it with ::tclsh(dorepl) in the piped in value This makes sense because the data being piped in to a script shouldn't be assumed to be commands for evaluation anyway The data on stdin may or may not be read by the script - but any remaining unconsumed is currently just stored in ::tclsh(inputbuffer) A script can decide it does want the stdin from the pipeline to be evaled by setting ::tclsh(evalinput) (but there is deliberately no way to set that from the stdin pipeline in this case) In the above error.tcl case - it may be inconvienent to have tclsh(dorepl) true and always drop into the shell By setting tclsh(evalinput) 1 in the .tcl script instead of tclsh(dorepl) - you can run tclsh error.tcl in the normal terminating manner - and call it as printf "set tclsh(dorepl) 1; set tcl_interactive 1" | tclsh error.tcl when you want the full repl. I believe the result is that even with the TCLSH_PIPEREPL environment variable enabled - when the tclsh(x) vars aren't set in scripts it should be consistent with standard tclsh - aside from creating the global ::tclsh array Which I understand may be an issue - and am open to renaming/namespacing if that is possible - although I think it's quite reasonable for the functionality. The tclsh(istty) variable is I think a useful addition at the script level anyway - as tcl_interactive doesn't serve this purpose. Only tested on windows (but when reopening to console, a line to open /dev/tty is included when not on windows - so maybe that works?) Is anyone interested in this feature - and does anyone here have any comments regarding feasibility/desirability for something like this to be included in future? I believe, but haven't tested; that it shouldn't have any impact on Tk startup or other custom startup but note that I'm not experienced with c programming or Tcl internals. I've read about refcounts and various bits and pieces - and tried to use examples from elsewhere in the source to get close to doing the right thing - but it definitely needs scrutiny Code review/comments would be most welcome even if this doesn't look like a candidate for inclusion in Tcl proper. There is a somewhat ugly debugging message in the attached patch to show when console reopen is about to happen - left in for evaluation purposes show what it's doing. I've tried to use the same tab/space conventions as the rest of the file - but I'm not entirely sure I got that right. Patch made from cmd.exe with diff -u --binary tclMain.c tclMain_piperepl.c lineendings are unix lf Cheers, Julian (JMN on wiki) |