Terminal Terminators

  • Caylan Van Larson

    The documentation at http://tinco.pair.com/bhaskar/gtm/doc/books/pg/UNIX_manual/ch09s03.html states that control characters that end a read are defined using TERMINATOR.  However, there are only conflicting examples:



    Moreover, how do you view current terminators and set multiple terminators?

  • Bob Isch

    Bob Isch - 2011-11-13

    Hi caylan,

    The use of "TERM" is an abbreviation for "TERMINATOR".  Most things in GT.M can be shortened to "unique" names.

    For multiple terminators, simply add them as additional bytes in the string.  For example, we use the following to terminate the read on SOH, BACKSPACE, TAB, ENTER, and DEL:

       u "":(pasthru:terminator=$c(1,8,9,13,127))


      u "":(nopasthru:terminator=$c(13,27))

    to terminate the read on ENTER or an ESCAPE character (and then presumably do additional reads to do function key processing).

    Hope that helps,

  • Caylan Van Larson

    Hi Bob,

    I'm revisiting this.  I've tried a lot of different options and I have one problem.  Pressing ESC only works if I press it twice.  What would cause this?

  • Bob Isch

    Bob Isch - 2012-02-17

    Hi Caylan,

    I think what you are seeing is due to your use of the ESCAPE device parameter.  This tells GT.M to "process" escape characters, or function keys.  Which is fine as long as you are not using the ESCape key as part of your user interface and you are willing to do your own function key processing.  The "problem" is that if you want GT.M to "process" the escape characters or function keys then it must read additional characters after it "sees" an ESCape character.  For example, the F1 key (depending on terminal/emulation) might send: "ESC [ 1 1 ~", the tilde (~) being the terminating character in this case.  In other words, the ESCAPE parameter is overriding the TERM=$c(27) parameter.  Also note that two ESC characters is an "invalid escape sequence" so that is why the read terminates if you press ESC twice.

    You may get the results you want simply by specifying NOESCAPE instead of ESCAPE.

    Also, you probably know that the "terminating" character(s) are placed in $ZB so you can use that to see what key was pressed.  See below:

    gtm> zp ^foo1
    foo1    ; Test ESCAPE processing
            u $p:(noecho:escape:nopasthru:term=$c(13,27))
            r !,"Type 'abc' and press ENTER: ",a,! s b=$zb
            zwr a,b
            r !,"Press ESC twice: ",a,! s b=$zb
            zwr a,b
            r !,"Press F1: ",a,! s b=$zb
            zwr a,b
            u $p:(echo:noescape:nopasthru:term=$c(13,27))
            r !,"Type 'abc' and press ENTER: ",a,! s b=$zb
            zwr a,b
            r !,"Press ESC once: ",a,! s b=$zb
            zwr a,b
            i 1 ; set $test=1
            r !,"Press F1 (and wait a second or two): ",a,! s b=$zb,t=$t
            r a1:1 s b1=$zb,t1=$t
            zwr a,b,t,a1,b1,t1
            q  ; >>> foo1
    gtm> d ^foo1
    Type 'abc' and press ENTER:
    Press ESC twice:
    Press F1:
    Type 'abc' and press ENTER: abc
    Press ESC once:
    Press F1 (and wait a second or two):

    Case 1: is a normal read with your settings.  The read is terminated by the ENTER key.  The user input is NOT echoed as you type.

    Case 2: demonstrates the need to press ESC twice to terminate the read.  Both ESC's are in the $zb special variable.

    Case 3: shows that the F1 key terminates the read (anything typed before pressing F1 will be in 'a' - nothing in this case) and the full ESCape sequence sent by the F1 key is in $ZB.
    NOTE: The above three cases function identically if TERM=$C(13) is used instead of TERM=$C(13,27)

    Case 4: (with "NOESCAPE" processing specified now, and "ECHO") functions the same as Case 1 except user input is echoed as you type.

    Case 5: shows that now a single ESC will terminate the read.

    Case 6: shows that the combination of NOESCAPE and TERM=$C(27) means that the read terminates on the ESC and then additional READs are required by the application to read the rest of the "F1 String".  Also, note that the use of ECHO means that part of the sequence will be echoed to the terminal.  You could turn echo off before doing that read I suppose.

    So, what you need to do depends on how you want to handle escape sequences, whether you really NEED to deal with the user pressing the ESC key (and if an immediate response is required), etc.

    Hope that helps some.  Let us know if you have any other questions,

  • Caylan Van Larson

    Bob - Thank you.  After a month of other activities I'm getting back to this project.  I have a few more questions.  Escape sequences and terminal interaction is complicated by itself, but migrating and mixing DTM with GTM in my brain is borderline psychosis inducing.  ;-)

    First things first, is $ZB in GTM equivalent to $K in DTM?

    The program will need to respond to arrow keys, function keys, and escape keys (immediately).  In DTM this is accomplished using something like:

    F I=1,4,5,6,9,11,12,14,18,21:1:26,28:1:31 D set^%mixinterp(I,35)

    Mixinterp is DTM-specific, the source of which is nasty "VIEW" commands.  My plan is to translate these to something like:


        u:value=35 $P:(TERM=$C(char))

    Will each invocation of "set" overwrite the previous TERM string?  Is there any way to read/get and concatenate the TERM character list?

    Thanks for your help.

    Seeking the secret sauce,


  • Bob Isch

    Bob Isch - 2012-03-26

    Hi Caylan,

    As you know, getting any particular implementation to work exactly like another implementation is not always possible.  This often makes trying to port something that uses implementation specific features by just identifying those features and implementing them in the target implementation problematic.  We typically deal with that by making sure the application code is written to "standard" M and isolate anything specific to the environment (file I/O, terminal handing, etc.)  When trying to move to a new environment it is best not to give preference to one environment or another but expect them all to be able to implement your basic set of functions.  But, you probably do a lot of that too…

    Getting to the questions at hand.

    If I recall correctly $K and $ZB are very similar.  GT.M does implement $KEY.  I would expect that to possibly be useful.

    You are correct that successive USE commands with TERM= values will override any existing values.  There are a couple of ways around this.  First, I'm guessing there is a "clear^%mixinterp(..)" or something similar.  This means you could implement something like "myTerminators^myLib($c(1,4,5,6,…))" that would call clear and then set multiple times if in MSM or just do a USE @("$P:(TERM=$C("_arg_"))") or something like that for GT.M.  Having separate versions of myLib.m for MSM and GT.M is one way to do that.

    If you REALLY want to emulate the DTM set^%mixinterp functionality you could do something like:

    foo     ; Test addterm()
            zshow "D"
            f c=1,4,5,6,9,10 d addterm(c)
            zshow "D"
    addterm(ch)     ;
            n (ch)
            zshow "D":s
            ; s("D",1)="/dev/pts/8 OPEN TERMINAL NOPAST NOESCA NOREADS TYPE TERM=$C(1,13,27) WIDTH=139 LENG=75 "
            f n=1:1 q:'$d(s("D",n))  i $p(s("D",n)," ",1)=$p d  q
            .       s term=$p($p(s("D",n),"TERM=",2)," ",1) ; $C(1,13,27)
            .       i term="" s term="$c("_ch_")"
            .       e  s term=$e(term,1,$l(term)-1)_","_ch_")"
            .       s @("term="_term)
            .       u $p:(TERM=term)
            q  ; >>> addterm

    Hope that helps,


Log in to post a comment.