mirror of
https://github.com/yabwon/SAS_PACKAGES.git
synced 2026-06-16 06:50:20 +00:00
75a8b77406
SAS Packages Framework, version `20260615` Changes: - Documentation update. Answer to issue: https://github.com/yabwon/SAS_PACKAGES/issues/136
286 lines
15 KiB
SAS
286 lines
15 KiB
SAS
/*+headerPackage+*/
|
|
/**############################################################################**/
|
|
/* */
|
|
/* Copyright Bartosz Jablonski, since July 2019 onward. */
|
|
/* */
|
|
/* Code is free and open source. If you want - you can use it. */
|
|
/* I tested it the best I could */
|
|
/* but it comes with absolutely no warranty whatsoever. */
|
|
/* If you cause any damage or something - it will be your own fault. */
|
|
/* You have been warned! You are using it on your own risk. */
|
|
/* However, if you decide to use it do not forget to mention author: */
|
|
/* Bartosz Jablonski (yabwon@gmail.com) */
|
|
/* */
|
|
/* Here is the official version: */
|
|
/*
|
|
Copyright (c) 2019 - 2026 Bartosz Jablonski (yabwon@gmail.com)
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
/**#############################################################################**/
|
|
|
|
/*** HELP START ***/
|
|
/* SPF (SAS Packages Framework) is a set of macros:
|
|
- to install,
|
|
- to load,
|
|
- to get help,
|
|
- to unload, or
|
|
- to generate SAS packages.
|
|
|
|
SAS Packages Framework, version 20260615.
|
|
See examples below.
|
|
|
|
A SAS package is a zip file containing a group of files
|
|
with SAS code (macros, functions, data steps generating
|
|
data, etc.) wrapped up together and %INCLUDEed by
|
|
a single load.sas file (also embedded inside the zip).
|
|
|
|
Contributors:
|
|
- Stu Sztukowski
|
|
LinkedIn: https://www.linkedin.com/in/statsguy/
|
|
GitHub: https://github.com/stu-code
|
|
- Ken Nakamatsu
|
|
LinkedIn: https://www.linkedin.com/in/k-nkmt
|
|
GitHub: https://github.com/k-nkmt
|
|
|
|
*/
|
|
/*** HELP END ***/
|
|
|
|
/*+verifyPackage+*/
|
|
/*** HELP START ***/
|
|
|
|
%macro verifyPackage(
|
|
packageName /* name of a package,
|
|
e.g. myPackage,
|
|
required and not null */
|
|
, path = %sysfunc(pathname(packages)) /* location of a package,
|
|
by default it looks for
|
|
location of "packages" fileref */
|
|
, hash = F* /* The SHA256 hash digest for
|
|
the package generated by
|
|
hashing_file() function, SAS 9.4M6 */
|
|
)/secure
|
|
/*** HELP END ***/
|
|
des = 'Macro to verify SAS package with the hash digest, version 20260615. Run %verifyPackage(HELP) for help info.'
|
|
;
|
|
%if (%superq(packageName) = ) OR (%qupcase(&packageName.) = HELP) %then
|
|
%do;
|
|
%local options_tmp ;
|
|
%let options_tmp = ls=%sysfunc(getoption(ls)) ps=%sysfunc(getoption(ps))
|
|
%sysfunc(getoption(notes)) %sysfunc(getoption(source))
|
|
msglevel=%sysfunc(getoption(msglevel))
|
|
;
|
|
options NOnotes NOsource ls=MAX ps=MAX msglevel=N;
|
|
%put ;
|
|
%put #################################################################################;
|
|
%put ### This is short help information for the `verifyPackage` macro #;
|
|
%put #-------------------------------------------------------------------------------#;
|
|
%put # #;
|
|
%put # Macro to verify SAS package with it hash digest, version `20260615` #;
|
|
%put # #;
|
|
%put # A SAS package is a zip file containing a group #;
|
|
%put # of SAS codes (macros, functions, data steps generating #;
|
|
%put # data, etc.) wrapped up together and embedded inside the zip. #;
|
|
%put # #;
|
|
%put # The `%nrstr(%%verifyPackage())` macro generate package SHA256 hash #;
|
|
%put # and compares it with the one provided by the user. #;
|
|
%put # #;
|
|
%put # #;
|
|
%put # *Minimum SAS version required for the process is 9.4M6.* #;
|
|
%put # #;
|
|
%put #### Parameters: #;
|
|
%put # #;
|
|
%put # 1. `packageName` Name of a package, e.g. myPackage, #;
|
|
%put # Required and not null, default use case: #;
|
|
%put # `%nrstr(%%loadPackage(myPackage))`. #;
|
|
%put # If empty displays this help information. #;
|
|
%put # #;
|
|
%put # - `hash=` A value of the package `SHA256` hash. #;
|
|
%put # Provided by the user. When the value is not provided #;
|
|
%put # then macro calculates `SHA256`, `SHA1`, and `MD5` #;
|
|
%put # digests and display then in the log. #;
|
|
%put # #;
|
|
%put # - `path=` Location of a package. By default it looks for #;
|
|
%put # location of the "packages" fileref, i.e. #;
|
|
%put # `%nrstr(%%sysfunc(pathname(packages)))` #;
|
|
%put # #;
|
|
%put #-------------------------------------------------------------------------------#;
|
|
%put # #;
|
|
%put # Visit: `https://github.com/yabwon/SAS_PACKAGES/tree/main/SPF/Documentation` #;
|
|
%put # to learn more. #;
|
|
%put # Tutorials available at: `https://github.com/yabwon/HoW-SASPackages` #;
|
|
%put # #;
|
|
%put #### Example ####################################################################;
|
|
%put # #;
|
|
%put # Enabling the SAS Package Framework #;
|
|
%put # from the local directory and installing & loading #;
|
|
%put # the SQLinDS package from the Internet. #;
|
|
%put # #;
|
|
%put # Assume that the `SPFinit.sas` file #;
|
|
%put # is located in the "C:/SAS_PACKAGES/" folder. #;
|
|
%put # #;
|
|
%put # Run the following code in your SAS session: #;
|
|
%put ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas;
|
|
%put %nrstr( filename packages "C:/SAS_PACKAGES"; %%* set-up a directory for packages; );
|
|
%put %nrstr( %%include packages(SPFinit.sas); %%* enable the framework; );
|
|
%put ;
|
|
%put %nrstr( %%installPackage(SQLinDS) %%* install the package from the Internet; );
|
|
%put %nrstr( %%verifyPackage%(SQLinDS, %%* verify the package with provided hash; );
|
|
%put %nrstr( hash=HDA478ANJ3HKHRY327FGE88HF89VH89HFFFV73GCV98RF390VB4%) );
|
|
%put ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
|
%put #################################################################################;
|
|
%put ;
|
|
options &options_tmp.;
|
|
%GOTO ENDofverifyPackage;
|
|
%end;
|
|
|
|
%local ls_tmp ps_tmp notes_tmp source_tmp stimer_tmp fullstimer_tmp msglevel_tmp mautocomploc_tmp;
|
|
%let ls_tmp = %sysfunc(getoption(ls));
|
|
%let ps_tmp = %sysfunc(getoption(ps));
|
|
%let notes_tmp = %sysfunc(getoption(notes));
|
|
%let source_tmp = %sysfunc(getoption(source));
|
|
%let stimer_tmp = %sysfunc(getoption(stimer));
|
|
%let fullstimer_tmp = %sysfunc(getoption(fullstimer));
|
|
%let msglevel_tmp = %sysfunc(getoption(msglevel));
|
|
%let mautocomploc_tmp = %sysfunc(getoption(mautocomploc));
|
|
|
|
options NOnotes NOsource ls=MAX ps=MAX NOfullstimer NOstimer msglevel=N NOmautocomploc;
|
|
|
|
%local _PackageFileref_ checkExist;
|
|
data _null_;
|
|
length packageName $ 140;
|
|
packageName = lowcase(symget("packageName"));
|
|
call symputX("_PackageFileref_", "P" !! put(MD5(strip(packageName)), hex7. -L), "L");
|
|
/*run;*/ /* <- comment out, because it can be 1 data step, not 2 */
|
|
|
|
/* when the packages reference is multi-directory search for the first one containing the package */
|
|
/*data _null_;*/ /* <- comment out, because it can be 1 data step, not 2 */
|
|
exists = 0;
|
|
length packages $ 32767 p $ 4096;
|
|
packages = resolve(symget("path"));
|
|
if char(packages,1) ^= "(" then packages = quote(strip(packages)); /* for paths with spaces */
|
|
do i = 1 to kcountw(packages, "()", "QS");
|
|
p = dequote(kscanx(packages, i, "()", "QS"));
|
|
exists + fileexist(catx("/", p, cats(packageName,".zip"))); /* check on zip files only! */
|
|
if exists then leave;
|
|
end;
|
|
if exists then call symputx("path", p, "L");
|
|
else call symputx("checkExist", '0 AND', "L");
|
|
run;
|
|
|
|
filename &_PackageFileref_.
|
|
/* put location of package myPackageFile.zip here */
|
|
"&path./%sysfunc(lowcase(&packageName.)).zip"
|
|
;
|
|
%if &checkExist. %sysfunc(fexist(&_PackageFileref_.)) %then
|
|
%do;
|
|
/* create hash SHA256 id *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
%local HASHING_FILE_exist;
|
|
%let HASHING_FILE_exist = 0;
|
|
|
|
%if %sysfunc(exist(sashelp.vfunc, VIEW)) %then
|
|
%do;
|
|
data _null_;
|
|
set sashelp.vfunc(keep=fncname);
|
|
where fncname = "HASHING_FILE";
|
|
call symputX('HASHING_FILE_exist', 1, "L");
|
|
stop;
|
|
run;
|
|
%end;
|
|
|
|
%if &HASHING_FILE_exist. = 1 %then
|
|
%do;
|
|
options notes;
|
|
filename &_PackageFileref_. list;
|
|
|
|
data _null_;
|
|
length providedHash $ 128 packageName $ 140;
|
|
providedHash = strip(symget("hash"));
|
|
packageName = strip(symget("packageName"));
|
|
|
|
emptyHash = (providedHash = " " OR providedHash in ("F*" "f*" "C*" "c*"));
|
|
|
|
put 82*"-" / @2 packageName / 82*"-" /;
|
|
|
|
if NOT emptyHash then put "Provided Hash: " providedHash;
|
|
|
|
length method $ 8 digest $ 128;
|
|
/* calculate SHA256 */
|
|
method="SHA256";
|
|
LINK CalcualteHashDigest; /* go to Link 1 */
|
|
|
|
if NOT emptyHash then
|
|
do; /* step for veryfication */
|
|
if upcase(digest) = upcase(providedHash) then
|
|
do;
|
|
put "NOTE: Verification SUCCESSFUL."
|
|
/ "NOTE- Generated hash is EQUAL to the provided one." / ;
|
|
end;
|
|
else
|
|
do;
|
|
pos = 0;
|
|
do i = 1 to max(lengthn(digest),lengthn(providedHash)) while(pos=0);
|
|
if char(digest,i) NE char(providedHash,i) then pos = i;
|
|
end;
|
|
put "ERROR- " @(pos+15)"^"/"ERROR- " @(pos+15)"| diff @" pos/"ERROR- ";
|
|
put "ERROR: Verification FAILED!!"
|
|
/ "ERROR- Generated hash is DIFFERENT than the provided one."
|
|
/ "ERROR- Check if the ZIP is genuine." / ;
|
|
end;
|
|
end;
|
|
else
|
|
do method = "SHA1", "MD5"; /* step for digest display, calcualte also SHA1 and MD5 */
|
|
LINK CalcualteHashDigest; /* go to Link 1 */
|
|
end;
|
|
put 82*"-" /;
|
|
stop;
|
|
return;
|
|
CalcualteHashDigest: /* Link 1 */
|
|
|
|
select;
|
|
when ( 'F*' = upcase(substr(providedHash,1,2)) ) /* F = file digest */
|
|
digest = 'F*' !! HASHING_FILE(method, pathname("&_PackageFileref_.",'F'), 0);
|
|
when ( 'C*' = upcase(substr(providedHash,1,2)) ) /* C = content digest */
|
|
digest = 'C*' !! HASHING_FILE(method, "&_PackageFileref_.", 4);
|
|
otherwise /* legacy approach, without C or F, digest value equivalent to C */
|
|
digest = HASHING_FILE(method, "&_PackageFileref_.", 4);
|
|
end;
|
|
put method "digest: " digest /;
|
|
|
|
return;
|
|
run;
|
|
%let HASHING_FILE_exist = 0;
|
|
%end;
|
|
%else
|
|
%put WARNING: Verification impossible! Minimum SAS version required for the process is 9.4M6. ;
|
|
/*-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-*/
|
|
%end;
|
|
%else %put ERROR:[&sysmacroname] File "&path./&packageName..zip" does not exist!;
|
|
filename &_PackageFileref_. clear;
|
|
|
|
options ls = &ls_tmp. ps = &ps_tmp.
|
|
¬es_tmp. &source_tmp.
|
|
&stimer_tmp. &fullstimer_tmp.
|
|
msglevel=&msglevel_tmp. &mautocomploc_tmp.;
|
|
|
|
%ENDofverifyPackage:
|
|
%mend verifyPackage;
|
|
/**/
|
|
|