Caché WebTerminal v4 Release

Greetings, InterSystems community!

I am pleased to announce that the web terminal project, Caché WebTerminal version 4 gets its release! After long period of enhancing this web application from 2013, it came to the version 4, which features major stability and security improvements, intelligent autocomplete and syntax highlighting, convenient SQL mode and a lot of other useful features.

The goal of this article is to spread the knowledge about this project over the InterSystems community.

Caché WebTerminal is a web-based terminal for InterSystems products built on top of InterSystems Caché. The power of this project is not only in projecting the standard terminal utility onto the browser. WebTerminal is a flexible tool that can be easily embedded into any other projects, used on mobile devices and vastly enhance the user experience.

History

The development of the first version of WebTerminal project started in 2013, during my first internship in InterSystems corporation in Russia (it was the closest office to my homeland, Ukraine). From that time WebTerminal project grew up a lot and found its place across the InterSystems corporation and its partners, who had noticed this project on InterSystems-RU GitHub and some other resources, or just by Googling.

Now WebTerminal is used around the world. It was embedded to InterSystems IDEL (Interactive Development Environment for Learning) project, Caché Visual Editor project, to the Atom-based Caché Studio and is mostly used by developers for the development in local and remote environments.

The analytics shown below demonstrates the interest in WebTerminal GitHub repository page views on intersystems-ru GitHub by users for the past days:

* Presented analytics is not precise and demonstrates only approximate data.

Features

Caché WebTerminal project has its own homepage, and you can find the full list of its features there. The list below summarizes the most important ones.

 ● Terminal In The Browser

 Just enter the URL of the server you need to connect to, and use it from the browser. No need to install any software. The simplest way to share the developer access to your server. It's safe.

 ● Terminal For Mobile Devices

 As well as for desktop browsing, you can use mobile device to perform operations.

 ● Intelligent Autocomplete

 WebTerminal understands the context and suggests the endings for you: class names, class method and instance method names, variables in current context, their properties and more.

 ● Clever Syntax Highlighting

 WebTerminal highlights Caché ObjectScript and SQL code not just by keywords, analyzing the structure of the command and giving the intelligent highlighting.

 ● Embedded SQL Mode

 No need to switch so often to SQL shell — WebTerminal has embedded SQL mode which renders native HTML table with output.

 ● Tracing Globals / Files And Remembering Commands

 Using WebTerminal's special commands you can start to track changes in Globals or files, save frequently used commands, etc.

 ● Customizable

 WebTerminal can be customized to match your needs.

 ● Embeddable

 WebTerminal can be embedded to any other web applications and is configurable with URL parameters.

Description, Documentation and Downloads

If you are interested in WebTerminal project, you can find out more information on the project's page. There is a rich documentation tab explaining how to install and use WebTerminal.

The installation process is easy: you just need to download and import the XML file to your Caché system. Then you will have the web application set up automatically. To restrict the access to this web application for particular users, or to solve any problems during the installation please refer to the related documentation section. If there are any cases not covered by docs, submit your case to the project's issue tracker.

Caché WebTerminal is an open-source project, and anybody is welcome to join for its development on InterSystems GitHub. Feel free to submit any bugs or features requests to the project's issue tracker. Check the contributing section in docs to get some ideas of what you can help or start with.

We would be glad to see any feedback on this project! There are a lot of things can be done to enhance WebTerminal usage experience, and there are still some things that need to be done, but the project is live and please do not hesitate to submit any problems or request for extending WebTerminal's functionality or API.

Thanks and enjoy using WebTerminal!

Comments

Looks great, nice project!

I noticed it takes a while to update the screen when I run something like this, is there any possibility for optimization of this?

for i=1:1:1000 w $tr($j("",400)," ","@"),!

Hello Mark, thank you!

I did a little fast research and found that it is possible to make the behavior of printing long output similar to the standard Caché terminal (just to make terminal scroll and scroll through the text until the output stops). But how do you think, does it makes sense to replace this "freezing" behavior with always-scrolling output? In both cases, the output is not readable (normally readable). Maybe it makes more sense to display a floating message like "Output in progress..." when WebTerminal freezes for more than 1 second?

Would be glad to hear your opinion, thanks!

Personally I think I would prefer the output to be streamed as it is received.  Perhaps this could be an option?  Live output vs "Output in progress" as you suggest?

I think scrolling and showing the output as soon as possible is the most useful as this shows the user what is happening and it is the same behavior for all other terminals I have used. Often you can make out patterns in the output even though it is scrolling and this can be handy.

The other thing I noticed is that Ctrl+C does not appear to work, is it meant to?

Mark,

Good point on the output issue. I was thinking to make non-freezing output during the development but postponed this idea in favor of synchronous output (which is freezing browser until all the content renders). I will take this into account in future versions of WebTerminal.

CTRL-C for now works as a default combination for copying text. Unfortunately, after reading documentation a lot, I am still having no idea how to implement interrupt in WebTerminal. Technically speaking, I need to send the interrupt signal to a jobbed process somehow, but continue to execute it from, for example, some label in the COS code. Any help here is appreciated!

To track the interrupt enhancement, I have created this issue.

Good news! Today we figured out how to implement Ctrl+C interrupts in WebTerminal. Thanks to Mark for pointing on this.

Now WebTerminal will prompt you to update to version 4.1.2, which includes Ctrl+C interrupts implementation. See the related docs: when there is any selection, Ctrl+C will work as a normal text copying. But when there is no text selected, Ctrl+C will work as an interrupt.

Mark, I need a bit more time to implement non-freezing long output behavior, but it is in my list now and will be implemented soon. Thanks for your comments!

Awesome, that is great news. Thanks for making this tool.

Wow, that's great! I should check it!

BTW, maybe one day the break command will work too? I mean as it works as a debugger in usual terminal?

I hope so! :) Or we can even think about some kind of "screen debugger" for terminal like it was suggested.

Firt, my congratulations, your application has more potential, I've been following it for a long time, and now I think it's in a developmental point that allows me to use it. I have an application that works in VT 240 emulation, and the application uses the function keys F1 to F12, as well as, the cursor movement keys, both possibilities don't work with WebTerminal. Also, the graphic characters are not displayed, it dosen't allow to draw boxes or lines. Both features are important to my program works correctly. Do you think you can develop these modifications?

Of course, Javier! Thank you for your interest and attention on this project.

If you application is open-sourced (or if you can share it with me), then it would be easier for me to figure out which escape sequences are missing. If not, you would need to find out what happens and which parts of your application is not supported by WebTerminal.

Regarding to F1-F12 keys support, I already have the way of implementing it, so please create the issue here to track the progress. For other things, like "graphic characters are not displayed", "it doesn't allow to draw boxes or lines" I need more detailed technical description, including possible code which doesn't work in WebTerminal, but does work in Caché TERM.

Feel free to submit your requests to our GitHub project issue tracker, this helps to create an even better project!

Thanks!

Nikita, this is really an excellent tool.

One question: The documentation states that Web Terminal is supported from version 2013.1 and up of Caché (Ensemble, etc.). But it seems (at least v4) to require functionality (e.g. %CSP.REST) that exists only in newer versions than 2013.1.

I assume older versions of Web Terminal supported 2013.1.

Could you please restate what is the minimal Caché version for v4 of Web Terminal, and what is the latest version of Web Terminal that did support Caché v2013.1.

(And perhaps update for every ("released") version of Web Terminal which is the minimal required Caché version.)

Thanks!

 

Thank you very much for this brilliant note!

I have fixed the Caché version requirements. The thing is, starting from WebTerminal version 3, it uses %CSP.REST classes, which support starts from Caché 2014.1. Earlier versions of WebTerminal (1-2) can be still used with Caché 2013.1.

The docs are updated now. Thanks!

Thanks very much for this.  I have been testing it for several days with a legacy application and I think I will be able to make good use of it.

 

However there are two issues I have found, so far, that make it unusable as it is.

The first is in relation to ESC sequences.  The ESC sequences for Erase in Line and Erase in Display do not handle the case ESC[0J and ESC[0K which should be treated as ESC[J and ESC[K respectively.  I think I know how to download from GIT and add these functions to esc.js source and push a change request but it may be simpler for you to add them.

The second issue is that our legacy application makes extensive use of %X364 mnemonics calls in write commands

e.g. write /CUP(1,1),/ED(0),"This text should appear on a cleared screen at row 1, column 1"

With WebTerminal version 4 as downloaded any use of %X364 mnemonics results in error <NOLINE>WebTerminal.Core.1. 

However I have been able to fix this by appending the code from ^%X364.int into the method redirects of class WebTerminal.Core as below.

You may want to consider adding this to a new release as it makes a whole new set of legacy apps usable with WebTerminal.

Many thanks for all your efforts on this.  If I can work out how I will raise the two problems above in the Github issue tracker.  In the meantime I have patched the version on my development machine and will continue with testing.

 

/// Cache WEB Terminal version 4.1.2 core.
/// The core class which handles client requests and executes COS code.
/// All writes used here are used for $X and $Y compatibility, but they actually do not
/// write any code to the screen.
Class WebTerminal.Core Extends Common [ Not ProcedureBlock ]
{

/// Write and read redirects used when redirecting i/o.
/// Each of the redirects signals to $ZPARENT process the $LISTBUILD string.
/// There is several actions defined in the WebTerminal.Engine handler class for received list.
/// "o" is for output. Resulting with $lb("o", {string})
/// "r" is for reading string. Resulting with $lb("r", {length}, {timeout})
/// "rc" is for reading char. Resulting with $lb("rc", {timeout})
/// "end" symbolizes that execution end is reached. Resulting with $lb("end", {error message})
Method redirects() [ Private, ProcedureBlock = 0 ]
{
wstr(str)
    do ##class(%Device).ReDirectIO($$$NO)
    write str
    do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", str)

wchr(c)
    do ##class(%Device).ReDirectIO($$$NO)
    write $CHAR(c)
    do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", $CHAR(c))

wnl
    do ##class(%Device).ReDirectIO($$$NO)
    write !
    do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", $CHAR(13, 10))

wff
    do ##class(%Device).ReDirectIO($$$NO)
    set $X = 0
    set $Y = 0
    do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", $CHAR(12))

wtab(s)
    do ##class(%Device).ReDirectIO($$$NO)
    set $x = s
    do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", $CHAR(27) _ "[" _ (s + 1) _ "G")

rstr(length = 32656, timeout = 86400)
    do ##class(WebTerminal.Common).SendChunk($ZPARENT, "r", $lb(length, timeout))
    quit $LISTGET(##class(WebTerminal.Common).ReceiveChunk(), 2)

rchr(timeout = 86400)
    do ##class(WebTerminal.Common).SendChunk($ZPARENT, "c", timeout)
    quit $LISTGET(##class(WebTerminal.Common).ReceiveChunk(), 2)
 ;
 ;  Added code below to be able to simulate %X364 Mnemonic space
 ;  functionality (e.g.  write /ED(n),EL(n),/SGA(n),etc ) when 
 ;  executing legacy routines in WebTerminal.
 ;
X364FIX
 ; FIXED ERROR - MISSING % - IN DAQ FUNCTION
 ; BINDING FOR ANSI X3.64 NAMESPACE, NOV/92 ; LRS952 06/07/05
 /*
 +--------------------------------------------------------+
 | Copyright 1986-2011 by InterSystems Corporation,       |
 | Cambridge, Massachusetts, U.S.A.                       |
 | All rights reserved.                                   |
 |                                                        |
 | Confidential, unpublished property of InterSystems.    |
 |                                                        |
 | This media contains an authorized copy or copies       |
 | of material copyrighted by InterSystems and is the     |
 | confidential, unpublished property of InterSystems.    |
 | This copyright notice and any other copyright notices  |
 | included in machine readable copies must be reproduced |
 | on all authorized copies.                              |
 +--------------------------------------------------------+
 */
 u 0 w !,"Illegal entry point: do not call ^",$ZN," directly."
 q:$zu(41)'>2  ZTRAP "EP"
erdx s $x=0 q
erdy s $y=0 q
APC ; Application program command
 w $c(27)_"_"
 q
BEL ; Ring the bell
 w $c(7)
 q
CBT(%1) ; Cursor backward tabulation %1 tab stops
 s %1=+$g(%1,1) w $c(27,91)_%1_"Z" s $zt="erdx",$x=$x+7\8-%1*8
 q
CCH ; Cancel character
 w $c(27)_"T"
 q
CHA(%1) ; Cursor horizontal absolute (move to column %1)
 s %1=+$g(%1,1) w $c(27,91)_%1_"G" s $zt="erdx",$x=%1-1
 q
CHT(%1) ; Cursor horizontal tabulation (forward %1 tab stops)
 s %1=+$g(%1,1) w $c(27,91)_%1_"I" s $zt="erdx",$x=$x\8+%1*8
 q
CNL(%1) ; Cursor next line (cursor down %1 lines)
 s %1=+$g(%1,1) w $c(27,91)_%1_"E" s $zt="erdy",$y=$y+%1
 q
CPL(%1) ; Cursor preceding line (cursor up %1 lines)
 s %1=+$g(%1,1) w $c(27,91)_%1_"F" s $zt="erdy",$y=$y-%1
 q
CPR ; Cursor position report (return in $KEY)
 n %1 w $c(27,91)_"6n" r %1
 q
CTC(%1,%2,%3,%4,%5,%6,%7,%8,%9) ; Cursor tabulation control
 n %i,%p s %p=""
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"W"
 q
CUB(%1) ; Cursor backward %1 columns
 s %1=+$g(%1,1) w $c(27,91)_%1_"D" s $zt="erdx",$x=$x-%1
 q
CUD(%1) ; Cursor down %1 lines
 s %1=+$g(%1,1) w $c(27,91)_%1_"B" s $zt="erdy",$y=$y+%1
 q
CUF(%1) ; Cursor forward %1 columns
 s %1=+$g(%1,1) w $c(27,91)_%1_"C" s $zt="erdx",$x=$x+%1
 q
CUP(%2,%1) ; Cursor position (column %1, line %2)
 s %1=+$g(%1,1),%2=+$g(%2,1) w $c(27,91)_%2_";"_%1_"H"
 s $zt="ecup",$x=%1-1,$zt="erdy",$y=%2-1 q
ecup s $x=0,$zt="erdy",$y=%2-1
 q
CUU(%1) ; Cursor up %1 lines
 s %1=+$g(%1,1) w $c(27,91)_%1_"A" s $zt="erdy",$y=$y-%1
 q
CVT(%1) ; Cursor vertical tabulation
 w $c(27,91)_+$g(%1,1)_"Y"
 q
DA ; Device attributes - return in $KEY
 n %1,%2,%3 s %3="" u $i:("":"+S")
 w $c(27,91)_"c" r %1 s %2=$k f  r *%1 s %3=%3_$c(%1) q:%1=99
 s $k=%2_%3 u $i:("":"-S")
 q
DAQ(%1,%2,%3,%4,%5,%6,%7,%8,%9) ; Define area qualification
 n %i,%p s %p=""
 //   Original %X364 routine fails on undefined variable i
 // f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_i)
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"o"
 q
DCH(%1) ; Delete %1 characters
 w $c(27,91)_+$g(%1,1)_"P"
 q
DCS ; Device control string
 w $c(27)_"P"
 q
DL(%1) ; Delete %1 lines
 w $c(27,91)_+$g(%1,1)_"M"
 q
DMI ; Disable manual input
 q
DSR(%1) ; Device status report - type %1 - return in $KEY
 n %2 w $c(27,91)_+$g(%1,5)_"n" r %2
 q
EA(%1) ; Erase in area
 w $c(27,91)_+$g(%1,1)_"O"
 q
ECH(%1) ; Erase %1 characters
 w $c(27,91)_+$g(%1,1)_"X"
 q
ED(%1) ; Erase in display (%1=0 cursor-to-end,1 begin-to-cursor,2 entire scr)
 w $c(27,91)_+$g(%1)_"J"
 q
EF(%1) ; Erase in field
 w $c(27,91)_+$g(%1,1)_"N"
 q
EL(%1) ; Erase in line (%1=0 cursor-to-end, 1 begin-to-cursor, 2 entire line)
 w $c(27,91)_+$g(%1)_"K"
 q
EMI ; Enable manual input
 q
EPA ; End of protected area
 w $c(27)_"W"
 q
ESA ; End of selected area
 w $c(27)_"G"
 q
FNT ; Font selection
 q
GSM ; Graphic size modification
 q
GSS ; Graphic size selection
 q
HPA(%1) ; Horizontal position attribute (cursor to column %1)
 s %1=+$g(%1,1) w $c(27,91)_%1_"`" s $zt="erdx",$x=%1-1
 q
HPR(%1) ; Horizontal position relative (cursor forward %1 columns)
 s %1=+$g(%1,1) w $c(27,91)_%1_"a" s $zt="erdx",$x=$x+%1
 q
HTJ ; Horizontal tab with justify
 w $c(27)_"I"
 q
HTS ; Horizontal tabulation set
 w $c(27)_"H"
 q
HVP(%1,%2) ; Horizontal and vertical position (column %1, line %2)
 s %1=+$g(%1,1),%2=+$g(%2,1) w $c(27,91)_%2_";"_%1_"f"
 s $zt="ehvp",$x=%1-1,$zt="erdy",$y=%2-1 q
ehvp s $x=0,$zt="erdy",$y=%2-1
 q
ICH(%1) ; Insert %1 characters
 w $c(27,91)_+$g(%1,1)_"@"
 q
IL(%1) ; Insert %1 lines
 w $c(27,91)_+$g(%1,1),"L"
 q
IND ; Index
 w $c(27)_"D" s $y=$y+1
 q
INT ; Interrupt
 w $c(27,91)_"a"
 q
JFY ; Justify
 q
MC ; Media copy
 w $c(27,91)_"i"
 q
MW ; Message waiting
 w $c(27)_"U"
 q
NEL ; Next line
 w $c(27)_"E" s $x=0,$y=$y+1
 q
NP(%1) ; Next page (advance %1 pages of terminal display memory)
 w $c(27,91)_+$g(%1,1)_"U"
 q
OSC ; Operating system command
 w $c(27)_"]"
 q
PLD ; Partial line down
 w $c(27)_"K"
 q
PLU ; Partial line up
 w $c(27)_"L"
 q
PM ; Privacy message
 w $c(27)_"^"
 q
PP(%1) ; Preceding page (backup %1 pages of terminal display memory)
 w $c(27,91)_+$g(%1,1)_"V"
 q
PU1 ; Private use one
 w $c(27)_"Q"
 q
PU2 ; Private use two
 w $c(27)_"R"
 q
QUAD ; QUAD
 q
REP ; Repeat
 w $c(27,91)_"b"
 q
RI ; Reverse index
 w $c(27)_"M" s $zt="erdy",$y=$y-1
 q
RIS ; Reset to initial state
 w $c(27)_"c" s $x=0,$y=0
 q
RM(%1,%2,%3,%4,%5,%6,%7,%8,%9) ; Reset mode
 n %i,%p s %p=""
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"l"
 q
SEM ; Select editing extent mode
 w $c(27,91)_"Q"
 q
SGR(%1,%2,%3,%4,%5,%6,%7,%8,%9)  ; Select graphic rendition %1 thru %9
 n %i,%p s %p=""
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"m"
 q
SL ; Scroll left
 q
SM(%1,%2,%3,%4,%5,%6,%7,%8,%9) ; Set mode
 n %i,%p s %p=""
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"h"
 q
SPA ; Start of protected area
 w $c(27)_"V"
 q
SPI ; Spacing increment
 q
SR ; Scroll right
 q
SS2 ; Single shift two
 w $c(27)_"N"
 q
SS3 ; Single shift three
 w $c(27)_"O"
 q
SSA ; Start of selected area
 w $c(27)_"F"
 q
ST ; String terminator
 w $c(27)_"\"
 q
STS ; Set transmit state
 w $c(27)_"S"
 q
SU ; Scroll up
 w $c(27,91)_"S"
 q
TBC ; Tabulation clear
 w $c(27,91)_"g"
 q
TSS ; Thin space specification
 q
VPA(%1) ; Vertical position attribute (move to row %1 at same column)
 s %1=+$g(%1,1) w $c(27,91)_%1_"d" s $zt="erdy",$y=%1-1
 q
VPR(%1) ; Vertical position relative (move down %1 lines at same column)
 s %1=+$g(%1,1) w $c(27,91)_%1_"e" s $zt="erdy",$y=$y+%1
 q
VTS ; Vertical tabulation sets
 w $c(27)_"J"
 q
}

ClassMethod VarList() As %String [ ProcedureBlock = 1 ]
{
    if $data(%)
    new % set %=$select($test:$LISTBUILD("%"),1:"")
    set:$data(%0) %=%_$LISTBUILD("%0")
    new %0 set %0="%0"
    for {
        set %0=$ORDER(@%0)
        quit:%0=""
        set %=%_$LISTBUILD(%0, $IsObject(@%0), @%0)
    }
    return %
}

/// Retrieves a command text from the parent process.
/// Terminates itself if the parent process is dead.
ClassMethod WaitCommand() As %String [ ProcedureBlock = 1 ]
{
    for {
        set data = ..ReceiveChunk()
        set flag = $LISTGET(data, 1)
        if (flag = "m") { // message
            return $LISTGET(data, 2)
        } elseif (flag = "a") { // autocomplete
            do ##class(WebTerminal.Common).SendChunk($ZPARENT, "a", ..VarList())
        } else { // end or unexpected
            do $system.Process.Terminate($JOB, 2)
            return ""
        }
    }
}

/// Starts new terminal loop. Must be called with JOB command.
ClassMethod Loop(StartupRoutine As %String = "") As %Status
{
    if ($ZPARENT = 0) {
        write "This method is for JOB use only."
        return 0
    }
    open "terminal"::"^%X364"
    use $io::"^" _ $ZName
    if (StartupRoutine '= "") {
        do $System.Util.SetInterruptEnable($$$YES)
        do ##class(%Device).ReDirectIO($$$YES)
        try {
            do @StartupRoutine
            do ##class(%Device).ReDirectIO($$$NO)
        } catch {
            do ##class(%Device).ReDirectIO($$$NO)
            do ..SendChunk($ZPARENT, "e", $LISTBUILD($NAMESPACE, $ZERROR))
        }
        do ..SendChunk($ZPARENT, "e", $LISTBUILD($NAMESPACE, ""))
        return $$$OK
    }
    kill // kill any temporary variables ProcedureBlock may have
    for {
        do ##class(%Device).ReDirectIO($$$YES)
        do $System.Util.SetInterruptEnable($$$YES)
        set $ZERROR = ""
        try {
            xecute ..WaitCommand()
        } catch {}
        do ##class(%Device).ReDirectIO($$$NO)
        write !! // assume that prompt takes 2 lines
        do ..SendChunk($ZPARENT, "e", $LISTBUILD($NAMESPACE, $ZERROR))
    }
    return $$$OK
}

}
 

 

/// Cache WEB Terminal version 4.1.2 core.
/// The core class which handles client requests and executes COS code.
/// All writes used here are used for $X and $Y compatibility, but they actually do not
/// write any code to the screen.
Class WebTerminal.Core Extends Common [ Not ProcedureBlock ]
{
 
/// Write and read redirects used when redirecting i/o.
/// Each of the redirects signals to $ZPARENT process the $LISTBUILD string.
/// There is several actions defined in the WebTerminal.Engine handler class for received list.
/// "o" is for output. Resulting with $lb("o", {string})
/// "r" is for reading string. Resulting with $lb("r", {length}, {timeout})
/// "rc" is for reading char. Resulting with $lb("rc", {timeout})
/// "end" symbolizes that execution end is reached. Resulting with $lb("end", {error message})
Method redirects() [ Private, ProcedureBlock = 0 ]
{
wstr(str)
do ##class(%Device).ReDirectIO($$$NO)
write str
do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", str)
 
wchr(c)
do ##class(%Device).ReDirectIO($$$NO)
write $CHAR(c)
do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", $CHAR(c))
 
wnl
do ##class(%Device).ReDirectIO($$$NO)
write !
do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", $CHAR(13, 10))
 
wff
do ##class(%Device).ReDirectIO($$$NO)
set $X = 0
set $Y = 0
do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", $CHAR(12))
 
wtab(s)
do ##class(%Device).ReDirectIO($$$NO)
set $x = s
do ##class(%Device).ReDirectIO($$$YES)
    quit ##class(WebTerminal.Common).SendChunk($ZPARENT, "o", $CHAR(27) _ "[" _ (s + 1) _ "G")
 
rstr(length = 32656, timeout = 86400)
    do ##class(WebTerminal.Common).SendChunk($ZPARENT, "r", $lb(length, timeout))
    quit $LISTGET(##class(WebTerminal.Common).ReceiveChunk(), 2)
 
rchr(timeout = 86400)
    do ##class(WebTerminal.Common).SendChunk($ZPARENT, "c", timeout)
    quit $LISTGET(##class(WebTerminal.Common).ReceiveChunk(), 2)
 ;
 ;  Added code below to be able to simulate %X364 Mnemonic space
 ;  functionality (e.g.  write /ED(n),EL(n),/SGA(n),etc ) when 
 ;  executing legacy routines in WebTerminal.
 ;
X364FIX
 ; FIXED ERROR - MISSING % - IN DAQ FUNCTION
 ; BINDING FOR ANSI X3.64 NAMESPACE, NOV/92 ; LRS952 06/07/05
 /*
 +--------------------------------------------------------+
 | Copyright 1986-2011 by InterSystems Corporation,       |
 | Cambridge, Massachusetts, U.S.A.                       |
 | All rights reserved.                                   |
 |                                                        |
 | Confidential, unpublished property of InterSystems.    |
 |                                                        |
 | This media contains an authorized copy or copies       |
 | of material copyrighted by InterSystems and is the     |
 | confidential, unpublished property of InterSystems.    |
 | This copyright notice and any other copyright notices  |
 | included in machine readable copies must be reproduced |
 | on all authorized copies.                              |
 +--------------------------------------------------------+
 */
 u 0 w !,"Illegal entry point: do not call ^",$ZN," directly."
 q:$zu(41)'>2  ZTRAP "EP"
erdx s $x=0 q
erdy s $y=0 q
APC ; Application program command
 w $c(27)_"_"
 q
BEL ; Ring the bell
 w $c(7)
 q
CBT(%1) ; Cursor backward tabulation %1 tab stops
 s %1=+$g(%1,1) w $c(27,91)_%1_"Z" s $zt="erdx",$x=$x+7\8-%1*8
 q
CCH ; Cancel character
 w $c(27)_"T"
 q
CHA(%1) ; Cursor horizontal absolute (move to column %1)
 s %1=+$g(%1,1) w $c(27,91)_%1_"G" s $zt="erdx",$x=%1-1
 q
CHT(%1) ; Cursor horizontal tabulation (forward %1 tab stops)
 s %1=+$g(%1,1) w $c(27,91)_%1_"I" s $zt="erdx",$x=$x\8+%1*8
 q
CNL(%1) ; Cursor next line (cursor down %1 lines)
 s %1=+$g(%1,1) w $c(27,91)_%1_"E" s $zt="erdy",$y=$y+%1
 q
CPL(%1) ; Cursor preceding line (cursor up %1 lines)
 s %1=+$g(%1,1) w $c(27,91)_%1_"F" s $zt="erdy",$y=$y-%1
 q
CPR ; Cursor position report (return in $KEY)
 n %1 w $c(27,91)_"6n" r %1
 q
CTC(%1,%2,%3,%4,%5,%6,%7,%8,%9) ; Cursor tabulation control
 n %i,%p s %p=""
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"W"
 q
CUB(%1) ; Cursor backward %1 columns
 s %1=+$g(%1,1) w $c(27,91)_%1_"D" s $zt="erdx",$x=$x-%1
 q
CUD(%1) ; Cursor down %1 lines
 s %1=+$g(%1,1) w $c(27,91)_%1_"B" s $zt="erdy",$y=$y+%1
 q
CUF(%1) ; Cursor forward %1 columns
 s %1=+$g(%1,1) w $c(27,91)_%1_"C" s $zt="erdx",$x=$x+%1
 q
CUP(%2,%1) ; Cursor position (column %1, line %2)
 s %1=+$g(%1,1),%2=+$g(%2,1) w $c(27,91)_%2_";"_%1_"H"
 s $zt="ecup",$x=%1-1,$zt="erdy",$y=%2-1 q
ecup s $x=0,$zt="erdy",$y=%2-1
 q
CUU(%1) ; Cursor up %1 lines
 s %1=+$g(%1,1) w $c(27,91)_%1_"A" s $zt="erdy",$y=$y-%1
 q
CVT(%1) ; Cursor vertical tabulation
 w $c(27,91)_+$g(%1,1)_"Y"
 q
DA ; Device attributes - return in $KEY
 n %1,%2,%3 s %3="" u $i:("":"+S")
 w $c(27,91)_"c" r %1 s %2=$k f  r *%1 s %3=%3_$c(%1) q:%1=99
 s $k=%2_%3 u $i:("":"-S")
 q
DAQ(%1,%2,%3,%4,%5,%6,%7,%8,%9) ; Define area qualification
 n %i,%p s %p=""
 //   Original %X364 routine fails on undefined variable i
 // f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_i)
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"o"
 q
DCH(%1) ; Delete %1 characters
 w $c(27,91)_+$g(%1,1)_"P"
 q
DCS ; Device control string
 w $c(27)_"P"
 q
DL(%1) ; Delete %1 lines
 w $c(27,91)_+$g(%1,1)_"M"
 q
DMI ; Disable manual input
 q
DSR(%1) ; Device status report - type %1 - return in $KEY
 n %2 w $c(27,91)_+$g(%1,5)_"n" r %2
 q
EA(%1) ; Erase in area
 w $c(27,91)_+$g(%1,1)_"O"
 q
ECH(%1) ; Erase %1 characters
 w $c(27,91)_+$g(%1,1)_"X"
 q
ED(%1) ; Erase in display (%1=0 cursor-to-end,1 begin-to-cursor,2 entire scr)
 w $c(27,91)_+$g(%1)_"J"
 q
EF(%1) ; Erase in field
 w $c(27,91)_+$g(%1,1)_"N"
 q
EL(%1) ; Erase in line (%1=0 cursor-to-end, 1 begin-to-cursor, 2 entire line)
 w $c(27,91)_+$g(%1)_"K"
 q
EMI ; Enable manual input
 q
EPA ; End of protected area
 w $c(27)_"W"
 q
ESA ; End of selected area
 w $c(27)_"G"
 q
FNT ; Font selection
 q
GSM ; Graphic size modification
 q
GSS ; Graphic size selection
 q
HPA(%1) ; Horizontal position attribute (cursor to column %1)
 s %1=+$g(%1,1) w $c(27,91)_%1_"`" s $zt="erdx",$x=%1-1
 q
HPR(%1) ; Horizontal position relative (cursor forward %1 columns)
 s %1=+$g(%1,1) w $c(27,91)_%1_"a" s $zt="erdx",$x=$x+%1
 q
HTJ ; Horizontal tab with justify
 w $c(27)_"I"
 q
HTS ; Horizontal tabulation set
 w $c(27)_"H"
 q
HVP(%1,%2) ; Horizontal and vertical position (column %1, line %2)
 s %1=+$g(%1,1),%2=+$g(%2,1) w $c(27,91)_%2_";"_%1_"f"
 s $zt="ehvp",$x=%1-1,$zt="erdy",$y=%2-1 q
ehvp s $x=0,$zt="erdy",$y=%2-1
 q
ICH(%1) ; Insert %1 characters
 w $c(27,91)_+$g(%1,1)_"@"
 q
IL(%1) ; Insert %1 lines
 w $c(27,91)_+$g(%1,1),"L"
 q
IND ; Index
 w $c(27)_"D" s $y=$y+1
 q
INT ; Interrupt
 w $c(27,91)_"a"
 q
JFY ; Justify
 q
MC ; Media copy
 w $c(27,91)_"i"
 q
MW ; Message waiting
 w $c(27)_"U"
 q
NEL ; Next line
 w $c(27)_"E" s $x=0,$y=$y+1
 q
NP(%1) ; Next page (advance %1 pages of terminal display memory)
 w $c(27,91)_+$g(%1,1)_"U"
 q
OSC ; Operating system command
 w $c(27)_"]"
 q
PLD ; Partial line down
 w $c(27)_"K"
 q
PLU ; Partial line up
 w $c(27)_"L"
 q
PM ; Privacy message
 w $c(27)_"^"
 q
PP(%1) ; Preceding page (backup %1 pages of terminal display memory)
 w $c(27,91)_+$g(%1,1)_"V"
 q
PU1 ; Private use one
 w $c(27)_"Q"
 q
PU2 ; Private use two
 w $c(27)_"R"
 q
QUAD ; QUAD
 q
REP ; Repeat
 w $c(27,91)_"b"
 q
RI ; Reverse index
 w $c(27)_"M" s $zt="erdy",$y=$y-1
 q
RIS ; Reset to initial state
 w $c(27)_"c" s $x=0,$y=0
 q
RM(%1,%2,%3,%4,%5,%6,%7,%8,%9) ; Reset mode
 n %i,%p s %p=""
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"l"
 q
SEM ; Select editing extent mode
 w $c(27,91)_"Q"
 q
SGR(%1,%2,%3,%4,%5,%6,%7,%8,%9)  ; Select graphic rendition %1 thru %9
 n %i,%p s %p=""
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"m"
 q
SL ; Scroll left
 q
SM(%1,%2,%3,%4,%5,%6,%7,%8,%9) ; Set mode
 n %i,%p s %p=""
 f %i=1:1:9 i $d(@("%"_%i)) s %p=%p_$e(";",%p'="")_@("%"_%i)
 w $c(27,91)_%p_"h"
 q
SPA ; Start of protected area
 w $c(27)_"V"
 q
SPI ; Spacing increment
 q
SR ; Scroll right
 q
SS2 ; Single shift two
 w $c(27)_"N"
 q
SS3 ; Single shift three
 w $c(27)_"O"
 q
SSA ; Start of selected area
 w $c(27)_"F"
 q
ST ; String terminator
 w $c(27)_"\"
 q
STS ; Set transmit state
 w $c(27)_"S"
 q
SU ; Scroll up
 w $c(27,91)_"S"
 q
TBC ; Tabulation clear
 w $c(27,91)_"g"
 q
TSS ; Thin space specification
 q
VPA(%1) ; Vertical position attribute (move to row %1 at same column)
 s %1=+$g(%1,1) w $c(27,91)_%1_"d" s $zt="erdy",$y=%1-1
 q
VPR(%1) ; Vertical position relative (move down %1 lines at same column)
 s %1=+$g(%1,1) w $c(27,91)_%1_"e" s $zt="erdy",$y=$y+%1
 q
VTS ; Vertical tabulation sets
 w $c(27)_"J"
 q
}
 
ClassMethod VarList() As %String [ ProcedureBlock = 1 ]
{
    if $data(%)
    new % set %=$select($test:$LISTBUILD("%"),1:"")
    set:$data(%0) %=%_$LISTBUILD("%0")
    new %0 set %0="%0"
    for {
        set %0=$ORDER(@%0)
        quit:%0=""
        set %=%_$LISTBUILD(%0, $IsObject(@%0), @%0)
    }
    return %
}
 
/// Retrieves a command text from the parent process.
/// Terminates itself if the parent process is dead.
ClassMethod WaitCommand() As %String [ ProcedureBlock = 1 ]
{
    for {
        set data = ..ReceiveChunk()
        set flag = $LISTGET(data, 1)
        if (flag = "m") { // message
            return $LISTGET(data, 2)
        } elseif (flag = "a") { // autocomplete
            do ##class(WebTerminal.Common).SendChunk($ZPARENT, "a", ..VarList())
        } else { // end or unexpected
            do $system.Process.Terminate($JOB, 2)
            return ""
        }
    }
}
 
/// Starts new terminal loop. Must be called with JOB command.
ClassMethod Loop(StartupRoutine As %String = "") As %Status
{
if ($ZPARENT = 0) {
        write "This method is for JOB use only."
        return 0
    }
    open "terminal"::"^%X364"
    use $io::"^" _ $ZName
    if (StartupRoutine '= "") {
        do $System.Util.SetInterruptEnable($$$YES)
        do ##class(%Device).ReDirectIO($$$YES)
        try {
            do @StartupRoutine
            do ##class(%Device).ReDirectIO($$$NO)
        } catch {
            do ##class(%Device).ReDirectIO($$$NO)
            do ..SendChunk($ZPARENT, "e", $LISTBUILD($NAMESPACE, $ZERROR))
        }
        do ..SendChunk($ZPARENT, "e", $LISTBUILD($NAMESPACE, ""))
        return $$$OK
    }
    kill // kill any temporary variables ProcedureBlock may have
    for {
        do ##class(%Device).ReDirectIO($$$YES)
        do $System.Util.SetInterruptEnable($$$YES)
        set $ZERROR = ""
        try {
            xecute ..WaitCommand()
        } catch {}
        do ##class(%Device).ReDirectIO($$$NO)
        write !! // assume that prompt takes 2 lines
        do ..SendChunk($ZPARENT, "e", $LISTBUILD($NAMESPACE, $ZERROR))
    }
    return $$$OK
}
 
}
 

Hi, Mike! Thanks for the comment, but it is so long that makes it quite unreadable. 

Please consider to post the class's code with gist (how to) for the long listing like here, or see the DC code formatting for the smaller pieces.

 

Yes, please, for the love all things holy!


Evgeny, can't you just edit it to include a snippet?

Thank you Mike for yet another awesome point!

From the WebTerminal v3 I did occasionally dropped ^X364 mnemonics support... Now it's restored! Please update to the latest version of WebTerminal (4.2.0) and confirm here that escape sequences work properly for you.

Thanks!

Hi, Nikita,
I'm a new WebTerminal user, just installed v.4.7.3.

JOBEXAM has problems with it, and it seems that it's %X364 support again. Here is an excerpt from my session: 

CWTv4.7.3 ...

...

USER > zn "%SYS"

%SYS > d ^JOBEXAM

<NOLINE>^%X364

╠%X364 ; BINDING FOR ANSI X3.64 NAMESPACE, NOV/92 ; LRS952 06/07/05

%SYS > w $zv

Cache for Windows (x86-64) 2017.2 (Build 744U) Fri Sep 29 2017 10:58:27 EDT

I've checked %X364 source, it has got correct DAQ() code with this Caché version.

Waiting for your help and advice.

Hi Alexey!

This is a known issue I still didn't figure out how to solve. If you can help here in any aspect (for example, how did you figured out that the thing is in DAQ() code?), it is very welcome. Please - continue bug/feature discussions as well as this one on Github.

In short, WebTerminal had problems with mnemonics, and what I did to solve them is a bit crazy - I just copied the code from %X364 to let them work here, as that code was utility. Maybe, there is a better solution, but I didn't came to it because of my lack of experience at some point.

Alexey, any help is very welcome. Do you know where even to find ^JOBEXAM's code to investigate the problem? Thanks!

I mentioned DAQ just because it was mentioned earlier and it was really buggy till 2017.x. I was apparently wrong as you seem to have all mnemonics embedded.

As to JOBEXAM.INT, isn't it better to ask somebody inside ISC? IMHO, too many "innocent" utilities are shipped w/o sources.

It seems to fail on second line of this code sample:

set prevspace="^"_$zu(96,12)
u 0::"^%X364"    ; Set mnemonic space
u 0::prevspace

Nice! Thanks! At least I diagnosed the problem: that's because I/O redirection, there's no I/O redirection routines in a new mnespace if the routine performs any writes or reads.

We need to find a better way to intercept Caché process I/O. Any help is very welcome!

I got this while importing the classes... any ideas?

edit: Nevermind, I just hit back then import again.  It worked.  /shrug

 

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Set-Cookie: CSPSESSIONID-SP-57772-UP-csp-sys-=008000010000rGtpFVtkj20000ApozxNVGmmNXNIMCWko6aQ--; path=/csp/sys/;  httpOnly;
CACHE-CONTROL: no-cache
DATE: Mon, 13 Mar 2017 15:10:15 GMT
EXPIRES: Thu, 29 Oct 1998 17:04:19 GMT
PRAGMA: no-cache
Transfer-Encoding: chunked
Set-Cookie: CSPWSERVERID=fcc28c73d69ac223d5063debc53f259f1ee904fe; path=/; httpOnly;
91
<P><P><B><FONT COLOR="RED">CSP application did not respond within the timeout period</FONT></B><P>
The processing of the request was interrupted
0

That's strange, WebTerminal should successfully install from the first attempt, if there were no errors during installation.  Hope this won't occur again :)

Don't worry, I'm sure it was our environment... :)

Thank you Rich for noticing this. We’ll update the links shortly. This problem is related to WebTerminal repository migration to intersystems-community organization on Github. Follow WebTerminal there!