Using a linux editor inside VistA

From VistApedia
Revision as of 21:41, 10 March 2012 by NeilArmstrong (talk | contribs) (Added glossary link to Record~)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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: ^^<line count>^<linecount>^<fmdate>^^
       ;"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: ^^<line count>^<linecount>^<fmdate>^^
       ;"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