From AlphaWiki

Pipes: Interaction with TeX under Unix

This page is a mostly technical discussion of interaction with unix implementations of TeX.

For more practical instructions, see the page AlphatkOnOsXWithTexApplications? which also discusses GUI implementations (OzTeX, CMacTeX, Textures, TeXShop).

This page has been superseeded be rapid developments in AlphaTcl --- the alphac script now handles many of the issues previously discussed here.

This page is now dedicated to a discussion of how to incorporate features of tetexComm into standard AlphaTcl.

tetexComm started life as an attempt to provide an interactive tex interface, using pipes instead of exec, as in standard AlphaTcl. Later the xserv-InSh combination has subsumed many of the features related to interactivity. Meanwhile tetexComm has developed many other features, which duplicate --- and according to its author (who is also the original author of this page!) are far superior to --- standard Alpha's tex interface features.

The goal is to see if any of these features or technologies can be incorporated into AlphaTcl and hence

Phases: determination of tex file and format.

tetexComm is not very different from standard alpha tex mode, when we are talking about a frontmost tex file and you invoke the typeset command. But if some sort of console is frontmost, then standard alpha tex tries to find the second-to-frontmost window and use that, while tetexComm has a record of previous tex run and uses that. That record is extremely useful for many things (aux file preservation, re-tex'ing etc) so a suggestion is to implement this notion in standard alpha tex. If the api for pipe based interaction is adopted, this will be quite natural. In any case the second-to-frontmost scheme is very delicate and often leads to unexpected results --- in particular for anybody using cycle-among- windows...

tetexComm has its own way of detecting formats. I don't know what it would take to compatibilise this...

First of all tetexComm has a more sophisticated console than standard alpha tex (which is not interactive at all) and also more sophisticated than xserv-InSh combination ---

tetexComm has error browsing. Hopefully the work of Aaron will lead to a general error browsing functionality that can be used by all tex implementations.

The rest are pieces and bits moved over here from the page [AlphaTcl and helpers]. As of this writing, most of it is rather obsolete and should perhaps rather be deleted.

TEXEDIT issues

One tricky detail with interaction with TeX is that not only must Alpha be able to drive tex --- tex must also be able to call Alpha, if the user presses 'e' at a TeX error to edit. Since Alpha doesn't really have a command line interface this is a bit hard. This problem seems to have been solved by Fr√İd√İric with the AlphaServer.

TeX Forward Search

Incidentally, for those (if any) using Miktex with alpha on a PC, one can do this by including the following line in miktex.ini:

    Editor = c:\alpha\alphatk +"%l" %f


For yap:

    yap.exe -1 -s "%l %n" "%P.dvi"

where n is the name of the source file, and %P is the name of the dvi file (the tex master file). So an example is yap.exe -1 -s 41myfile.tex mainfile.dvi

For xdvi:

    xdvi [-unique] -sourceposition "%l %n" "P.dvi"

These last two Alphatk automatically contains with the 'Synchronize Doc' menu item (Control-`).

Also contains some useful stuff, e.g.

Also this from Joachim and Juan:

(is there any reason this uses 'exec /bin/cp' instead of 'file copy -force', etc?)

NB. The code below, with minor changes, works perfectly on Windows with MikTeX.

NBB. This code is pretty similar to the interactive spell-checker which is used by Alphatk on Windows (in Tcl/SystemCode/CorePackages/spellcheck.tcl). I'm sure we can move some common 'pipe' functionality into AlphaTcl.

NBBB. Why do you have to use '|open /bin/sh'? Can't you just open the actual tex command directly? Any sub-process will inherit Alpha's 'pwd' and $env(PATH) anyway, so those can just be set inside Alpha.

    # This is an fully interactive interface to tetex, based on pipes rather
    # than simply exec: when you tex the current document you get the usual TeX
    # output rolling down line by line in an interactive TeX console inside
    # AlphaX: if there is an error you can type an answer (h, e, q, x, or the
    # name of a not-found input file, etc., which will be processed.  In
    # particular if 'e' is chosen, the error line is opened immediately in your
    # TeX source document.  (Furthermore, in this case (and in the case of
    # 'x'), in an attempt to be smart, the script preserves the previous .aux
    # file, so that in next run you don't get all those missing-refs errors you
    # usually get after an interrupted tex run.)
    # Optionally, upon completion, PDFViewer or MacDviX is called to display
    # the result.  (These are the only applications I know of, that know how to
    # refresh a displayed document when it is updated on disk...)  PDFViewer
    # can be downloaded from
    # MacDviX, the good-old shareware app of Tom Kiffe, can be downloaded from
    # Edit the postProc at the end of this file to choose support for one of
    # these previewers.
    # Adjust the Bind statement below to use another tex programme, e.g.
    # pdflatex or Gerben Wierda's altpdflatex.  It could also be bibtex!
    # In fact you can also put more complicated stuff as argument, like
    # "tex &amstex".
    # Load the code below and type  Cmd-opt-T  in your LaTeX file window.

    # juanfc 2003-08-04
    # TO DO:
    #   A thing I miss is to revert/refresh with the last retypeset file
    #   in the PDFViewer file.  Now you need to close the window in PDFViewer
    #   before switch to Alpha, a stupid duty. If you don't do this, this
    script will not
    #   work so well since you will be switched to PDFViewer but not
    #   seen the last version of your typeseting.
    # I have
    #  1. added the proc    buscaFormato file
    #     that look for the format specification in the first
    #     line of the file
    #  2. unused the direct command specification on typeset proc
    #     replacing it for a call to a smarter proc that
    #       a. uses the format specification found by buscaFormato
    #       b. consider the starting pdf... prefix in that format specification
    #          to use in postProc to select a DVI/PDF viewer :)

    namespace eval TeX::altComm {}

    Bind 't' <co> {TeX::altComm::typeset "pdflatex  "} "TeX"

    # juanfc 2003-08-04
    # look for the file format
    proc TeX::altComm::buscaFormato {f} {
	 set formato ""
	 set p [open "$f" r]
	 set primeraLinea [gets $p]
	 close $p
	 regexp "^%&(.+)$" $primeraLinea primeraLinea formato
	 return $formato

    proc TeX::altComm::cmd {f} {
	 variable isDVI
	 set formato [::TeX::altComm::buscaFormato $f]
	 if {$formato == ""} {
	     set isDVI 0
	     return "pdftex  -progname=pdflatex \"$f\""
	 } elseif {[string match pdf* $formato]} {
	     set isDVI 0
	     return "pdftex -fmt=$formato -progname=pdflatex \"$f\""
	 } else {
	     set isDVI 1
	     return "tex -fmt=$formato -progname=latex \"$f\""

    # juanfc 2003-08-04
    # cmd now is not used but a [proc] that evaluates it
    proc TeX::altComm::typeset { cmd } {
	 variable texPipe
	 set thisFile [win::Current]
	 if { [file readable $thisFile] } {
	     # The top-most window is a readable file, so let us work with it:
	     set texPipe(filePath) [file dirname $thisFile]
	     set texPipe(jobName) [win::Tail]
	     # We don't want the extension:
	     set texPipe(jobName) [file rootname $texPipe(jobName)]
	 } else {
	     if { $thisFile == "*TeX console*" } {
	     # Since we are in a TeX console, most probably the path and the
	     # file name is still recorded in texPipe --- we trust this...
	     } else {
		 status::msg "Can't typeset this window"

	 # Save a copy of the aux file in case we interrupt tex...:
	 set auxFileName "$texPipe(jobName)"
	 append auxFileName ".aux"
	 if { [file writable $texPipe(filePath)/$auxFileName] } {
	     exec /bin/cp $texPipe(filePath)/${auxFileName} $texPipe(filePath)/${auxFileName}.tmp

	 # Open the pipe:
	 set texPipe(pipeName) [open "|/bin/sh" RDWR]

	 # Make sure the output from sh comes rolling line by line in the correct way:
	 fconfigure $texPipe(pipeName) -buffering line -translation auto -blocking 0

	 # Make sure the tetex path is in place:
	 puts $texPipe(pipeName) \
    {PATH="/usr/local/teTeX/bin/powerpc-apple-darwin-current:${PATH}";export PATH}
	 # Turn the shell to the right directory:
	 puts $texPipe(pipeName) "cd $texPipe(filePath)"
	 # ========== SEND THE LATEX COMMAND TO THE SHELL ==============
    # juanfc 2003-08-04
    # change a simple $cmd for a command that is builded looking into the own file to typeset
	 puts $texPipe(pipeName) "[::TeX::altComm::cmd $thisFile] $texPipe(jobName) ; exit"
    #     puts $texPipe(pipeName) "$cmd $texPipe(jobName) ; exit"
	 # When the shell exits, and eof event will occur.  The display proc
	 # must then detect this and close the pipe.  (It furthermore invokes
	 # the proc postProc which can for example do some further things like
	 # invoking a PDFViewer.)

	 # The handler needs a window to write in:
	 if { [lsearch -exact [winNames] "*TeX console*" ] == -1 } {
	     # Console: 80 cols 20 rows (of Monaco 9).  (The numbers 300 are arbitary):
	     new -g 300 300 503 243 -n "*TeX console*" -fontsize 9 -shell 1 -m TeX
	 } else {
	     deleteText -w "*TeX console*" [minPos] [maxPos -w "*TeX console*"]
	     bringToFront "*TeX console*"
	 # Set up a handler for the output:
	 fileevent $texPipe(pipeName) readable ::TeX::altComm::receiveAndDisplay


    # Handles all output from the shell
    proc TeX::altComm::receiveAndDisplay {} {
	 variable texPipe

	 set status [catch { set texPipe(_res) [read $texPipe(pipeName)] }]

	 # Special case (end of job):
	 if { $status || [eof $texPipe(pipeName)] } {
	     # There is nothing more to read --- just close the pipe:
	     close $texPipe(pipeName)
	     # Call a post procedure (which might for example invoke PDFViewer):

	 # Normal case --- just print the line to the TeX console:
	 if { [string length $texPipe(_res)] } {
	     # The line we have read was nonempty
		 insertText -w "*TeX console*" "$texPipe(_res)"

	 } else {
	     # There was an empty line.  Check if this is because the shell has died:
	     set shPID [pid $texPipe(pipeName)]
	     set answer [exec ps -p $shPID]
	     if { ![regexp $shPID $answer] } {
		 # The shell has died (probably latex simply finished)
		 close $texPipe(pipeName)
    # (None of the return statements are necessary, they are just for easier reading)

    # Upon carriage return in the *TeX console*, send the command to tex pipe
    proc TeX::altComm::sendMore {} {
	 variable texPipe

	 # Get the last line from the console:
	 set more [getText [pos::lineStart [getPos]] [getPos]]
	 # Find out what the prompt is:
	 regexp {[^\r\n]*$} $texPipe(_res) prompt
	 # The rest is what we want to send to the pipe:
	 regsub "^[quote::Regfind $prompt]" $more "" more

	 # Insert carriage return:
	 insertText -w "*TeX console*" "\r"

	 # Intercept the command 'e' for edit.  Instead of sending it to tex
	 # we kill the tex run (closing the pipe) and go directly to the source
	 # window for editing the error line:
	 if { $prompt == "? " } {

	     if { $more == "e" } {
		 # Find the line number of the error:
		 catch { search -f 0 -r 1 -s {l\.([0-9]+)\s} [getPos] } pos
		 set errorSpec [getText [lindex $pos 0] [lindex $pos 1]]
		 regexp {[0-9]+} $errorSpec errorLine
		 # Find out which file the error belongs to:
		 if { ![catch {set opParPos [matchIt ")" [lindex $pos 0]]}] } {
		     set line [getText [pos::math $opParPos + 1] [pos::lineEnd $opParPos]]
		     set file [lindex $line 0]
    # juanfc 2003-08-04
    # a very very obscure patch I needed to introduce for name file to be
    # correctly scanned.  I found that after the name comes a {/....}
    with the command used!
    # but only in my tests
    regexp {[^\{]*} $file file
		     regsub {^\./} $file "$texPipe(filePath)/" file
		 } else {
		     set file $texPipe(jobName).tex
		 # That's all we want from the TeX console
		 insertText -w "*TeX console*" "TeX interrupted.  No pages of output.\r\
                   Transcript written on $texPipe(jobName).log.\r"
		 close $texPipe(pipeName)
		 # Now go to the file
		 if { [lsearch -exact [winNames] [file tail $file]] >= 0 } {
		     bringToFront [file tail $file]
		 } elseif { [file readable $file] } {
		     edit $file
		 } else {
		     status::msg "Could not find the file containing the TeX error."
		 goto [pos::fromRowCol $errorLine 0]
		 status::msg ""

		 insertText -w "*TeX console*" "Previous .aux file preserved.\r"
	 if { $more == "x" } {
	     insertText  -w "*TeX console*" "TeX interrupted.  No pages of output.\r\
               Transcript written on $texPipe(jobName).log.\r"
	     close $texPipe(pipeName)
	     insertText -w "*TeX console*" "Previous .aux file preserved.\r"

	 puts $TeX::altComm::texPipe(pipeName) $more

    proc TeX::altComm::copyBackOldAuxFile { } {
	 variable texPipe
	 # should be done better to handle the case where the tex file does not
	 # have a tex extension!
	 set auxFileName $texPipe(jobName)
	 append auxFileName ".aux"
	 if { [file writable $texPipe(filePath)/${auxFileName}] \
	   && [file readable $texPipe(filePath)/${auxFileName}.tmp] } {
	     exec /bin/mv $texPipe(filePath)/${auxFileName}.tmp $texPipe(filePath)/$auxFileName

    proc TeX::altComm::shellCarriageReturn {} {
	    if { [win::Current] == "*TeX console*" } {
	    } else {

    ascii  0x0d   TeX::altComm::shellCarriageReturn "TeX"

    proc TeX::altComm::postProc {} {
	 variable texPipe
	 variable isDVI

	 if {$isDVI} {
	     set dviFileName $texPipe(jobName)
	     append dviFileName ".dvi"
	     exec open -a MacDviX $texPipe(filePath)/$dviFileName
	 } else {
	     set pdfFileName $texPipe(jobName)
	     append pdfFileName ".pdf"
	     exec open -a PDFViewer $texPipe(filePath)/$pdfFileName

	 # Remove that temporary .aux file:
	 set auxFileName $texPipe(jobName)
	 append auxFileName ".aux"
	 if { [file writable $texPipe(filePath)/${auxFileName}.tmp] } {
	     exec /bin/rm $texPipe(filePath)/${auxFileName}.tmp

	 # While PDFViewer is in the front, let us secretly make the source file
	 # the frontmost Alpha window, so that we are ready to edit it when we
	 # later return to Alpha:
	 after 2500
	 bringToFront $texPipe(jobName).tex

(Anonymous) remark copied over from the page [AlphaTcl and Helpers]: the code of the tex interface in newComm.tcl is pretty similar to the interactive spell-checker which is used by Alphatk on Windows (in Tcl/SystemCode/CorePackages/spellcheck.tcl). I'm sure we can move some common 'pipe' functionality into AlphaTcl.

JK (20/09/2003): no coincidence --- newComm.tcl learned about pipes from spellcheck.tcl. Later it turned out there is a lot of instructive examples of pipes over at the TclTk wiki and it is also very well explained in Bernard's wonderful book Tcl/Tk [] (in French). It is all pretty much the same. I think the conclusion is that this is simply how pipes work in Tcl, and I don't think it is worth trying to define an API on top of that.

My interest is more towards specific interactions, and to exploit the particular possibilities and needs of each programme. One of the most important unix programmes to interact with it tex.

To implement a really good tex interface on the principles of the minimal examples above requires first of all some preliminary operations to make clear what the current document is, what the path is, and perhaps what format it is supposed to be tex'ed in. Once this is in place (and assuming Fr√İd√İric's AlphaServer is running) a very usable tex interface is easy to obtain. But in fact the problem of returning to Alpha with correct error line number can be circumvented quite easily, since anyway we are in fact already running inside Alpha. All we need is to intercept the command "e": instead of sending it to tex who in turn will prepare a command to send to the AlphaClient, who will call the AlphaServer, we might as well do all this internally, and never tell tex that we wanted to edit at that error. The alternative tetex interface altComm.tcl is meant to do this.

Another trick made possible by the fact that we can intercept the signals is that of preserving the previous .aux file in case of a tex interruption (and thus avoid warning for missing references at the next run). Simply take a temporary copy of the .aux file and use that one in case of interruption.


Retrieved from
Page last modified on July 04, 2006, at 01:08 AM