TMGPAT3 ;TMG/kst/Patching tools ;09/17/08 ;;1.0;TMG-LIB;**1**;09/17/08 ; ;"Kevin Toppenberg MD ;"GNU General Public License (GPL) applies ;"9/26/08 ;"======================================================================= ;" API -- Public Functions. ;"======================================================================= ;"NEWPACK -- Install a new package from ftp server. ;"CONSOLE --show how many patches for a package are available and have not been installed yet ;"RESCAN -- show how many patches for a package are available and have not been installed yet ;"ShowPatches(PckInit,Ver) -- show installed patches, using scroll box. ;"EditNotes -- launch an editor for editing notes about patching. ;"======================================================================= ;"Private Functions ;"======================================================================= ;"PrepAvail(pArray,Option) -- prepair an array with patch status, for use with Scroller^TMGUSRIF ;"HndOnSel(pArray,Option,Info) -- handle ON SELECT event from Scroller^TMGUSRIF ;"HndOnCmd(pArray,Option,Info) -- handle ON SELECT event from Scroller ;"StoreMissing(PckInit,pArray) store the list of missing patches with the pending patches ;"DownPck(PatchName,Option,Msg) -- Given a package name, ensure all pending patches are local. ;"$$Rpt1Avail^TMGPAT3(PatchName) ;"$$RptAvail^TMGPAT3(PckInit) ;"Scan4New(MaxDays,Option) -- scan all packages and determine how many patches are pending for each ;"Scan41(PckInit,MaxDays,Option) -- scan one package and determine how many patches are pending ;"Scan41a1Ver(PckInit,Ver,MaxDays,Option) -- scan one package and determine how many patches are pending ;"GetNew(PckInit,Ver,pArray,RefreshNeeded,Option) -- Get array of **just** patches still to be installed for a given package/version ;"GetAvail(PckInit,Ver,pArray,RefreshNeeded,Option) -- return array of all patches for a given package/version ;"GetPList(PckInit,Ver,pArray) -- get a list of applied patches, from PACKAGE file, into Array ;"PrepPatchList(PckInit,Ver,pShowArray,ByPatchNum) -- prepair the patch list for display in scroll box. ;"HndOnPCmd(pArray,Option,Info) -- handle ON SELECT event from Scroller ;"ShowAvail -- Show data that tallies the available patches. ;"IncLineCt(lineCount,pageLen) ;"======================================================================= ;"======================================================================= ;"NOTE: This Module should be re-written. Rather than store the data in the global ^TMG(... ;" the Fileman file 22709 should be used. As it is now, it is a duplication of organization. NEWPACK ;"Purpose: Install a new package from ftp server. new %,DIR,PckInit,Ver,X,Y,Msg do Logo^TMGPAT1 set %=1 write "Install a NEW PACKAGE from ftp.va.gov" do YN^DICN write ! if %'=1 goto NPDone set DIR(0)="F^2:4" set DIR("A")="Enter PACKAGE prefix (? for help)" set DIR("?")="Enter Namespace initials." set DIR("?",1)="Enter namespace package prefix initials." set DIR("?",2)="E.g. for Fileman, enter: DI" set DIR("?",3)="Enter ^ to abort." do ^DIR write ! ;"results in X and Y if Y="^" goto NPDone set PckInit=Y new Array,result write "Fetching info from VA ftp server..." set result=$$GetPckList^TMGKERNL(PckInit,.Array) write " Done.",! if result=0 goto NPDone new IEN9d4 set IEN9d4=+$order(^DIC(9.4,"C",PckInit,"")) if IEN9d4'>0 do goto NPDone . do AddMsg^TMGPAT2("Can't find PACKAGE named '"_PckInit_"'",1,.Msg) NPDone if $$ShowMsg^TMGPAT2(.Msg) write "Goodbye.",! quit ;"==================================================================== CONSOLE new Array,Option do PrepAvail("Array",.Option) set Option("FOOTER",1,1)="^ Exit" set Option("FOOTER",1,2)="? Help" set Option("FOOTER",1,3)="[F1] SHOW Compl" set Option("FOOTER",1,4)="[F3] Hx" set Option("FOOTER",1,5)="[F4] Downld Pak" set Option("FOOTER",1,6)="[F5] Notes" set Option("FOOTER",1,7)="[F6] Add Waiting" set Option("ON SELECT")="HndOnSel^TMGPAT3" set Option("ON CMD")="HndOnCmd^TMGPAT3" write # do Scroller^TMGUSRIF("Array",.Option) quit PrepAvail(pArray,Option) ;"Purpose: To prepair an array with patch status, for use with Scroller^TMGUSRIF ;"Input: pArray -- PASS BY NAME. Array to put info into. Prior data is killed. ;" Option -- PASS BY REFERENCE. Prior data is NOT killed. See Scroller^TMGUSRIF for details ;" Option("HIDE EMPTY")=0 OPTIONAL. Default is 0. If 1 then, entries with no patches. ;" Also-- Uses global variable... ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version)=Count ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"DATE REFRESHED")=Last date server checked. ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"PATCHES",######)=AAAA*NN.NN*NNNN SEQ #1234" ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"PATCHES",######)=AAAA*NN.NN*NNNN SEQ #1234" ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,"FULL NAME")=Package name ;"Results: None set pArray=$get(pArray) goto:(pArray="") pAvDone kill @pArray new Hinder,Blocked new oneLine,lineCt set lineCt=1 new PckInit set PckInit="" new grandTotal set grandTotal=0 new hideEmpty set hideEmpty=$get(Option("HIDE EMPTY"),1) for set PckInit=$order(^TMG("KIDS","PENDING PATCHES",PckInit)) quit:(PckInit="") do . new total set total=0 . new Ver set Ver="" . new PackageName set PackageName=$get(^TMG("KIDS","PENDING PATCHES",PckInit,"FULL NAME")) . for set Ver=$order(^TMG("KIDS","PENDING PATCHES",PckInit,Ver)) quit:(+Ver'>0) do . . set total=total+$get(^TMG("KIDS","PENDING PATCHES",PckInit,Ver)) . set grandTotal=grandTotal+total . if (total=0)&(hideEmpty=1) quit . set oneLine="("_PckInit_") "_PackageName_" " . set oneLine=$$LJ^XLFSTR($extract(oneLine,1,40),40)_"--> "_$$RJ^XLFSTR(total,3)_" patches. " . new tempArray,current,maxVer . set maxVer=0,Ver="",current="" . for set Ver=$order(^TMG("KIDS","PENDING PATCHES",PckInit,Ver)) quit:(+Ver'>0) do . . if Ver'>maxVer quit . . new temp set temp=$$GetLastPackage^TMGPAT1(PckInit,Ver) . . if temp'="" set maxVer=Ver,current=temp . set oneLine=oneLine_"Currently @ "_current . set @pArray@(lineCt,oneLine)=$piece(current,"*",1,2) . set lineCt=lineCt+1 . new i set i="" . for set i=$order(^TMG("KIDS","PENDING PATCHES",PckInit,"WAITING FOR",i)) quit:(i="") do . . set oneLine=" Waiting for "_i . . set @pArray@(lineCt,oneLine)=$piece(i,"*",1,2) . . set lineCt=lineCt+1 . . new init set init=$piece(i,"*",1) . . set Hinder(init,PckInit)="" . . set Blocked(PckInit)=1 if '$data(Hinder) goto pAV2 new count for count=1:1:5 do . set init="" for set init=$order(Hinder(init)) quit:(init="") do . . new init1,init2 set init1=init,init2="" . . for set init2=$order(Hinder(init,init2)) quit:(init2="") do . . . quit:(init2=init) . . . merge Hinder(init,init2)=Hinder(init2) set @pArray@(lineCt,"--- SUMMARY ------------------------------")="" set lineCt=lineCt+1 new spaces set $piece(spaces," ",20)=" " new ref set ref="Hinder" new hideArray set init="" for set ref=$query(@ref) quit:(ref="") do . if $$OREF^DILF($query(@ref))[$$OREF^DILF(ref) quit . new count,node,done set done=0 . for count=1:1:$qlength(ref) do quit:done . . set node=$qsubscript(ref,count) . . set oneLine=$select((count=1):"#",1:"") . . set oneLine=oneLine_$extract(spaces,1,count*3)_"Package "_node . . if (count=1)&($get(Blocked(node))=1) set done=1 quit . . if count=1 set oneLine=oneLine_" is hindering..." . . else if count<$qlength(ref) set oneLine=oneLine_", which is hindering..." . . if $get(hideArray(count))=oneLine quit . . set hideArray(count)=oneLine . . set @pArray@(lineCt,oneLine)="",lineCt=lineCt+1 pAV2 set Option("HEADER",1)="TMG Patch Helper-- "_grandTotal_" Patches to be installed in all packages." pAvDone quit HndOnSel(pArray,Option,Info) ;"Purpose: handle ON SELECT event from Scroller^TMGUSRIF ;"Input: pArray,Option,Info -- see documentation in Scroller^TMGUSRIF ;" Info has this: ;" Info("CURRENT LINE","NUMBER")=number currently highlighted line ;" Info("CURRENT LINE","TEXT")=Text of currently highlighted line ;" Info("CURRENT LINE","RETURN")=return value of currently highlighted line new PatchName,PckInit,Ver set PatchName=$get(Info("CURRENT LINE","RETURN")) do ParsePatchName^TMGPAT2(PatchName,.PckInit,.Ver) if (PckInit="")&(Ver="") do goto HOSDone . write "?? The line selected specify any command ??",! do DONEXTPK^TMGPAT1(PckInit,Ver) do PrepAvail(pArray,.Option) HOSDone do PressToCont^TMGUSRIF write # quit HndOnCmd(pArray,Option,Info) ;"Part of TestScrl ;"Purpose: handle ON SELECT event from Scroller ;"Input: pArray,Option,Info -- see documentation in Scroller ;" Info has this: ;" Info("USER INPUT")=input ;" Info("CURRENT LINE","NUMBER")=number currently highlighted line ;" Info("CURRENT LINE","TEXT")=Text of currently highlighted line ;" Info("CURRENT LINE","RETURN")=return value of currently highlighted line new input set input=$$UP^XLFSTR($get(Info("USER INPUT"))) if input["F2" do . set Option("FOOTER",1,3)="[F1] SHOW compl" . set Option("HIDE EMPTY")=1 else if input="RESCAN" do . do RESCAN else if input["F1" do . set Option("FOOTER",1,3)="[F2] HIDE compl" . set Option("HIDE EMPTY")=0 else if input["F3" do . new PatchName set PatchName=$get(Info("CURRENT LINE","RETURN")) quit:(PatchName="") . new PckInit,Ver . do ParsePatchName^TMGPAT2(PatchName,.PckInit,.Ver) . if $$ShowPatches(PckInit,Ver) else if input["F4" do . new PatchName set PatchName=$get(Info("CURRENT LINE","RETURN")) quit:(PatchName="") . new Option set Option("VERBOSE")=1 . new % set %=1 . write "Ensure that all pending patches for ",PatchName," have been downloaded" . do YN^DICN write ! . if %'=1 quit . do DownPck(PatchName,.Option) . do PressToCont^TMGUSRIF else if input["F5" do . do EditNotes . do PressToCont^TMGUSRIF else if input["F6" do ;"Add Waiting" . if Info("CURRENT LINE","TEXT")'["-->" do quit . . write !,"Please first select containing '-->'",! . . do PressToCont^TMGUSRIF . new PatchName set PatchName=$get(Info("CURRENT LINE","RETURN")) quit:(PatchName="") . new PckInit set PckInit=$piece(PatchName,"*",1) . new % set %=1 . write !,"Manually add a 'Waiting For' entry for ",PatchName . do YN^DICN write ! . if %'=1 quit . new DIR,DIRUT set DIR(0)="F",DIR("A")="Enter what "_PatchName_" is waiting for" . do ^DIR write ! if $data(DIRUT) quit . do AddMissing(PckInit,Y) . do PressToCont^TMGUSRIF else if input="NEWPACK" do . do NEWPACK else if input="?" do . write !,"Use UP and DOWN cursor keys to select package, then ENTER to work on.",! . write "Enter 'NEWPACK' to install a NEW package.",! . write "Enter 'RESCAN' to rescan the ftp.va.gov server",! . write "Enter ^ at the ':' prompt to quit",! . do PressToCont^TMGUSRIF else if input'="" do . write !,"Input ",$get(Info("USER INPUT"))," not recognized.",! . do PressToCont^TMGUSRIF do PrepAvail(pArray,.Option) write # quit StoreMissing(PckInit,pArray) ;"Purpose: to store the list of missing patches with the pending patches kill ^TMG("KIDS","PENDING PATCHES",PckInit,"WAITING FOR") merge ^TMG("KIDS","PENDING PATCHES",PckInit,"WAITING FOR")=@pArray quit AddMissing(PckInit,PatchName) ;"Purpose: Add a missing patche to pending patches set ^TMG("KIDS","PENDING PATCHES",PckInit,"WAITING FOR",PatchName)="" quit DownPck(PatchName,Option,Msg) ;"Purpose: given a patch name, ensure all pending patches are local. ;"Input: PatchName -- patch name, e.g. ABC*1.0*123 ;" Option -- Optional. PASS BY REFERENCE. ;" Option("VERBOSE")=1 --> puts output to console ;" Msg -- PASS BY REFERANCE, an OUT PARAMETER ;" Errors are stored in Msg("ERROR",x)=Message ;" Msg("ERROR")=count of last error ;" Message are store in Msg(x)=Message ;" Msg=count of last message+1 ;"Results: none new PckInit,Ver,PatchNum,seqNum,Info do ParsePatchName^TMGPAT2(PatchName,.PckInit,.Ver,.PatchNum,.seqNum) do Scan41a1Ver(PckInit,Ver,90) new total set total=+$get(^TMG("KIDS","PENDING PATCHES",PckInit,Ver)) new count set count=1 new patch set patch="" for set patch=$order(^TMG("KIDS","PENDING PATCHES",PckInit,Ver,"PATCHES",patch)) quit:(patch="") do . new patchName set patchName=$get(^TMG("KIDS","PENDING PATCHES",PckInit,Ver,"PATCHES",patch)) . if $get(Option("VERBOSE"))=1 write count,"/",total,". ---- ",patchName," ----",! . new IENS set IENS=$$GetIENS^TMGPAT2(patchName) quit:(IENS="") . if $$EnsureLocal^TMGPAT2(IENS,.Info,.Msg,.Option)=0 do . . do AddMsg^TMGPAT2("Unable to download patch to local file system.",1,Msg) . set count=count+1 if $get(Option("VERBOSE"))=1 do . if $$ShowMsg^TMGPAT2(.Msg) quit Rpt1Avail(PatchName) ;"Purpose: given a patch name (e.g. ABC*1.0*123), return pending patches. new PckInit,Ver,PatchNum,seqNum new count set count=-1 do ParsePatchName^TMGPAT2(PatchName,.PckInit,.Ver,.PatchNum,.seqNum) if ($get(PckInit)="")!($get(Ver)="") goto Rpt1Done do Scan41a1Ver(PckInit,Ver,90) set count=+$get(^TMG("KIDS","PENDING PATCHES",PckInit,Ver)) Rpt1Done quit count RptAvail(PckInit) ;"Purpose: given a package (e.g. ABC), return pending patches. new total set total=0 new Ver set Ver="" for set Ver=$order(^TMG("KIDS","PENDING PATCHES",PckInit,Ver)) quit:(+Ver'>0) do . set total=total+$get(^TMG("KIDS","PENDING PATCHES",PckInit,Ver)) quit total RESCAN ;"Purpose: To show how many patches for a package are available and have not been installed yet write ! new DUOUT,DIR set DIR("A")="Search ftp server if data is older than __ days old? (SLOW!)" set DIR("B")=90 set DIR(0)="N^0:999:0" do ^DIR write ! new Option set Option("VERBOSE")=0 if $get(DUOUT) quit do Scan4New(+Y,.Option) ;"if '$get(DUOUT) do ShowAvail SADone quit Scan4New(MaxDays,Option) ;"Purpose: to scan all packages and determine how many patches are pending for each ;"Input: MaxDays -- the number of days that old days can be used. If last refresh ;" was greater than this number, then ftp.va.gov is queried again. ;" Option -- Optional. PASS BY REFERENCE. ;" Option("VERBOSE")=1 --> puts output to console ;"Output: Results will be stored: ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version)=Count ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"DATE REFRESHED")=Last date server checked. ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"PATCHES",######)=AAAA*NN.NN*NNNN SEQ #1234" ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"PATCHES",######)=AAAA*NN.NN*NNNN SEQ #1234" ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,"FULL NAME")=Package name ;" ;"Results: none ;"NOTE: This function should be re-written. Rather than store the data in the global ^TMG(... ;" the Fileman file 22709 should be used. As it is now, it is a duplication of organization. set MaxDays=+$get(MaxDays) new PackageName set PackageName="" for set PackageName=$order(^DIC(9.4,"B",PackageName)) quit:(PackageName="") do . new IEN9d4 set IEN9d4=+$order(^DIC(9.4,"B",PackageName,"")) quit:(IEN9d4'>0) . new PckInit set PckInit=$piece($get(^DIC(9.4,IEN9d4,0)),"^",2) ;"0;2 = Package prefix . do Scan41(PckInit,MaxDays,.Option) quit Scan41(PckInit,MaxDays,Option) ;"Purpose: to scan one package and determine how many patches are pending ;"Input: PckInit -- Package Initials/prefix ;" MaxDays -- the cutoff for when to requery the server ;" Option -- PASS BY REFERENCE. ;" Option("VERBOSE")=1 to be verbose. ;"Output: Results will be stored: ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version)=Count ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"DATE REFRESHED")=Last date server checked. ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"PATCHES",######)=AAAA*NN.NN*NNNN SEQ #1234" ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"PATCHES",######)=AAAA*NN.NN*NNNN SEQ #1234" ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,"FULL NAME")=Package name ;"Results: none new IEN9d4 set IEN9d4=+$order(^DIC(9.4,"C",PckInit,"")) new PackageName set PackageName="" if IEN9d4>0 set PackageName=$piece($get(^DIC(9.4,IEN9d4,0)),"^",1) if $get(Option("VERBOSE"))=1 write "Checking Package: ",PackageName," (",PckInit,")...",! set ^TMG("KIDS","PENDING PATCHES",PckInit,"FULL NAME")=PackageName ;"kill ^TMG("KIDS","PENDING PATCHES",PckInit,"DATE REFRESHED") ;"force refresh set Ver="" for set Ver=$order(^DIC(9.4,IEN9d4,22,"B",Ver)) quit:(Ver="") do . do Scan41a1Ver(PckInit,Ver,MaxDays,.Option) quit Scan41a1Ver(PckInit,Ver,MaxDays,Option) ;"Purpose: to scan one package and determine how many patches are pending ;"Input: PckInit -- Package Initials/prefix ;" Ver -- The version of the Package ;" MaxDays -- the cutoff for when to requery the server ;" Option -- PASS BY REFERENCE. ;" Option("VERBOSE")=1 to be verbose. ;"Output: Results will be stored: ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version)=Count ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"DATE REFRESHED")=Last date server checked. ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"PATCHES",######)=AAAA*NN.NN*NNNN SEQ #1234" ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,Version,"PATCHES",######)=AAAA*NN.NN*NNNN SEQ #1234" ;" ^TMG("KIDS","PENDING PATCHES",PackageInitials,"FULL NAME")=Package name ;"Results: none ;"kill ^TMG("KIDS","PENDING PATCHES",PckInit,"DATE REFRESHED") ;"force refresh if $get(PckInit)="" goto S41Done if $get(Ver)="" goto S41Done set MaxDays=+$get(MaxDays,90) if $get(Option("VERBOSE"))=1 write " Ver: ",Ver," ",! new RefreshNeeded new lastCheck set lastCheck=$get(^TMG("KIDS","PENDING PATCHES",PckInit,"DATE REFRESHED")) if lastCheck'="" do . new X,Y,%DT,X1,X2 . set X=lastCheck,%DT="TS" . do ^%DT ;"result in Y . set X=0 . do NOW^%DTC ;"returns date in X . set X1=X,X2=Y . do ^%DTC ;"returns X=X1-X2 . set RefreshNeeded=(X>MaxDays) else set RefreshNeeded=1 new pArray set pArray=$name(^TMG("KIDS","PENDING PATCHES",PckInit,Ver,"PATCHES")) new count set count=$$GetNew(PckInit,Ver,pArray,RefreshNeeded,.Option) if $get(Option("VERBOSE"))=1 write " ",count," patches to be installed.",! set ^TMG("KIDS","PENDING PATCHES",PckInit,Ver)=count if RefreshNeeded do . new %,%I,X,Y . do NOW^%DTC set Y=% . X ^DD("DD") ;"result in Y . set ^TMG("KIDS","PENDING PATCHES",PckInit,"DATE REFRESHED")=Y S41Done quit GetNew(PckInit,Ver,pArray,RefreshNeeded,Option) ;"Purpose: Get array of **just** patches still to be installed for a given package/version ;"Input: PckInit -- this is the namespace of the package to get patches for, e.g. 'DI' for fileman ;" Ver -- the package version ;" pArray -- PASS BY NAME. An OUT PARAMETER. Format: ;" @pArray@(######)=AAAA*NN.NN*NNNN SEQ #1234" ;" @pArray@(######)=AAAA*NN.NN*NNNN SEQ #1234" ;" NeedsRefresh -- 0 if refreshing not needed (just ensure file exists) ;" Option -- Optional. PASS BY REFERENCE. ;" Option("VERBOSE")=1 --> puts output to console ;"Results: Number of patches still to be installed. new count set count=0 do GetAvail(PckInit,Ver,pArray,.RefreshNeeded,.Option) new LastPck set LastPck=$$GetLastPackage^TMGPAT1(PckInit,Ver) new lastSeq set lastSeq=+$piece(LastPck,"SEQ #",2) if lastSeq="" kill @pArray goto GNDone new i set i="" for set i=$order(@pArray@(i)) quit:(i="") do . if +i'>lastSeq kill @pArray@(i) quit . set count=count+1 GNDone quit count GetAvail(PckInit,Ver,pArray,RefreshNeeded,Option) ;"Purpose: return array of all patches for a given package/version ;"Input: PckInit -- this is the namespace of the package to get patches for, e.g. 'DI' for fileman ;" Ver -- the package version ;" pArray -- PASS BY NAME. An OUT PARAMETER. Format: ;" @pArray@(######)=AAAA*NN.NN*NNNN SEQ #1234" ;" @pArray@(######)=AAAA*NN.NN*NNNN SEQ #1234" ;" NeedsRefresh -- 0 if refreshing not needed (just ensure file exists) ;" Option -- Optional. PASS BY REFERENCE. ;" Option("VERBOSE")=1 --> puts output to console ;"results: none kill @pArray if $$RefreshPackge^TMGPAT2(PckInit,.Msg,.RefreshNeeded,.Option) new IEN9d4 set IEN9d4=+$order(^DIC(9.4,"C",PckInit,"")) if IEN9d4'>0 goto GADone new PckIEN set PckIEN=+$order(^TMG(22709,"B",IEN9d4,"")) if PckIEN'>0 goto GADone new VerIEN set VerIEN=+$order(^TMG(22709,PckIEN,1,"B",Ver,"")) if VerIEN'>0 set VerIEN=+$order(^TMG(22709,PckIEN,1,"B",$piece(Ver,".0",1),"")) if VerIEN'>0 goto GADone new patchIEN,nextSeq set patchIEN=0 for set patchIEN=$order(^TMG(22709,PckIEN,1,VerIEN,1,patchIEN)) quit:(patchIEN'>0) do . new node0 set node0=$get(^TMG(22709,PckIEN,1,VerIEN,1,patchIEN,0)) quit:(node0="") . new patchNum set patchNum=$piece(node0,"^",2) . new oneSeqNum set oneSeqNum=$piece(node0,"^",2) . if oneSeqNum'>0 set oneSeqNum=0 . set @pArray@($$RJ^XLFSTR(oneSeqNum,6,"0"))=PckInit_"*"_Ver_"*"_patchNum_" SEQ #"_oneSeqNum GADone quit TestPList ;"temp function. new PckInit,Ver,Array do GetPckVer^TMGPAT1(.PckInit,.Ver) if Ver="^" goto TPLDone if $$GetPList(PckInit,Ver,"Array")=0 goto TPLDone zwr Array(*) TPLDone quit GetPList(PckInit,Ver,pArray) ;"Purpose: to get a list of applied patches, from PACKAGE file, into Array ;"Input: ;"Input: PckInit -- this is the namespace of the package to get patches for, e.g. 'DI' for fileman ;" Ver -- the package version ;" pArray -- PASS BY NAME. An OUT PARAMETER. Format: ;" @pArray@(OrderNum)=PatchName ;" @pArray@(OrderNum,"i",".01")=PatchName ;" @pArray@(OrderNum,"i",".02")=Patch Date ;" @pArray@(OrderNum,"i",".03")=Applied By ;" @pArray@(OrderNum)=PatchName ;"Results: 1 if OK, 0 if error ; new result set result=1 ; new IEN9d4,IEN9d49 if $$GetPVIEN^TMGPAT1(PckInit,Ver,.IEN9d4,.IEN9d49)=0 set result=0 goto GPLDone ; new orderNum set orderNum=1 new patchIEN set patchIEN=0 for set patchIEN=$order(^DIC(9.4,IEN9d4,22,IEN9d49,"PAH",patchIEN)) quit:(+patchIEN'>0) do . new s set s=$get(^DIC(9.4,IEN9d4,22,IEN9d49,"PAH",patchIEN,0)) . set @pArray@(orderNum)=$piece(s,"^",1) ;"0;1=.01 --PATCH APPLICATION HISTORY . new IENS set IENS=patchIEN_","_IEN9d49_","_IEN9d4_"," . new TMGDATA,TMGMSG . do GETS^DIQ(9.4901,IENS,".01;.02;.03","","TMGDATA","TMGMSG") . merge @pArray@(orderNum,"i")=TMGDATA("9.4901",IENS) . ;"zwr TMGDATA(*) . set orderNum=orderNum+1 ; GPLDone quit result SHOWPLST ;"Purpose: query user for package and version, then show patches. new PckInit,Ver,Array do GetPckVer^TMGPAT1(.PckInit,.Ver) if Ver="^" goto SLPDone if $$ShowPatches(PckInit,Ver) SLPDone quit ShowPatches(PckInit,Ver) ;"Purpose: to show installed patches, using scroll box. ;"Input: PckInit -- this is the namespace of the package to get patches for, e.g. 'DI' for fileman ;" Ver -- the package version ;"Results: 1 if OK, 0 if error new TMGPCKI set TMGPCKI=PckInit ;"used in HndOnPCmd^TMGPAT3 new TMGPVER set TMGPVER=Ver ;"used in HndOnPCmd^TMGPAT3 new TMGSORT set TMGSORT=2 ;"sort by SEQ number. Used in HndOnPCmd^TMGPAT3 new TMGSARRAY set TMGSARRAY(0)="SORT by IEN order" set TMGSARRAY(1)="SORT by PATCH num" set TMGSARRAY(2)="SORT by SEQ num" new Option,ShowArray,result set Option("HEADER",1)="Applied patches for package "_PckInit_" "_TMGSARRAY(TMGSORT) set Option("FOOTER",1,1)="^ Done" set Option("FOOTER",1,2)="? Help" set Option("FOOTER",1,3)="[F1] "_TMGSARRAY(0) set Option("FOOTER",1,4)="[F2] Fix Missing PATCH" set Option("FOOTER",1,5)="[F3] Fix Missing SEQ" set Option("ON CMD")="HndOnPCmd^TMGPAT3" set Option("SHOW INDEX")=1 do PrepPatchList(PckInit,Ver,"ShowArray",TMGSORT) if $data(ShowArray)=0 do . do AskVer^TMGPAT1(PckInit,.Ver) . do PrepPatchList(PckInit,Ver,"ShowArray",TMGSORT) write # do Scroller^TMGUSRIF("ShowArray",.Option) SPDone quit 1 PrepPatchList(PckInit,Ver,pShowArray,Mode) ;"Purpose: to prepair the patch list for display in scroll box. ;"Input: PckInit -- this is the namespace of the package to get patches for, e.g. 'DI' for fileman ;" Ver -- the package version ;" pShowArray -- PASS BY NAME, an OUT PARAMATER ;" Mode -- OPTIONAL. 0: Otherwise by IEN order ;" 1: Then sorted by patch number, ;" 2: Otherwise by SEQ Num set ByPatchNum=+$get(ByPatchNum) new index set index=1 new showI set showI=1 kill @pShowArray new Array,tempA if $$GetPList(.PckInit,.Ver,"Array")=0 goto PPLDone if Mode=0 goto PPL2 new Num for set index=$order(Array(index)) quit:(index="") do . if Mode=1 set Num=+$piece(Array(index)," ",1) . else set Num=+$piece(Array(index),"SEQ #",2) . new s,patch . set patch=PckInit_"*"_Ver_"*"_$get(Array(index)) . set s=$$LJ^XLFSTR(patch,25) . set s=s_" Applied: "_$get(Array(index,"i",".02"))_" " . set s=s_" By: "_$get(Array(index,"i",".03")) . set tempA(Num)=s set Num="" for set Num=$order(tempA(Num)) quit:(Num="") do . new s set s=$get(tempA(Num)) quit:(s="") . set @pShowArray@(showI,s)=$piece(s," Applied",1) . set showI=showI+1 goto PPLDone PPL2 for set index=$order(Array(index)) quit:(index="") do . new s,patch . set patch=PckInit_"*"_Ver_"*"_$get(Array(index)) . set s=$$LJ^XLFSTR(patch,25) . set s=s_" Applied: "_$get(Array(index,"i",".02"))_" " . set s=s_" By: "_$get(Array(index,"i",".03")) . set @pShowArray@(showI,s)=patch . set showI=showI+1 PPLDone quit HndOnPCmd(pArray,Option,Info) ;"Purpose: handle ON SELECT event from Scroller ;"Input: pArray,Option,Info -- see documentation in Scroller ;" Info has this: Info("USER INPUT")=input ;"NOTE: uses global-scope vars set up in ShowPatches: ;" TMGPCKI,TMGPVER,TMGSORT new input set input=$$UP^XLFSTR($get(Info("USER INPUT"))) if input["F1" do . set Option("HEADER",1)="Applied patches for package "_PckInit_" "_TMGSARRAY(TMGSORT) . do PrepPatchList(TMGPCKI,TMGPVER,pArray,TMGSORT) . set TMGSORT=TMGSORT+1 . if TMGSORT=3 set TMGSORT=0 . set Option("FOOTER",1,3)="[F1] "_TMGSARRAY(TMGSORT) else if input["F2" do . do FixMisInit^TMGPAT1(TMGPCKI,TMGPVER,0) . do PressToCont^TMGUSRIF else if input["F3" do . do FixMisInit^TMGPAT1(TMGPCKI,TMGPVER,1) . do PrepPatchList(TMGPCKI,TMGPVER,pArray,TMGSORT) . do PressToCont^TMGUSRIF else if input="?" do . write !,"Use UP and DOWN cursor keys to scroll.",! . write "Press F1 or F2 to change sorting",! . write "Enter ^ at the ':' prompt when done",! . do PressToCont^TMGUSRIF else if input'="" do . write !,"Input ",$get(Info("USER INPUT"))," not recognized.",! . do PressToCont^TMGUSRIF write # quit EditNotes ;"Purpose: to launch an editor for editing notes about patching. new FPName set FPName=$get(^TMG("KIDS","PATCH DIR"),"/tmp/")_"Patch_Notes.txt" if $$EditHFSFile^TMGKERNL(FPName) quit