/*** HELP START ***//*
 
## >>> `%zipLibrary()` macro: <<< <a name="ziplibrary-macro"></a> #######################  

The zipLibrary() macro allows to zip content of a SAS library. 

Files can be zipped into a single file (named as the input library)
or into multiple files (named as "dataset.sas7bdat.zip"). 
If a file is indexed also the index file is zipped.

Source files can be deleted after compression.

Status of compression and processing time is reported. 

See examples below for the details.

### SYNTAX: ###################################################################

The basic syntax is the following, the `<...>` means optional parameters:
~~~~~~~~~~~~~~~~~~~~~~~sas
%zipLibrary(
    lib
  <,mode=>
  <,clean=> 
  <,libOut=>
  <,compression=>
)
~~~~~~~~~~~~~~~~~~~~~~~

**Arguments description**:

1. `lib`           - *Required*, a name of the library to be zipped.
                     Must be a valid SAS V7, V8, or V9 library.


* `mode  = S`      - *Optional*, default value is `S`, 
                     indicates mode of compression 
                     generates single zip file (`SINGLE/S`) 
                     or multiple files (`MULTI/M`)

* `clean = 0`      - *Optional*, default value is `0`,
                     should datasets be deleted after zipping? 
                     `1` means *yes*, `0` means *no*. 

* `libOut =`       - *Optional*, default value is empty,
                     output library for a single zip file.

* `compression =`  - *Optional*, default value is `6`,
                     specifies the compression level
                     `0` to `9`, where `0` is no compression
                     and `9` is maximum compression.

*//*** HELP END ***/

%macro zipLibrary(
  lib             /* name of the library to be zipped */
 ,mode=S          /* mode of compression, default is "S", 
                     generates single zip file or multiple files */
 ,clean=0         /* should datasets be deleted after zipping? 1 = yes, 0 = no */
 ,libOut=         /* output library for single zip file */
 ,compression=6   /* specifies the compression level */
)
/secure des='Macro zipLibrary allows to zip content of an entire library'
;
%local processing_time;
%let   processing_time = %sysfunc(datetime());
%local tmp_options;
%let   tmp_options = %sysfunc(getoption(notes));
options Notes;
%put NOTE: The zipLibrary() macro supports V7, V8, and V9 engines.;
%put; 
options noNotes;

data _null_;
  mode = upcase(symget("mode"));
  if mode not in ("S", "M", "SINGLE", "MULTI") then 
    do;
      mode = "S";
      put "WARNING: Improper Mode value.";
      put "WARNING- Use: SINGLE/S or MULTI/M";
      put "WARNING- Mode set to SINGLE.";
    end;
  call symputX('mode', char(upcase(mode),1), "L");

  clean = input(symget("clean"), ?? best32.);
  if clean not in (0 1) then 
  do;
    clean = 0;
    put "WARNING: Improper Clean value.";
    put "WARNING- Use: 0 or 1";
    put "WARNING: Clean set to 0.";
  end;
  call symputX('clean', clean, "L");
  
  compression = input(symget("compression"), ?? best32.);
  if compression not in (0:9) then 
  do;
    compression = 6;
    put "WARNING: Improper Compression value.";
    put "WARNING- Use: 0, 1, 2, ..., 8 or 9";
    put "WARNING: Compression set to 6.";
  end;
  call symputX('compression', compression, "L");
run;

%local _testLB_ ;
%let   _testLB_ = i%sysfunc(datetime(),hex7.);

%if %superq(libOut)= %then
  %do;
    libname &_testLB_. (&lib. &lib.); /* to ensure that library with space in location directory will be quoted */
    %let libOut = %scan(%sysfunc(pathname(&_testLB_.)),1,( ),q);
    libname &_testLB_. clear;
  %end;
%else
  %do;
    libname &_testLB_. (&libOut. &libOut.);
    %let libOut = %scan(%sysfunc(pathname(&_testLB_.)),1,( ),q);
    libname &_testLB_. clear;
  %end;

%if &mode. = S %then
  %do;
    options notes;
    %put NOTE: LibOut is: &libOut.;
    %put;
    options noNotes;
  %end;

%local libContent1 libContent2 libContent3;
%let   libContent1 = WORK._libContent1_%sysfunc(datetime(),hex16.)_;
%let   libContent2 = WORK._libContent2_%sysfunc(datetime(),hex16.)_;
%let   libContent3 = WORK._libContent3_%sysfunc(datetime(),hex16.)_;

ods select none;
ods output EngineHost = &libContent2.(where = (Label1="Filename"));
proc contents 
  data=&lib.._ALL_ 
  out = &libContent1.(
    keep = libname memname memtype idxcount engine
    where = (memtype = "DATA" and engine in ("V9" "V8" "V7" "BASE"))
  ) 
;
run;
ods select all;
proc sort data = &libContent1. nodupkey;
  by _all_;
run;
proc sort data = &libContent2. nodupkey;
  by _all_;
run;

proc sql noprint;
  create table &libContent3. as
    select 
      t1.*
     ,t2.*
    from
     &libContent1. as t1
     join
     &libContent2. as t2
    on
     catx(".", t1.libname, t1.memname) = t2.member
  ;
quit;

proc delete data=
  &libContent1.
  &libContent2.
;
run;

%local _inF_ _outF_;
%let   _inF_ = i%sysfunc(datetime(),hex7.);
%let   _outF_= o%sysfunc(datetime(),hex7.);

data &libContent3.;
  set &libContent3.;
  output;

  if IDXCOUNT > 0 then 
    do;
      substr(cValue1,length(cValue1)-7,8) = "sas7bndx";
      MEMTYPE = "INDEX";
      output;
    end;
run;


data _null_;
  set &libContent3. end=END;

  if symgetn("clean") = _N_ then /* clean = 1 and _N_ = 1 */
    call execute('proc delete data = ');

  put "STATUS FOR " MEMBER MEMTYPE;

  rc1 = filename("&_inF_.", cValue1, , "lrecl=1 recfm=n");
  rc1msg = sysmsg();
  if rc1 then put "assign input: " @15 (rc1:) (=) ;

  if upcase(symget("mode")) =: "M" then
    rc2 = filename("&_outF_."
                  ,strip(cValue1)||'.zip'
                  ,"ZIP"
                  ,"lrecl=1 recfm=n");

  if upcase(symget("mode")) =: "S" then
    rc2 = filename("&_outF_."
                  ,catx("/", dequote("&libOut."), "&lib..zip")
                  ,"ZIP"
                  ,"member='" !! strip(scan(cValue1, -1, "/\")) !!"' lrecl=1 recfm=n compression=&compression.");  
  rc2msg = sysmsg();
  if rc2 then put "assign output: " @15 (rc2:) (=) ;

  do _N_ = 1 to 5 until(rc4=0);
    rc3 = fcopy("&_inF_.", "&_outF_.");
    rc3msg = sysmsg();
    if rc3 then put "copying: " @15 (rc3:) (=) ;
    rc4 = ^fexist("&_outF_.");
    rc4msg = sysmsg();
    if rc4 then put "file exists: "  @15 (rc4:) (=) ;
  end;

  if symgetn("clean") and rc4 = 0 and MEMTYPE = "DATA" then
    call execute(MEMBER); 

  rc5 = filename("&_inF_.");
  rc5msg = sysmsg();
  rc6 = filename("&_outF_.");
  rc6msg = sysmsg();
  if rc5 then put "clear input: "  @15 (rc5:) (=) ;
  if rc6 then put "clear output: " @15 (rc6:) (=) ;

  if not sum(rc1, rc2, rc3, rc4, rc5, rc6) then put " O.K.";
  errorZipLibrary + sum(rc1, rc2, rc3, rc4, rc5, rc6); 

  if END and errorZipLibrary then
    do;
      put "ERROR: Compression was not clean. See the log for details.";
    end;

  if symgetn("clean") and END then /* clean = 1 and end = 1 */
    do;
      if errorZipLibrary then call execute('; run CANCEL;');
                         else call execute('; run ;');
      put / "Delete procedure: ";
    end;

run;

proc delete data=
  &libContent3.
;
run;

%if &mode. = S %then
  %do;
    options notes;
    %put;
    %put NOTE: LibOut is: &libOut.;
    options noNotes;
  %end;
options notes;
%put;
%put Processing time was: %sysevalf(%sysfunc(datetime()) - &processing_time.) s.;
%put;
options &tmp_options.;
%mend zipLibrary;



/*** HELP START ***//*

### EXAMPLES AND USECASES: ####################################################

**EXAMPLE 1.** Generate data:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas

options dlcreatedir;
  libname test1 "%sysfunc(pathname(work))/test1";
  libname test2 "%sysfunc(pathname(work))/test2";
  libname test3 (test1 test2);
  libname test4 "%sysfunc(pathname(work))/test4";
options nodlcreatedir;

%put %sysfunc(pathname(test3));
%put %sysfunc(pathname(test4));

data 
  test1.A(index=(model)) 
  test1.B 
  test2.C 
  test2.D(index=(model make io=(invoice origin))) 
;
  set sashelp.cars;
run;

data test1.B2 / view=test1.B2;
  set test1.B;
  output;
  output;
run;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 2.** Zip content of test3 library 
               into the same location in one zip file:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%zipLibrary(test3)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 3.** Zip content of test3 library 
               into the same location in multiple zip files:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%zipLibrary(test3, mode=MULTI)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 4.** Zip content of test3 library
               with maximum compression level 
               into different location in one zip file
               and delete source files:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%zipLibrary(test3, clean=1, libOut=test4, compression=9)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

*//*** HELP END ***/
