TMGPAT4 ;TMG/kst/Patching tools ;09/22/08 ;;1.0;TMG-LIB;**1**;09/22/08 ; ;"Kevin Toppenberg MD ;"GNU General Public License (GPL) applies ;"9/26/08 ;"======================================================================= ;" API -- Public Functions. ;"======================================================================= ;"Analyze(Info,Option) -- look at a patch TXT file and extract useful information ;"ShowAnalysis(Info,Msg) -- display to user analysis info in a meaningful way. ;"======================================================================= ;"Private Functions ;"======================================================================= ;"GetReqPatches(Array,Info) -- scan Array, holding TXT file, and assemble list of required patches ;"GetCategory(Array,Info) -- scan Array, holding TXT file, and assemble category entries ;"GetFNames(Array,Info) -- scan Array, holding TXT file, and scavenge and .KID file names ;"GetMultPatches(Array,Info,Option) -- scan Array, holding TXT file, and matching multi-patch info ;"GetSeq(PatchName,Mode,Option,URL) -- For a given patch (e.g. DI*12*123), return the SEQ # ;"FindMultPatch(PatchName,PckInit,Option,URL) -- ;"CheckDelta(Info) -- compare the requirements in Info vs existing system. ;"PAnalyze(Info,Option) -- look at a patch KID file and extract useful information ;"PGetReqPatches(Array,Info) -- scan Array, holding KID file, and assemble list of required patches ;"CheckLocal(pArray,Option) -- check a KIDS for conflict with local modifications. ;"Chk1Routine(routine) -- see if one routine has any local modifications. ;"======================================================================= Analyze(Info,Option) ;"Purpose: To look at a patch TXT file and extract useful information ;"Input: Info -- PASS BY REFERENCE, and IN and OUT PARAMETER. ;" Input: Info("PATH") -- path on HFS of TXT file ;" Info("TEXT FILE") -- filename on HFS of TXT file ;" Output: ;" Info("SPECIFIED REQ",PatchName)="" ;" Info("SPECIFIED REQ",PatchName)="" ;" Info("MULTI-PATCH","FILENAME")=FileName ;" Info("MULTI-PATCH","CONTAINS",PatchName)="" ;" Info("MULTI-PATCH","CONTAINS",patchName,"LATEST IN THIS PACKAGE")=lastPatch ;" Option -- optional. Pass by reference. ;" Option("VERBOSE")=1, means messaages also written directly to output ;" ;"Results: none new FPath,FName,Array set FPath=$get(Info("PATH")) if FPath="" goto ADone set FName=$get(Info("TEXT FILE")) if FName="" goto ADone if $$FTG^%ZISH(FPath,FName,"Array(0)",1)=0 goto ADone do GetReqPatches(.Array,.Info) do GetCategory(.Array,.Info) do GetMultPatches(.Array,.Info,.Option) do GetFNames(.Array,.Info) do GetSubject(.Array,.Info) do CheckDelta(.Info) ADone quit PAnalyze(Info,Option) ;"Purpose: To look at a patch KID file and extract useful information ;"Input: Info -- PASS BY REFERENCE, and IN and OUT PARAMETER. ;" Input: Info("PATH") -- path on HFS of TXT file ;" Info("KID FILE") -- filename on HFS of KID file ;" Output: Info("SPECIFIED REQ",PatchName)="" ;" Info("SPECIFIED REQ",PatchName)="" ;" Option -- optional. Pass by reference. ;" Option("VERBOSE")=1, means messaages also written directly to output ;"Results: none new FPath,FName,Array set FPath=$get(Info("PATH")) if FPath="" goto PADone set FName=$get(Info("KID FILE")) if FName="" goto PADone if $$FTG^%ZISH(FPath,FName,"Array(0)",1)=0 goto PADone do PGetReqPatches(.Array,.Info) ;"do PCheckDelta(.Info) PADone quit GetReqPatches(Array,Info) ;"Purpose: to scan Array, holding TXT file, and assemble list of required patches ;"Input: Array -- PASS BY REFERENCE -- holds TXT file ;" Info -- PASS BY REFERENCE. Data added as follows: ;" Info("SPECIFIED REQ",PatchName)="" ;"Results: none new done set done=0 new foundList set foundList=0 new i set i="" for set i=$order(Array(i)) quit:(i="")!done do . new s set s=$$TRIM^XLFSTR($get(Array(i))) . set s=$$STRIP^XLFSTR(s,$char(10)) . set s=$$STRIP^XLFSTR(s,$char(13)) . if (foundList=1)&(s="") set done=1 quit . if s["Associated patches:" set foundList=1 . if foundList do . . new onePatch . . set onePatch=$$TRIM^XLFSTR($piece($piece(s,")",2)," ",1)) ;"e.g. (v)PX*1*29 <<= must be installed BEFORE `PX*1*121' . . if onePatch="" quit . . set Info("SPECIFIED REQ",onePatch)="" quit PGetReqPatches(Array,Info) ;"Purpose: to scan Array, holding KID file, and assemble list of required patches ;"Input: Array -- PASS BY REFERENCE -- holds TXT file ;" Info -- PASS BY REFERENCE. Data added as follows: ;" Info("SPECIFIED REQ",PatchName)="" ;"Results: none ; new done set done=0 new foundList set foundList=0 new i set i="" for set i=$order(Array(i)) quit:(i="")!done do . new s set s=$$TRIM^XLFSTR($get(Array(i))) . set s=$$STRIP^XLFSTR(s,$char(10)) . set s=$$STRIP^XLFSTR(s,$char(13)) . if s'["REQB" quit . if (s["""REQB"",""B""")!(s["""REQB"",0") quit . set i=$order(Array(i)) quit:(i="") . new onePatch set onePatch=$piece($get(Array(i)),"^",1) quit:(onePatch="") . set Info("SPECIFIED REQ",onePatch)="" quit GetCategory(Array,Info) ;"Purpose: to scan Array, holding TXT file, and assemble category entries ;"Input: Array -- PASS BY REFERENCE -- holds TXT file ;" Info -- PASS BY REFERENCE. Data added as follows: ;" Info("PATCH CATEGORY",name)="" ;"Results: none new done set done=0 new foundList set foundList=0 new i set i="" for set i=$order(Array(i)) quit:(i="")!done do . new s set s=$$TRIM^XLFSTR($get(Array(i))) . set s=$$STRIP^XLFSTR(s,$char(10)) . set s=$$STRIP^XLFSTR(s,$char(13)) . if (foundList=1)&(s="") set done=1 quit . if s["Category:" set foundList=1 . if foundList do . . new onePatch . . set onePatch=$$TRIM^XLFSTR($piece(s,"-",2)) ;"e.g. - Routine . . if onePatch="" quit . . set Info("PATCH CATEGORY",onePatch)="" quit GetFNames(Array,Info) ;"Purpose: to scan Array, holding TXT file, and scavenge and .KID file names ;"Input: Array -- PASS BY REFERENCE -- holds TXT file ;" Info -- PASS BY REFERENCE. Data added as follows: ;" Info("MISC KID FILES",name)="" ;"Results: none new i set i="" for set i=$order(Array(i)) quit:(i="") do . new s set s=$$TRIM^XLFSTR($get(Array(i))) . set s=$$STRIP^XLFSTR(s,$char(10)) . set s=$$STRIP^XLFSTR(s,$char(13)) . if $$UP^XLFSTR(s)[".KID" do ;"NOTE: this system will only get 1 filename per line... . . new kidS . . if s[".kid" set kidS=".kid" . . else set kidS=".KID" . . if $piece(s,kidS,1)="" quit . . new FName set FName=$$GetWord^TMGSTUTL(s,$find(s,kidS)-1," "," ") . . if $extract(FName,$length(FName))="." set FName=$$TRIM^XLFSTR(FName,"R",".") . . if (FName="") quit . . set Info("MISC KID FILES",FName)="" quit GetSubject(Array,Info) ;"Purpose: to scan Array, holding TXT file, and scavenge SUBJECT line ;"Input: Array -- PASS BY REFERENCE -- holds TXT file ;" Info -- PASS BY REFERENCE. Data added as follows: ;" Info("SUBJECT",name)="" ;"Results: none new i set i="" new done set done=0 for set i=$order(Array(i)) quit:(i="")!done do . new s set s=$$TRIM^XLFSTR($get(Array(i))) . set s=$$STRIP^XLFSTR(s,$char(10)) . set s=$$STRIP^XLFSTR(s,$char(13)) . if s["Subject:" do . . new subj set subj=$piece(s,"Subject:",2) . . if subj="" quit . . set Info("SUBJECT",subj)="" . . set done=1 quit GetMultPatches(Array,Info,Option) ;"Purpose: to scan Array, holding TXT file, and matching multi-patch info ;"Input: Array -- PASS BY REFERENCE -- holds TXT file ;" Info -- PASS BY REFERENCE. Data added as follows: ;" Info("MULTI-PATCH","FILENAME")=FileName ;" Info("MULTI-PATCH","CONTAINS",PatchName)="" ;" Option -- optional. Pass by reference. ;" Option("VERBOSE")=1, means messaages also written directly to output ;"Results: none new done set done=0 new foundList,foundSection set (foundList,foundSection)=0 new fileName set fileName="" new i set i="" for set i=$order(Array(i)) quit:(i="")!done do . new s set s=$$TRIM^XLFSTR($get(Array(i))) . if (foundList=1)&(s="") set done=1 quit . if s["SOFTWARE AND DOCUMENTATION RETRIEVAL" do . . set foundSection=1 . if foundList do quit . . if s["---" quit . . if s="" set done=1 quit . . new tempS set tempS=$$TRIM^XLFSTR(s) . . new onePatch . . set onePatch=$$GetWord^TMGSTUTL(tempS,$length(tempS)-1) . . if onePatch="" quit . . new tempName set tempName=$$GetSeq(onePatch,1) . . if tempName="" set onePatch=onePatch_" SEQ #???" . . else set onePatch=tempName . . set Info("MULTI-PATCH","CONTAINS",onePatch)="" . if foundSection do . . if s["Patch(es)" set foundList=1 quit . . new tempS set tempS=$$UP^XLFSTR(s) . . if tempS[".KID" do . . . new p set p=$find(tempS,".KID")-1 . . . set tempS=$$GetWord^TMGSTUTL(s,p) quit:tempS="" . . . for quit:($$UP^XLFSTR($e(tempS,$length(tempS)))="D")!(tempS="") do . . . . set tempS=$e(tempS,1,$length(tempS)-1) . . . set Info("MULTI-PATCH","FILENAME")=tempS quit GetSeq(PatchName,Mode,Option,URL) ;"Purpose: For a given patch (e.g. DI*12*123), return name with the SEQ # ;"Input: PatchName -- e.g. DI*12*123 ;" Mode -- OPTIONAL (0 is default) -- 0: search .KID & .TXT; 1: search .TXT files only, 2: search .KID only ;" Option -- optional. Pass by reference. ;" Option("VERBOSE")=1, means messaages also written directly to output ;" URL -- PASS BY REFERENCE, an OUT PARAMETER ;"Output: in addition to OUT PARAMETERS, the following global variables are set: ;" set ^TMG("KIDS","PATCH NAMES",PckInit,Ver,PatchNum,oneSeqNum)="" ;" set ^TMG("KIDS","PATCH NAMES",PckInit,"COMBINED",result)=URL ;"Results: returns patch name with seq #, e.g. 'DI*12*123 SEQ #123' ;" or "" if problem. new result set result="" new PckInit,Ver,PatchNum set PatchName=$get(PatchName) set PckInit=$piece(PatchName,"*",1) set Ver=$piece(PatchName,"*",2) set PatchNum=$piece(PatchName,"*",3) if (PckInit="")!(Ver="")!(PatchNum="") goto GSqDone set result=$get(^TMG("KIDS","PATCH NAMES",PckInit,Ver,PatchNum)) ;"stored from prior search if result'="" do goto GSqDone . if result="???" set result="" quit . set URL=$get(^TMG("KIDS","PATCH NAMES",PckInit,"COMBINED",result)) new verbose set verbose=$get(Option("VERBOSE")) set Mode=+$get(Mode) new FPath,FName,Array,abort set abort=0 set FPath=$get(^TMG("KIDS","PATCH DIR"),"/tmp/") set FName="ftp.va.gov-dirFor-"_PckInit if '$$FileExists^TMGIOUTL(FPath_FName) do . if verbose do . . write "Finding Sequence # for ",PatchName,! . . write "Getting directory information from VA ftp server..." . if $$GetPckList^TMGKERNL(PckInit,.Array,1)=0 set abort=1 . if verbose write " Done.",! else do . if $$FTG^%ZISH(FPath,FName,"Array(0)",1)=0 set abort=1 quit if abort goto GSqDone new found set found=0 new i set i=0 ;"skip first line, a header line for set i=$order(Array(i)) quit:(i="")!found do . new Name,Path,FullNamePath,oneVer,onePatchNum,oneSeqNum . set FullNamePath=$get(Array(i)) quit:FullNamePath="" . do SplitFNamePath^TMGIOUTL(FullNamePath,.Path,.Name,"/") . if Name="" quit . new tempName set tempName=$$UP^XLFSTR(Name) . if Mode=0,(tempName'[".TXT")&((tempName'[".KID")) quit . else if Mode=1,(tempName'[".TXT") quit . else if Mode=2,(tempName'[".KID") quit . set oneVer=$piece($piece(tempName,"_",1),"-",2) quit:(oneVer="") . if oneVer?.N1"P".N set oneVer=$translate(Ver,"P",".") . set oneSeqNum=$piece($piece(tempName,"_",2),"-",2) quit:(oneSeqNum="") . set onePatchNum=$piece($piece(tempName,"_",3),"-",2) quit:(onePatchNum="") . set onePatchNum=$piece(onePatchNum,".",1) quit:(onePatchNum="") . if onePatchNum=PatchNum do . . set result=PatchName_" SEQ #"_oneSeqNum . . set URL=FullNamePath . . set found=1 . . set ^TMG("KIDS","PATCH NAMES",PckInit,Ver,PatchNum)=result . . set ^TMG("KIDS","PATCH NAMES",PckInit,"COMBINED",result)=URL GSqDone if result="" do . if (PckInit="")!(Ver="")!(PatchNum="") quit . set ^TMG("KIDS","PATCH NAMES",PckInit,Ver,PatchNum)="???" quit result FindMultPatch(PatchName,PckInit,Option,URL,Info) ;"Purpose: Search through downloaded directory file for patch, and return URL ;"Input: PatchName -- e.g. CSV_12_1234.KID ;" PckInit -- the initials for the package. ;" Option -- optional. Pass by reference. ;" Option("VERBOSE")=1, means messaages also written directly to output ;" URL -- PASS BY REFERENCE, an OUT PARAMETER ;" Info -- PASS BY REFERENCE ;" Info("KID URL")=URL on server for KID file ;" Info("TEXT URL")=URL on server for TXT file ;"Results: 1 if found, 0 if not found or problem. new result set result=0 set PatchName=$get(PatchName) goto:(PatchName="") FMPDone new verbose set verbose=$get(Option("VERBOSE")) new FPath,FName,Array,abort set abort=0 set FPath=$get(^TMG("KIDS","PATCH DIR"),"/tmp/") set FName="ftp.va.gov-dirFor-"_PckInit if '$$FileExists^TMGIOUTL(FPath_FName) do . if verbose do . . write "Finding Patch: ",PatchName,! . . write "Getting directory information from VA ftp server..." . if $$GetPckList^TMGKERNL(PckInit,.Array,1)=0 set abort=1 . if verbose write " Done.",! else do . if $$FTG^%ZISH(FPath,FName,"Array(0)",1)=0 set abort=1 quit if abort goto FMPDone new i set i=0 ;"skip first line, a header line for set i=$order(Array(i)) quit:(i="")!(result=1) do . new Name,Path,FullNamePath,tempName . set FullNamePath=$get(Array(i)) quit:FullNamePath="" . do SplitFNamePath^TMGIOUTL(FullNamePath,.Path,.Name,"/") . if Name="" quit . if $$UP^XLFSTR(Name)=$$UP^XLFSTR(PatchName) do . . set URL=FullNamePath . . set result=1 if result=0 do ;"last try to come up with filename. . new serverPath set serverPath=$get(Info("KID URL")) . if serverPath="" set serverPath=$get(Info("TEXT URL")) . set serverPath=$$PathExtract^TMGIOUTL(serverPath) . set serverPath=$piece(serverPath,"ftp://",2) . set URL=serverPath_PatchName . set result=1 ;"this is not a sure 'find', more of a hopeful guess that it will be in same directory. FMPDone quit result CheckDelta(Info) ;"Purpose: to compare the requirements in Info (as created by Analyze) and ;" determine differences in existing system. ;"Input: Info. PASS BY REFERENCE ;" Info("PATH") -- path on HFS of TXT file ;" Info("TEXT FILE") -- filename on HFS of TXT file ;" Info("SPECIFIED REQ",PatchName)="" ;" Info("MULTI-PATCH","FILENAME")=FileName ;" Info("MULTI-PATCH","CONTAINS",PatchName)="" ;" Info("MULTI-PATCH","CONTAINS",patchName,"LATEST IN THIS PACKAGE")=lastPatch ;"Output: ;" Info("SPECIFIED REQ",PatchName)="OK, Installed." or "Still Needed" ;" Info("STILL NEEDED",reqPatch)="" new reqPatch set reqPatch="" for set reqPatch=$order(Info("SPECIFIED REQ",reqPatch)) quit:(reqPatch="") do . new tempS . if $$IsInstalled^TMGPAT2(reqPatch) set tempS="OK, Installed." . else do . . set tempS="Still Needed." . . new s2 set s2=$$GetSeq(reqPatch) if s2="" set s2=reqPatch . . set Info("STILL NEEDED",s2)="" . set Info("SPECIFIED REQ",reqPatch)=tempS new patchName set patchName="" for set patchName=$order(Info("MULTI-PATCH","CONTAINS",patchName)) quit:(patchName="") do . new PckInit set PckInit=$piece(patchName,"*",1) . new Ver set Ver=$piece(patchName,"*",2) . set patchNum=$piece($piece(patchName,"*",3)," ",1) . new seqNum set seqNum=$piece(patchName,"SEQ #",2) . if (PckInit="")!(Ver="")!(patchNum="") quit . new lastPatch set lastPatch=$$GetLastPackage^TMGPAT1(PckInit,Ver) ;"returns e.g. 'DI*22.0*140 SEQ# 123' . set Info("MULTI-PATCH","CONTAINS",patchName,"LATEST IN THIS PACKAGE")=lastPatch . new lastSeqNum set lastSeqNum=$piece(lastPatch,"SEQ #",2) . if lastSeqNum10)!result . new ref set ref="+"_line_"^"_routine . new s set s=$TEXT(@ref) . if s="" set blankCt=blankCt+1 . set result=(s["//kt") . set line=line+1 quit result