From ecd389c9355cb8b5d81ec45c5c2b315d05ca984a Mon Sep 17 00:00:00 2001 From: Allan Bowe Date: Thu, 24 Jun 2021 00:26:41 +0300 Subject: [PATCH 1/3] feat: adding mp_base64copy macro --- all.sas | 140 +++++++++++++++++++++++++++++++++++++++-- base/mp_base64copy.sas | 120 +++++++++++++++++++++++++++++++++++ viya/mv_createfile.sas | 17 ++++- 3 files changed, 268 insertions(+), 9 deletions(-) create mode 100644 base/mp_base64copy.sas diff --git a/all.sas b/all.sas index 79c4f41..56740dc 100644 --- a/all.sas +++ b/all.sas @@ -2172,6 +2172,125 @@ Usage: drop table &ds; %mend mp_assertdsobs;/** + @file + @brief Convert a file to/from base64 format + @details Creates a new version of a file either encoded or decoded using + Base64. Inspired by this post by Michael Dixon: + https://support.selerity.com.au/hc/en-us/articles/223345708-Tip-SAS-and-Base64 + Usage: + + filename tmp temp; + data _null_; + file tmp; + put 'base ik ally'; + run; + %mp_base64copy(inref=tmp, outref=myref, action=ENCODE) + + data _null_; + infile myref; + input; + put _infile_; + run; + + %mp_base64copy(inref=myref, outref=mynewref, action=DECODE) + + data _null_; + infile mynewref; + input; + put _infile_; + run; + + @param inref= Fileref of the input file (should exist) + @param outref= Output filref. If it does not exist, it is created. + @param action= (ENCODE) The action to take. Valid values: + @li ENCODE Convert the file to base64 format + @li DECODE Decode the file from base64 format + +

SAS Macros

+ @li mp_abort.sas + + @version 9.2 + +**/ + +%macro mp_base64copy( + inref=0, + outref=0, + action=ENCODE +)/*/STORE SOURCE*/; + +%let inref=%upcase(&inref); +%let outref=%upcase(&outref); +%let action=%upcase(&action); +%local infound outfound; +%let infound=0; +%let outfound=0; +data _null_; + set sashelp.vextfl(where=(upcase(fileref)="&inref" or fileref="&outref")); + if fileref="&inref" then call symputx('infound',1,'l'); + if fileref="&outref" then call symputx('outfound',1,'l'); +run; + +%mp_abort(iftrue= (&infound=0) + ,mac=&sysmacroname + ,msg=%str(INREF &inref NOT FOUND!) +) +%mp_abort(iftrue= (&outref=0) + ,mac=&sysmacroname + ,msg=%str(OUTREF NOT PROVIDED!) +) +%mp_abort(iftrue= (&action ne ENCODE and &action ne DECODE) + ,mac=&sysmacroname + ,msg=%str(Invalid action! Should be ENCODE OR DECODE) +) + +%if &outfound=0 %then %do; + filename &outref temp lrecl=2097088; +%%end; + +%if &action=ENCODE %then %do; + data _null_; + length b64 $ 76 line $ 57; + retain line ""; + infile &inref recfm=F lrecl= 1 end=eof; + input @1 stream $char1.; + file &outref lrecl=76; + substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream)); + if mod(_N_,57)=0 or EOF then do; + if eof then b64=put(trim(line),$base64X76.); + else b64=put(line, $base64X76.); + put b64; + line=""; + end; + run; +%end; +%else %if &action=DECODE %then %do; + data _null_; + length filein 8 fileout 8; + filein = fopen("&inref",'I',4,'B'); + fileout = fopen("&outref",'O',3,'B'); + char= '20'x; + do while(fread(filein)=0); + raw="1234"; + do i=1 to 4; + rc=fget(filein,char,1); + substr(raw,i,1)=char; + end; + val="123"; + val=input(raw,$base64X4.); + do i=1 to 3; + length byte $1; + byte=byte(rank(substr(val,i,1))); + rc = fput(fileout, byte); + end; + rc =fwrite(fileout); + end; + rc = fclose(filein); + rc = fclose(fileout); + run; +%end; + +%mend mp_base64copy;/** @file @brief Copy any file using binary input / output streams @details Reads in a file byte by byte and writes it back out. Is an @@ -12702,9 +12821,7 @@ data _null_; putlog _infile_; run; - -%mend mmx_deletemetafolder; -/** +%mend mmx_deletemetafolder;/** @file mmx_spkexport.sas @brief Exports everything in a particular metadata folder @details Will export everything in a metadata folder to a specified location. @@ -12912,9 +13029,13 @@ run; @param [in] path= The parent folder in which to create the file @param [in] name= The name of the file to be created @param [in] inref= The fileref pointing to the file to be uploaded + @param [in] intype= (BINARY) The type of the input data. Valid values: + @li BINARY File is copied byte for byte using the mp_binarycopy.sas macro. + @li BASE64 File will be first decoded using the mp_base64.sas macro, then + loaded byte by byte to SAS Drive. @param [in] contentdisp= (inline) Content Disposition. Example values: - @li inline - @li attachment + @li inline + @li attachment @param [in] access_token_var= The global macro variable to contain the access token, if using authorization_code grant type. @@ -12932,6 +13053,7 @@ run; @li mf_getuniquefileref.sas @li mf_isblank.sas @li mp_abort.sas + @li mp_base64.sas @li mp_binarycopy.sas @li mv_createfolder.sas @@ -12940,6 +13062,7 @@ run; %macro mv_createfile(path= ,name= ,inref= + ,intype=BINARY ,contentdisp=inline ,access_token_var=ACCESS_TOKEN ,grant_type=sas_services @@ -12994,7 +13117,12 @@ filename &fref filesrvc cdisp="&contentdisp" lrecl=1048544; -%mp_binarycopy(inref=&inref, outref=&fref) +%if &intype=BINARY %then %do; + %mp_binarycopy(inref=&inref, outref=&fref) +%end; +%else %if &intype=BASE64 %then %do; + %mp_base64copy(inref=&inref, outref=&fref, action=DECODE) +%end; filename &fref clear; diff --git a/base/mp_base64copy.sas b/base/mp_base64copy.sas new file mode 100644 index 0000000..2cb7c0c --- /dev/null +++ b/base/mp_base64copy.sas @@ -0,0 +1,120 @@ +/** + @file + @brief Convert a file to/from base64 format + @details Creates a new version of a file either encoded or decoded using + Base64. Inspired by this post by Michael Dixon: + https://support.selerity.com.au/hc/en-us/articles/223345708-Tip-SAS-and-Base64 + Usage: + + filename tmp temp; + data _null_; + file tmp; + put 'base ik ally'; + run; + %mp_base64copy(inref=tmp, outref=myref, action=ENCODE) + + data _null_; + infile myref; + input; + put _infile_; + run; + + %mp_base64copy(inref=myref, outref=mynewref, action=DECODE) + + data _null_; + infile mynewref; + input; + put _infile_; + run; + + @param inref= Fileref of the input file (should exist) + @param outref= Output filref. If it does not exist, it is created. + @param action= (ENCODE) The action to take. Valid values: + @li ENCODE Convert the file to base64 format + @li DECODE Decode the file from base64 format + +

SAS Macros

+ @li mp_abort.sas + + @version 9.2 + +**/ + +%macro mp_base64copy( + inref=0, + outref=0, + action=ENCODE +)/*/STORE SOURCE*/; + +%let inref=%upcase(&inref); +%let outref=%upcase(&outref); +%let action=%upcase(&action); +%local infound outfound; +%let infound=0; +%let outfound=0; +data _null_; + set sashelp.vextfl(where=(upcase(fileref)="&inref" or fileref="&outref")); + if fileref="&inref" then call symputx('infound',1,'l'); + if fileref="&outref" then call symputx('outfound',1,'l'); +run; + +%mp_abort(iftrue= (&infound=0) + ,mac=&sysmacroname + ,msg=%str(INREF &inref NOT FOUND!) +) +%mp_abort(iftrue= (&outref=0) + ,mac=&sysmacroname + ,msg=%str(OUTREF NOT PROVIDED!) +) +%mp_abort(iftrue= (&action ne ENCODE and &action ne DECODE) + ,mac=&sysmacroname + ,msg=%str(Invalid action! Should be ENCODE OR DECODE) +) + +%if &outfound=0 %then %do; + filename &outref temp lrecl=2097088; +%%end; + +%if &action=ENCODE %then %do; + data _null_; + length b64 $ 76 line $ 57; + retain line ""; + infile &inref recfm=F lrecl= 1 end=eof; + input @1 stream $char1.; + file &outref lrecl=76; + substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream)); + if mod(_N_,57)=0 or EOF then do; + if eof then b64=put(trim(line),$base64X76.); + else b64=put(line, $base64X76.); + put b64; + line=""; + end; + run; +%end; +%else %if &action=DECODE %then %do; + data _null_; + length filein 8 fileout 8; + filein = fopen("&inref",'I',4,'B'); + fileout = fopen("&outref",'O',3,'B'); + char= '20'x; + do while(fread(filein)=0); + raw="1234"; + do i=1 to 4; + rc=fget(filein,char,1); + substr(raw,i,1)=char; + end; + val="123"; + val=input(raw,$base64X4.); + do i=1 to 3; + length byte $1; + byte=byte(rank(substr(val,i,1))); + rc = fput(fileout, byte); + end; + rc =fwrite(fileout); + end; + rc = fclose(filein); + rc = fclose(fileout); + run; +%end; + +%mend mp_base64copy; \ No newline at end of file diff --git a/viya/mv_createfile.sas b/viya/mv_createfile.sas index ea00974..00e4092 100644 --- a/viya/mv_createfile.sas +++ b/viya/mv_createfile.sas @@ -17,9 +17,13 @@ @param [in] path= The parent folder in which to create the file @param [in] name= The name of the file to be created @param [in] inref= The fileref pointing to the file to be uploaded + @param [in] intype= (BINARY) The type of the input data. Valid values: + @li BINARY File is copied byte for byte using the mp_binarycopy.sas macro. + @li BASE64 File will be first decoded using the mp_base64.sas macro, then + loaded byte by byte to SAS Drive. @param [in] contentdisp= (inline) Content Disposition. Example values: - @li inline - @li attachment + @li inline + @li attachment @param [in] access_token_var= The global macro variable to contain the access token, if using authorization_code grant type. @@ -37,6 +41,7 @@ @li mf_getuniquefileref.sas @li mf_isblank.sas @li mp_abort.sas + @li mp_base64.sas @li mp_binarycopy.sas @li mv_createfolder.sas @@ -45,6 +50,7 @@ %macro mv_createfile(path= ,name= ,inref= + ,intype=BINARY ,contentdisp=inline ,access_token_var=ACCESS_TOKEN ,grant_type=sas_services @@ -99,7 +105,12 @@ filename &fref filesrvc cdisp="&contentdisp" lrecl=1048544; -%mp_binarycopy(inref=&inref, outref=&fref) +%if &intype=BINARY %then %do; + %mp_binarycopy(inref=&inref, outref=&fref) +%end; +%else %if &intype=BASE64 %then %do; + %mp_base64copy(inref=&inref, outref=&fref, action=DECODE) +%end; filename &fref clear; From ac0ddf38b07a8826f697e57887ddb9da54688d4e Mon Sep 17 00:00:00 2001 From: Allan Bowe Date: Thu, 24 Jun 2021 00:28:41 +0300 Subject: [PATCH 2/3] chore: automated commit --- all.sas | 2 +- viya/mv_createfile.sas | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/all.sas b/all.sas index 56740dc..4568684 100644 --- a/all.sas +++ b/all.sas @@ -13053,7 +13053,7 @@ run; @li mf_getuniquefileref.sas @li mf_isblank.sas @li mp_abort.sas - @li mp_base64.sas + @li mp_base64copy.sas @li mp_binarycopy.sas @li mv_createfolder.sas diff --git a/viya/mv_createfile.sas b/viya/mv_createfile.sas index 00e4092..64d2f8c 100644 --- a/viya/mv_createfile.sas +++ b/viya/mv_createfile.sas @@ -41,7 +41,7 @@ @li mf_getuniquefileref.sas @li mf_isblank.sas @li mp_abort.sas - @li mp_base64.sas + @li mp_base64copy.sas @li mp_binarycopy.sas @li mv_createfolder.sas From a8d222a0f8e79556c8fb30e7a9ffa8d9e6629ff0 Mon Sep 17 00:00:00 2001 From: Allan Bowe Date: Thu, 24 Jun 2021 00:29:54 +0300 Subject: [PATCH 3/3] chore: automated commit --- all.sas | 2 +- base/mp_base64copy.sas | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/all.sas b/all.sas index 4568684..aff99c2 100644 --- a/all.sas +++ b/all.sas @@ -2226,7 +2226,7 @@ Usage: %let infound=0; %let outfound=0; data _null_; - set sashelp.vextfl(where=(upcase(fileref)="&inref" or fileref="&outref")); + set sashelp.vextfl(where=(fileref="&inref" or fileref="&outref")); if fileref="&inref" then call symputx('infound',1,'l'); if fileref="&outref" then call symputx('outfound',1,'l'); run; diff --git a/base/mp_base64copy.sas b/base/mp_base64copy.sas index 2cb7c0c..ec81f58 100644 --- a/base/mp_base64copy.sas +++ b/base/mp_base64copy.sas @@ -53,7 +53,7 @@ %let infound=0; %let outfound=0; data _null_; - set sashelp.vextfl(where=(upcase(fileref)="&inref" or fileref="&outref")); + set sashelp.vextfl(where=(fileref="&inref" or fileref="&outref")); if fileref="&inref" then call symputx('infound',1,'l'); if fileref="&outref" then call symputx('outfound',1,'l'); run;