Using a linux editor inside VistA

Back to Programming VistA Issues

The screen editor in VistA lacks some of the features provided by linux editors. But how to have VistA use your favorite editor? Here's how...

First, make new Record in the ALTERNATE EDITOR file like this: NAME: JOE - LINUX EDITOR ACTIVATION CODE FROM DIWE: DO EDIT^TMGEDIT("joe") DESCRIPTION: This will evoke the linux editor 'joe'

Then, in the user's entry/Record in file 200 (NEW PERSON), in field PREFERRED EDITOR (#31.3), enter this new editor (e.g. "JOE - LINUX EDITOR") Next, put the following code into a file named TMGEDIT.m

EDIT(Editor) ;"Purpose: This will be a shell for a linux editor        ;"Input: Editor -- the name of the linux editor to use (i.e. vim, joe, pico etc) ;"             Allowed values: joe,vim,pico        ;"Note: When this function gets called, VistA sets up some variables ;"     first to tell what should be edited etc.        ;"      DIC=The global root of the WP field where the text to be edited is        ;"              stored (or where new text should be stored)        ;"              e.g. "^TMG(22702,27,DV,"        ;"      (DV is also predefined, so reference to DV in DIC is covered.)        ;"      There are other variables set up re margins etc.  I will be ignoring these.        new result set result=0        new GlobalP        ;"By limiting value to certain values, it prevents a rouge user from putting a wedged        ;"linux command into "Editor" and executing a system command through zsystem.        set Editor=$get(Editor,"vim")        if (Editor'="vim")&(Editor'="joe")&(Editor'="pico") goto EditAbort        new EditErrFile set EditErrFile="/tmp/trashjoeoutput.txt"        set GlobalP=$extract(DIC,1,$length(DIC)-1)_")"  ;"convert to closed form        new Filename set Filename=$$UNIQUE^%ZISUTL("/tmp/vistaedit.tmp") set result=$$WP2HFSfp^TMGIOUTL(GlobalP,Filename) if result=0 goto EditDone new HookCmd set HookCmd=Editor_" "_Filename_" 2>"_EditErrFile zsystem HookCmd set result=$ZSYSTEM&255 ;"get result of execution. (low byte only). 0=success        if result>0 goto EditDone        ;"read file back into global WP                 set result=$$HFS2WPfp^TMGIOUTL(Filename,GlobalP) if result=1 do EditDone new temp set temp=$$DelFile^TMGIOUTL(Filename) set temp=$$DelFile^TMGIOUTL(Filename_"~") ;"delete joe backup file       set temp=$$DelFile^TMGIOUTL(EditErrFile) EditAbort                quit Next, put the following functions into a file named TMGIOUTL.m

SplitFNamePath(FullNamePath,OutPath,OutName,NodeDiv) ;"SCOPE: Public       ;"Purpose: Take FullNamePath, and split into name and path. ;"Input: FullNamePath: String to process.         ;"                e.g.: "/tmp/myfilename.txt" ;"               NOTICE: IF PASSED BY REFERENCE, WILL BE CHANGED TO FILENAME!        ;"        OutName: MUST BE PASSED BY REFERENCE. This is an OUT parameter ;"       OutPath: MUST BE PASSED BY REFERENCE.  This is an OUT parameter        ;"        NodeDiv: [OPTIONAL] -- the character that separates folders (e.g. "/") ;"               if not supplied, then default value is "/"        ;"Output: The resulting file name is put into OutName, ;"               e.g.: "myfilename.txt"        ;"        and the path is put into OutPath. ;"               e.g.: "/tmp/"        ;"Result: None. set OutPath="" set OutName="" new PathNode set NodeDiv=$get(NodeDiv,"/") set FullNamePath=$get(FullNamePath) SPN1 if (FullNamePath[NodeDiv)=0 set OutName=FullNamePath goto SPNDone set PathNode=$piece(FullNamePath,NodeDiv,1) set OutPath=OutPath_PathNode_NodeDiv set $piece(FullNamePath,NodeDiv,1)="" set FullNamePath=$extract(FullNamePath,2,255) goto SPN1 SPNDone quit

WP2HFS(GlobalP,path,filename) ;"Purpose: To write a WP field to a Host-File-System file       ;"Input: GlobalP -- The reference to the header node (e.g.  ^TMG(22702,99,1) in example below) ;"        path: for the output file, the path up to, but not including, the filename        ;"         filename -- the filename to save to in the host file system. ;"        If file already exists, it will be overwritten.        ;"Note:  The format of a WP field is as follows: ;"     e.g.    ^TMG(22702,99,1,0) = ^^4^4^3050118^        ;"               ^TMG(22702,99,1,1,0) = Here is the first line of text ;"              ^TMG(22702,99,1,2,0) = And here is another line        ;"               ^TMG(22702,99,1,3,0) = ;"              ^TMG(22702,99,1,4,0) = And here is a final line        ;"  And the format of the 0 node is: ^^ ^ ^ ^^ ;"Result: 0 if failure, 1 if success       ;"Assumptions: That GlobalP is a valid reference to a WP field new result set result=0 ;"default to failure       if $data(GlobalP)&($data(path))&($data(filename)) do        . new TMGWP         . merge TMGWP=@GlobalP        . set result=$$GTF^%ZISH("TMGWP(1,0)",1,path,filename)        quit result

WP2HFSfp(GlobalP,pathfilename) ;"Purpose: To provide an interface to WP2HFS for cases when filename is not already separated from path       ;"Result: 0 if failure, 1 if success new path,filename,result do SplitFNamePath(.pathfilename,.path,.filename) set result=$$WP2HFS(.GlobalP,.path,.filename) quit result

HFS2WP(path,filename,GlobalP) ;"Purpose: To read a WP field from a Host-File-System file       ;"Input: path: for the output file, the path up to, but not including, the filename ;"        filename -- the filename to save to in the host file system.         ;"If file already exists, it will be overwritten. ;"        GlobalP -- The reference to the header node (e.g.  ^TMG(22702,99,1) in example below)        ;"Note:  The format of a WP field is as follows: ;"     e.g.    ^TMG(22702,99,1,0) = ^^4^4^3050118^        ;"               ^TMG(22702,99,1,1,0) = Here is the first line of text ;"              ^TMG(22702,99,1,2,0) = And here is another line        ;"               ^TMG(22702,99,1,3,0) = ;"              ^TMG(22702,99,1,4,0) = And here is a final line        ;"  And the format of the 0 node is: ^^ ^ ^ ^^ ;"Result: 0 if failure, 1 if success       ;"Assumptions: That GlobalP is a valid reference to a WP field new result set result=0 ;"default to failure       if $data(GlobalP)&($data(path))&($data(filename)) do        . new TMGWP,WP        . set result=$$FTG^%ZISH(path,filename,"TMGWP(1,0)",1)        . if result=0 quit        . ;"Scan for overflow nodes, and integrate into main body . new i set i=$order(TMGWP("")) . if i'="" for do  quit:(i="") . . if $data(TMGWP(i,"OVF")) do       .. . new j set j=$order(TMGWP(i,"OVF","")) . . . if j'="" for do  quit:(j="") . . . . new n set n=i+(j/10) . . . . set TMGWP(n,0)=TMGWP(i,"OVF",j) . . . . set j=$order(TMGWP(i,"OVF",j)) . . . kill TMGWP(i,"OVF") . . set i=$order(TMGWP(i)) . ;"Now copy into another variable, renumbering lines (in case there were overflow lines)       . set i=$order(TMGWP(""))        . set j=0        . if i'="" for  do  quit:(i="")        . . set j=j+1        . . set WP(j,0)=TMGWP(i,0)        . . set i=$order(TMGWP(i))        . ;"now create a header node . do NOW^%DTC ;"returns result in X        . set WP(0)="^^"_j_"^"_j_"^"_X_"^^"        . ;"now put WP into global reference. . kill @GlobalP . merge @GlobalP=WP quit result

HFS2WPfp(pathfilename,GlobalP) ;"Purpose: To provide an interface to HFS2WP for cases when filename is not already separated from path       ;"Result: 0 if failure, 1 if success new path,filename,result do SplitFNamePath(.pathfilename,.path,.filename) set result=$$HFS2WP(.path,.filename,.GlobalP) quit result

DelFile(pathfilename) ;"Purpose: to delete one file on host file system       new path,filename,result        new TMGFile        do SplitFNamePath(.pathfilename,.path,.filename)        set TMGFile(filename)=""        set result=$$DEL^%ZISH(path,"TMGFile")        quit result

That should do it...

Good luck and happy editing. Kevin Toppenberg