@sasjs/utils 2.49.0 → 2.49.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fs/internal/helper.js +19 -24
- package/fs/internal/helper.js.map +1 -1
- package/macros/mf_abort.sas +27 -0
- package/macros/mf_dedup.sas +53 -0
- package/macros/mf_deletefile.sas +31 -0
- package/macros/mf_existds.sas +30 -0
- package/macros/mf_existfeature.sas +59 -0
- package/macros/mf_existfileref.sas +33 -0
- package/macros/mf_existfunction.sas +37 -0
- package/macros/mf_existvar.sas +39 -0
- package/macros/mf_existvarlist.sas +56 -0
- package/macros/mf_fmtdttm.sas +42 -0
- package/macros/mf_getapploc.sas +78 -0
- package/macros/mf_getattrc.sas +34 -0
- package/macros/mf_getattrn.sas +34 -0
- package/macros/mf_getengine.sas +54 -0
- package/macros/mf_getfilesize.sas +64 -0
- package/macros/mf_getfmtlist.sas +61 -0
- package/macros/mf_getfmtname.sas +44 -0
- package/macros/mf_getkeyvalue.sas +32 -0
- package/macros/mf_getplatform.sas +75 -0
- package/macros/mf_getquotedstr.sas +57 -0
- package/macros/mf_getschema.sas +43 -0
- package/macros/mf_getuniquefileref.sas +60 -0
- package/macros/mf_getuniquelibref.sas +57 -0
- package/macros/mf_getuniquename.sas +22 -0
- package/macros/mf_getuser.sas +44 -0
- package/macros/mf_getvalue.sas +36 -0
- package/macros/mf_getvarcount.sas +49 -0
- package/macros/mf_getvarformat.sas +72 -0
- package/macros/mf_getvarlen.sas +56 -0
- package/macros/mf_getvarlist.sas +74 -0
- package/macros/mf_getvarnum.sas +58 -0
- package/macros/mf_getvartype.sas +52 -0
- package/macros/mf_getxengine.sas +43 -0
- package/macros/mf_increment.sas +29 -0
- package/macros/mf_isblank.sas +27 -0
- package/macros/mf_isdir.sas +34 -0
- package/macros/mf_isint.sas +36 -0
- package/macros/mf_islibds.sas +40 -0
- package/macros/mf_loc.sas +29 -0
- package/macros/mf_mkdir.sas +67 -0
- package/macros/mf_mval.sas +19 -0
- package/macros/mf_nobs.sas +26 -0
- package/macros/mf_trimstr.sas +50 -0
- package/macros/mf_uid.sas +21 -0
- package/macros/mf_verifymacvars.sas +72 -0
- package/macros/mf_wordsinstr1andstr2.sas +53 -0
- package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
- package/macros/mf_writefile.sas +67 -0
- package/macros/mp_abort.sas +301 -0
- package/macros/mp_appendfile.sas +57 -0
- package/macros/mp_applyformats.sas +181 -0
- package/macros/mp_assert.sas +56 -0
- package/macros/mp_assertcols.sas +145 -0
- package/macros/mp_assertcolvals.sas +168 -0
- package/macros/mp_assertdsobs.sas +121 -0
- package/macros/mp_assertscope.sas +147 -0
- package/macros/mp_base64copy.sas +117 -0
- package/macros/mp_binarycopy.sas +79 -0
- package/macros/mp_chop.sas +194 -0
- package/macros/mp_cleancsv.sas +78 -0
- package/macros/mp_cntlout.sas +85 -0
- package/macros/mp_copyfolder.sas +84 -0
- package/macros/mp_coretable.sas +73 -0
- package/macros/mp_createconstraints.sas +70 -0
- package/macros/mp_createwebservice.sas +32 -0
- package/macros/mp_csv2ds.sas +140 -0
- package/macros/mp_deleteconstraints.sas +52 -0
- package/macros/mp_deletefolder.sas +70 -0
- package/macros/mp_dictionary.sas +52 -0
- package/macros/mp_dirlist.sas +262 -0
- package/macros/mp_distinctfmtvalues.sas +50 -0
- package/macros/mp_dropmembers.sas +46 -0
- package/macros/mp_ds2cards.sas +291 -0
- package/macros/mp_ds2csv.sas +212 -0
- package/macros/mp_ds2ddl.sas +30 -0
- package/macros/mp_ds2fmtds.sas +105 -0
- package/macros/mp_ds2inserts.sas +180 -0
- package/macros/mp_ds2md.sas +103 -0
- package/macros/mp_ds2squeeze.sas +120 -0
- package/macros/mp_dsmeta.sas +110 -0
- package/macros/mp_filtercheck.sas +251 -0
- package/macros/mp_filtergenerate.sas +105 -0
- package/macros/mp_filterstore.sas +254 -0
- package/macros/mp_filtervalidate.sas +105 -0
- package/macros/mp_getcols.sas +67 -0
- package/macros/mp_getconstraints.sas +115 -0
- package/macros/mp_getdbml.sas +335 -0
- package/macros/mp_getddl.sas +400 -0
- package/macros/mp_getformats.sas +124 -0
- package/macros/mp_getmaxvarlengths.sas +130 -0
- package/macros/mp_getpk.sas +281 -0
- package/macros/mp_gitadd.sas +45 -0
- package/macros/mp_gitreleaseinfo.sas +74 -0
- package/macros/mp_gitstatus.sas +67 -0
- package/macros/mp_gsubfile.sas +58 -0
- package/macros/mp_guesspk.sas +332 -0
- package/macros/mp_hashdataset.sas +91 -0
- package/macros/mp_hashdirectory.sas +164 -0
- package/macros/mp_include.sas +106 -0
- package/macros/mp_init.sas +76 -0
- package/macros/mp_jsonout.sas +406 -0
- package/macros/mp_lib2cards.sas +78 -0
- package/macros/mp_lib2inserts.sas +77 -0
- package/macros/mp_loadformat.sas +303 -0
- package/macros/mp_lockanytable.sas +243 -0
- package/macros/mp_lockfilecheck.sas +103 -0
- package/macros/mp_makedata.sas +107 -0
- package/macros/mp_md5.sas +58 -0
- package/macros/mp_perflog.sas +43 -0
- package/macros/mp_prevobs.sas +88 -0
- package/macros/mp_recursivejoin.sas +90 -0
- package/macros/mp_replace.sas +151 -0
- package/macros/mp_reseterror.sas +27 -0
- package/macros/mp_resetoption.sas +39 -0
- package/macros/mp_retainedkey.sas +247 -0
- package/macros/mp_runddl.sas +49 -0
- package/macros/mp_searchcols.sas +103 -0
- package/macros/mp_searchdata.sas +131 -0
- package/macros/mp_setkeyvalue.sas +55 -0
- package/macros/mp_sortinplace.sas +122 -0
- package/macros/mp_stackdiffs.sas +592 -0
- package/macros/mp_storediffs.sas +230 -0
- package/macros/mp_stprequests.sas +74 -0
- package/macros/mp_streamfile.sas +212 -0
- package/macros/mp_testjob.sas +92 -0
- package/macros/mp_testservice.sas +39 -0
- package/macros/mp_testwritespeedlibrary.sas +59 -0
- package/macros/mp_tree.sas +71 -0
- package/macros/mp_unzip.sas +90 -0
- package/macros/mp_updatevarlength.sas +93 -0
- package/macros/mp_validatecol.sas +97 -0
- package/macros/mp_wait4file.sas +35 -0
- package/macros/mp_webin.sas +59 -0
- package/macros/mp_zip.sas +85 -0
- package/package.json +5 -2
- package/scripts/copySASjsCore.ts +36 -0
- package/utils/index.d.ts +1 -2
- package/utils/index.js +2 -3
- package/utils/index.js.map +1 -1
- package/utils/utils.d.ts +1 -0
- package/utils/utils.js +7 -1
- package/utils/utils.js.map +1 -1
- package/utils/getNodeModulePath.d.ts +0 -2
- package/utils/getNodeModulePath.js +0 -83
- package/utils/getNodeModulePath.js.map +0 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Returns number of logical (undeleted) observations.
|
|
4
|
+
@details Beware - will not work on external database tables!
|
|
5
|
+
Is just a convenience macro for calling <code> %mf_getattrn()</code>.
|
|
6
|
+
|
|
7
|
+
%put Number of observations=%mf_nobs(sashelp.class);
|
|
8
|
+
|
|
9
|
+
<h4> SAS Macros </h4>
|
|
10
|
+
@li mf_getattrn.sas
|
|
11
|
+
|
|
12
|
+
@param libds library.dataset
|
|
13
|
+
|
|
14
|
+
@return output returns result of the attrn value supplied, or log message
|
|
15
|
+
if err.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@version 9.2
|
|
19
|
+
@author Allan Bowe
|
|
20
|
+
|
|
21
|
+
**/
|
|
22
|
+
|
|
23
|
+
%macro mf_nobs(libds
|
|
24
|
+
)/*/STORE SOURCE*/;
|
|
25
|
+
%mf_getattrn(&libds,NLOBS)
|
|
26
|
+
%mend mf_nobs;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file mf_trimstr.sas
|
|
3
|
+
@brief Removes character(s) from the end, if they exist
|
|
4
|
+
@details If the designated characters exist at the end of the string, they
|
|
5
|
+
are removed
|
|
6
|
+
|
|
7
|
+
%put %mf_trimstr(/blah/,/); * /blah;
|
|
8
|
+
%put %mf_trimstr(/blah/,h); * /blah/;
|
|
9
|
+
%put %mf_trimstr(/blah/,h/);* /bla;
|
|
10
|
+
|
|
11
|
+
<h4> SAS Macros </h4>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@param basestr The string to be modified
|
|
15
|
+
@param trimstr The string to be removed from the end of `basestr`, if it
|
|
16
|
+
exists
|
|
17
|
+
|
|
18
|
+
@return output returns result with the value of `trimstr` removed from the end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@version 9.2
|
|
22
|
+
@author Allan Bowe
|
|
23
|
+
|
|
24
|
+
**/
|
|
25
|
+
|
|
26
|
+
%macro mf_trimstr(basestr,trimstr);
|
|
27
|
+
%local baselen trimlen trimval;
|
|
28
|
+
|
|
29
|
+
/* return if basestr is shorter than trimstr (or 0) */
|
|
30
|
+
%let baselen=%length(%superq(basestr));
|
|
31
|
+
%let trimlen=%length(%superq(trimstr));
|
|
32
|
+
%if &baselen < &trimlen or &baselen=0 %then %return;
|
|
33
|
+
|
|
34
|
+
/* obtain the characters from the end of basestr */
|
|
35
|
+
%let trimval=%qsubstr(%superq(basestr)
|
|
36
|
+
,%length(%superq(basestr))-&trimlen+1
|
|
37
|
+
,&trimlen);
|
|
38
|
+
|
|
39
|
+
/* compare and if matching, chop it off! */
|
|
40
|
+
%if %superq(basestr)=%superq(trimstr) %then %do;
|
|
41
|
+
%return;
|
|
42
|
+
%end;
|
|
43
|
+
%else %if %superq(trimval)=%superq(trimstr) %then %do;
|
|
44
|
+
%qsubstr(%superq(basestr),1,%length(%superq(basestr))-&trimlen)
|
|
45
|
+
%end;
|
|
46
|
+
%else %do;
|
|
47
|
+
&basestr
|
|
48
|
+
%end;
|
|
49
|
+
|
|
50
|
+
%mend mf_trimstr;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Creates a unique ID based on system time in friendly format
|
|
4
|
+
@details format = YYYYMMDD_HHMMSSmmm_<sysjobid>_<3randomDigits>
|
|
5
|
+
|
|
6
|
+
%put %mf_uid();
|
|
7
|
+
|
|
8
|
+
@version 9.3
|
|
9
|
+
@author Allan Bowe
|
|
10
|
+
|
|
11
|
+
**/
|
|
12
|
+
|
|
13
|
+
%macro mf_uid(
|
|
14
|
+
)/*/STORE SOURCE*/;
|
|
15
|
+
%local today now;
|
|
16
|
+
%let today=%sysfunc(today(),yymmddn8.);
|
|
17
|
+
%let now=%sysfunc(compress(%sysfunc(time(),tod12.3),:.));
|
|
18
|
+
|
|
19
|
+
&today._&now._&sysjobid._%sysevalf(%sysfunc(ranuni(0))*999,CEIL)
|
|
20
|
+
|
|
21
|
+
%mend mf_uid;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Checks if a set of macro variables exist AND contain values.
|
|
4
|
+
@details Writes ERROR to log if abortType is SOFT, else will call %mf_abort.
|
|
5
|
+
Usage:
|
|
6
|
+
|
|
7
|
+
%let var1=x;
|
|
8
|
+
%let var2=y;
|
|
9
|
+
%put %mf_verifymacvars(var1 var2);
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
> 1
|
|
13
|
+
|
|
14
|
+
<h4> SAS Macros </h4>
|
|
15
|
+
@li mf_abort.sas
|
|
16
|
+
|
|
17
|
+
@param [in] verifyvars Space separated list of macro variable names
|
|
18
|
+
@param [in] makeupcase= (NO) Set to YES to convert all variable VALUES to
|
|
19
|
+
uppercase.
|
|
20
|
+
@param [in] mAbort= (SOFT) Abort Type. When SOFT, simply writes an err
|
|
21
|
+
message to the log.
|
|
22
|
+
Set to any other value to call mf_abort (which can be configured to abort in
|
|
23
|
+
various fashions according to context).
|
|
24
|
+
|
|
25
|
+
@warning will not be able to verify the following variables due to
|
|
26
|
+
naming clash!
|
|
27
|
+
- verifyVars
|
|
28
|
+
- verifyVar
|
|
29
|
+
- verifyIterator
|
|
30
|
+
- makeUpcase
|
|
31
|
+
|
|
32
|
+
@version 9.2
|
|
33
|
+
@author Allan Bowe
|
|
34
|
+
|
|
35
|
+
**/
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
%macro mf_verifymacvars(
|
|
39
|
+
verifyVars /* list of macro variable NAMES */
|
|
40
|
+
,makeUpcase=NO /* set to YES to make all the variable VALUES uppercase */
|
|
41
|
+
,mAbort=SOFT
|
|
42
|
+
)/*/STORE SOURCE*/;
|
|
43
|
+
|
|
44
|
+
%local verifyIterator verifyVar abortmsg;
|
|
45
|
+
%do verifyIterator=1 %to %sysfunc(countw(&verifyVars,%str( )));
|
|
46
|
+
%let verifyVar=%qscan(&verifyVars,&verifyIterator,%str( ));
|
|
47
|
+
%if not %symexist(&verifyvar) %then %do;
|
|
48
|
+
%let abortmsg= Variable &verifyVar is MISSING;
|
|
49
|
+
%goto exit_err;
|
|
50
|
+
%end;
|
|
51
|
+
%if %length(%trim(&&&verifyVar))=0 %then %do;
|
|
52
|
+
%let abortmsg= Variable &verifyVar is EMPTY;
|
|
53
|
+
%goto exit_err;
|
|
54
|
+
%end;
|
|
55
|
+
%if &makeupcase=YES %then %do;
|
|
56
|
+
%let &verifyVar=%upcase(&&&verifyvar);
|
|
57
|
+
%end;
|
|
58
|
+
%end;
|
|
59
|
+
|
|
60
|
+
%goto exit_success;
|
|
61
|
+
%exit_err:
|
|
62
|
+
%put &abortmsg;
|
|
63
|
+
%mf_abort(iftrue=(&mabort ne SOFT),
|
|
64
|
+
mac=mf_verifymacvars,
|
|
65
|
+
msg=%str(&abortmsg)
|
|
66
|
+
)
|
|
67
|
+
0
|
|
68
|
+
%return;
|
|
69
|
+
%exit_success:
|
|
70
|
+
1
|
|
71
|
+
|
|
72
|
+
%mend mf_verifymacvars;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Returns words that are in both string 1 and string 2
|
|
4
|
+
@details Compares two space separated strings and returns the words that are
|
|
5
|
+
in both.
|
|
6
|
+
Usage:
|
|
7
|
+
|
|
8
|
+
%put %mf_wordsInStr1andStr2(
|
|
9
|
+
Str1=blah sss blaaah brah bram boo
|
|
10
|
+
,Str2= blah blaaah brah ssss
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
returns:
|
|
14
|
+
> blah blaaah brah
|
|
15
|
+
|
|
16
|
+
@param str1= string containing words to extract
|
|
17
|
+
@param str2= used to compare with the extract string
|
|
18
|
+
|
|
19
|
+
@warning CASE SENSITIVE!
|
|
20
|
+
|
|
21
|
+
@version 9.2
|
|
22
|
+
@author Allan Bowe
|
|
23
|
+
|
|
24
|
+
**/
|
|
25
|
+
|
|
26
|
+
%macro mf_wordsInStr1andStr2(
|
|
27
|
+
Str1= /* string containing words to extract */
|
|
28
|
+
,Str2= /* used to compare with the extract string */
|
|
29
|
+
)/*/STORE SOURCE*/;
|
|
30
|
+
|
|
31
|
+
%local count_base count_extr i i2 extr_word base_word match outvar;
|
|
32
|
+
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
|
|
33
|
+
%put base string (str1)= &str1;
|
|
34
|
+
%put compare string (str2) = &str2;
|
|
35
|
+
%return;
|
|
36
|
+
%end;
|
|
37
|
+
%let count_base=%sysfunc(countw(&Str2));
|
|
38
|
+
%let count_extr=%sysfunc(countw(&Str1));
|
|
39
|
+
|
|
40
|
+
%do i=1 %to &count_extr;
|
|
41
|
+
%let extr_word=%scan(&Str1,&i,%str( ));
|
|
42
|
+
%let match=0;
|
|
43
|
+
%do i2=1 %to &count_base;
|
|
44
|
+
%let base_word=%scan(&Str2,&i2,%str( ));
|
|
45
|
+
%if &extr_word=&base_word %then %let match=1;
|
|
46
|
+
%end;
|
|
47
|
+
%if &match=1 %then %let outvar=&outvar &extr_word;
|
|
48
|
+
%end;
|
|
49
|
+
|
|
50
|
+
&outvar
|
|
51
|
+
|
|
52
|
+
%mend mf_wordsInStr1andStr2;
|
|
53
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Returns words that are in string 1 but not in string 2
|
|
4
|
+
@details Compares two space separated strings and returns the words that are
|
|
5
|
+
in the first but not in the second.
|
|
6
|
+
|
|
7
|
+
Note - case sensitive!
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
|
|
11
|
+
%let x= %mf_wordsInStr1ButNotStr2(
|
|
12
|
+
Str1=blah sss blaaah brah bram boo
|
|
13
|
+
,Str2= blah blaaah brah ssss
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
returns:
|
|
17
|
+
> sss bram boo
|
|
18
|
+
|
|
19
|
+
@param [in] str1= string containing words to extract
|
|
20
|
+
@param [in] str2= used to compare with the extract string
|
|
21
|
+
|
|
22
|
+
@version 9.2
|
|
23
|
+
@author Allan Bowe
|
|
24
|
+
|
|
25
|
+
**/
|
|
26
|
+
|
|
27
|
+
%macro mf_wordsInStr1ButNotStr2(
|
|
28
|
+
Str1= /* string containing words to extract */
|
|
29
|
+
,Str2= /* used to compare with the extract string */
|
|
30
|
+
)/*/STORE SOURCE*/;
|
|
31
|
+
|
|
32
|
+
%local count_base count_extr i i2 extr_word base_word match outvar;
|
|
33
|
+
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
|
|
34
|
+
%put base string (str1)= &str1;
|
|
35
|
+
%put compare string (str2) = &str2;
|
|
36
|
+
%return;
|
|
37
|
+
%end;
|
|
38
|
+
%let count_base=%sysfunc(countw(&Str2));
|
|
39
|
+
%let count_extr=%sysfunc(countw(&Str1));
|
|
40
|
+
|
|
41
|
+
%do i=1 %to &count_extr;
|
|
42
|
+
%let extr_word=%scan(&Str1,&i,%str( ));
|
|
43
|
+
%let match=0;
|
|
44
|
+
%do i2=1 %to &count_base;
|
|
45
|
+
%let base_word=%scan(&Str2,&i2,%str( ));
|
|
46
|
+
%if &extr_word=&base_word %then %let match=1;
|
|
47
|
+
%end;
|
|
48
|
+
%if &match=0 %then %let outvar=&outvar &extr_word;
|
|
49
|
+
%end;
|
|
50
|
+
|
|
51
|
+
&outvar
|
|
52
|
+
|
|
53
|
+
%mend mf_wordsInStr1ButNotStr2;
|
|
54
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Creates a text file using pure macro
|
|
4
|
+
@details Creates a text file of up to 10 lines. If further lines are
|
|
5
|
+
desired, feel free to [create an issue](
|
|
6
|
+
https://github.com/sasjs/core/issues/new), or make a pull request!
|
|
7
|
+
|
|
8
|
+
The use of PARMBUFF was considered for this macro, but it would have made
|
|
9
|
+
things problematic for writing lines containing commas.
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
|
|
13
|
+
%mf_writefile(&sasjswork/myfile.txt,l1=some content,l2=more content)
|
|
14
|
+
data _null_;
|
|
15
|
+
infile "&sasjswork/myfile.txt";
|
|
16
|
+
input;
|
|
17
|
+
list;
|
|
18
|
+
run;
|
|
19
|
+
|
|
20
|
+
@param [in] fpath Full path to file to be created or appended to
|
|
21
|
+
@param [in] mode= (O) Available options are A or O as follows:
|
|
22
|
+
@li A APPEND mode, writes new records after the current end of the file.
|
|
23
|
+
@li O OUTPUT mode, writes new records from the beginning of the file.
|
|
24
|
+
@param [in] l1= First line
|
|
25
|
+
@param [in] l2= Second line (etc through to l10)
|
|
26
|
+
|
|
27
|
+
<h4> Related Macros </h4>
|
|
28
|
+
@li mf_writefile.test.sas
|
|
29
|
+
|
|
30
|
+
@version 9.2
|
|
31
|
+
@author Allan Bowe
|
|
32
|
+
**/
|
|
33
|
+
/** @cond */
|
|
34
|
+
|
|
35
|
+
%macro mf_writefile(fpath,mode=O,l1=,l2=,l3=,l4=,l5=,l6=,l7=,l8=,l9=,l10=
|
|
36
|
+
)/*/STORE SOURCE*/;
|
|
37
|
+
%local fref rc fid i total_lines;
|
|
38
|
+
|
|
39
|
+
/* find number of lines by reference to first non-blank param */
|
|
40
|
+
%do i=10 %to 1 %by -1;
|
|
41
|
+
%if %str(&&l&i) ne %str() %then %goto continue;
|
|
42
|
+
%end;
|
|
43
|
+
%continue:
|
|
44
|
+
%let total_lines=&i;
|
|
45
|
+
|
|
46
|
+
%if %sysfunc(filename(fref,&fpath)) ne 0 %then %do;
|
|
47
|
+
%put &=fref &=fpath;
|
|
48
|
+
%put %str(ERR)OR: %sysfunc(sysmsg());
|
|
49
|
+
%return;
|
|
50
|
+
%end;
|
|
51
|
+
|
|
52
|
+
%let fid=%sysfunc(fopen(&fref,&mode));
|
|
53
|
+
|
|
54
|
+
%if &fid=0 %then %do;
|
|
55
|
+
%put %str(ERR)OR: %sysfunc(sysmsg());
|
|
56
|
+
%return;
|
|
57
|
+
%end;
|
|
58
|
+
|
|
59
|
+
%do i=1 %to &total_lines;
|
|
60
|
+
%let rc=%sysfunc(fput(&fid, &&l&i));
|
|
61
|
+
%let rc=%sysfunc(fwrite(&fid));
|
|
62
|
+
%end;
|
|
63
|
+
%let rc=%sysfunc(fclose(&fid));
|
|
64
|
+
%let rc=%sysfunc(filename(&fref));
|
|
65
|
+
|
|
66
|
+
%mend mf_writefile;
|
|
67
|
+
/** @endcond */
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief abort gracefully according to context
|
|
4
|
+
@details Configures an abort mechanism according to site specific policies or
|
|
5
|
+
the particulars of an environment. For instance, can stream custom
|
|
6
|
+
results back to the client in an STP Web App context, or completely stop
|
|
7
|
+
in the case of a batch run. For STP sessions
|
|
8
|
+
|
|
9
|
+
The method used varies according to the context. Important points:
|
|
10
|
+
|
|
11
|
+
@li should not use endsas or abort cancel in 9.4m3 WIN environments as this
|
|
12
|
+
can cause hung multibridge sessions and result in a frozen STP server
|
|
13
|
+
@li The use of endsas in 9.4m6+ windows environments for POST requests to the
|
|
14
|
+
STP server can result in an empty response body
|
|
15
|
+
@li should not use endsas in viya 3.5 as this destroys the session and cannot
|
|
16
|
+
fetch results (although both mv_getjoblog.sas and the @sasjs/adapter will
|
|
17
|
+
recognise this and fetch the log of the parent session instead)
|
|
18
|
+
@li STP environments must finish cleanly to avoid the log being sent to
|
|
19
|
+
_webout. To assist with this, we also run stpsrvset('program error', 0)
|
|
20
|
+
and set SYSCC=0.
|
|
21
|
+
Where possible, we take a unique "soft abort" approach - we open a macro
|
|
22
|
+
but don't close it! This works everywhere EXCEPT inside a \%include inside
|
|
23
|
+
a macro. For that, we recommend you use mp_include.sas to perform the
|
|
24
|
+
include, and then call \%mp_abort(mode=INCLUDE) from the source program (ie,
|
|
25
|
+
OUTSIDE of the top-parent macro).
|
|
26
|
+
The soft abort has become ineffective in 9.4m6 WINDOWS environments. We are
|
|
27
|
+
currently investigating approaches to deal with this.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@param mac= (mp_abort.sas) To contain the name of the calling macro. Do not
|
|
31
|
+
use &sysmacroname as this will always resolve to MP_ABORT.
|
|
32
|
+
@param msg= message to be returned
|
|
33
|
+
@param iftrue= (1=1) Supply a condition for which the macro should be executed
|
|
34
|
+
@param errds= (work.mp_abort_errds) There is no clean way to end a process
|
|
35
|
+
within a %include called within a macro. Furthermore, there is no way to
|
|
36
|
+
test if a macro is called within a %include. To handle this particular
|
|
37
|
+
scenario, the %include should be switched for the mp_include.sas macro.
|
|
38
|
+
This provides an indicator that we are running a macro within a \%include
|
|
39
|
+
(`_SYSINCLUDEFILEDEVICE`) and allows us to provide a dataset with the abort
|
|
40
|
+
values (msg, mac).
|
|
41
|
+
We can then run an abort cancel FILE to stop the include running, and pass
|
|
42
|
+
the dataset back to the calling program to run a regular \%mp_abort().
|
|
43
|
+
The dataset will contain the following fields:
|
|
44
|
+
@li iftrue (1=1)
|
|
45
|
+
@li msg (the message)
|
|
46
|
+
@li mac (the mac param)
|
|
47
|
+
|
|
48
|
+
@param mode= (REGULAR) If mode=INCLUDE then the &errds dataset is checked for
|
|
49
|
+
an abort status.
|
|
50
|
+
Valid values:
|
|
51
|
+
@li REGULAR (default)
|
|
52
|
+
@li INCLUDE
|
|
53
|
+
|
|
54
|
+
@version 9.4
|
|
55
|
+
@author Allan Bowe
|
|
56
|
+
|
|
57
|
+
<h4> Related Macros </h4>
|
|
58
|
+
@li mp_include.sas
|
|
59
|
+
|
|
60
|
+
@cond
|
|
61
|
+
**/
|
|
62
|
+
|
|
63
|
+
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
|
64
|
+
, errds=work.mp_abort_errds
|
|
65
|
+
, mode=REGULAR
|
|
66
|
+
)/*/STORE SOURCE*/;
|
|
67
|
+
|
|
68
|
+
%global sysprocessmode sysprocessname sasjs_stpsrv_header_loc sasjsprocessmode;
|
|
69
|
+
%local fref fid i;
|
|
70
|
+
|
|
71
|
+
%if not(%eval(%unquote(&iftrue))) %then %return;
|
|
72
|
+
|
|
73
|
+
%put NOTE: /// mp_abort macro executing //;
|
|
74
|
+
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
|
75
|
+
%put NOTE - &msg;
|
|
76
|
+
|
|
77
|
+
%if %symexist(_SYSINCLUDEFILEDEVICE)
|
|
78
|
+
/* abort cancel FILE does not restart outside the INCLUDE on Viya 3.5 */
|
|
79
|
+
and %superq(SYSPROCESSNAME) ne %str(Compute Server)
|
|
80
|
+
%then %do;
|
|
81
|
+
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
|
|
82
|
+
data &errds;
|
|
83
|
+
iftrue='1=1';
|
|
84
|
+
length mac $100 msg $5000;
|
|
85
|
+
mac=symget('mac');
|
|
86
|
+
msg=symget('msg');
|
|
87
|
+
run;
|
|
88
|
+
data _null_;
|
|
89
|
+
abort cancel FILE;
|
|
90
|
+
run;
|
|
91
|
+
%return;
|
|
92
|
+
%end;
|
|
93
|
+
%end;
|
|
94
|
+
|
|
95
|
+
/* Web App Context */
|
|
96
|
+
%if %symexist(_PROGRAM)
|
|
97
|
+
or %superq(SYSPROCESSNAME) = %str(Compute Server)
|
|
98
|
+
or &mode=INCLUDE
|
|
99
|
+
%then %do;
|
|
100
|
+
options obs=max replace mprint;
|
|
101
|
+
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5"
|
|
102
|
+
%then %do;
|
|
103
|
+
options nosyntaxcheck;
|
|
104
|
+
%end;
|
|
105
|
+
|
|
106
|
+
%if &mode=INCLUDE %then %do;
|
|
107
|
+
%if %sysfunc(exist(&errds))=1 %then %do;
|
|
108
|
+
data _null_;
|
|
109
|
+
set &errds;
|
|
110
|
+
call symputx('iftrue',iftrue,'l');
|
|
111
|
+
call symputx('mac',mac,'l');
|
|
112
|
+
call symputx('msg',msg,'l');
|
|
113
|
+
putlog (_all_)(=);
|
|
114
|
+
run;
|
|
115
|
+
%if (&iftrue)=0 %then %return;
|
|
116
|
+
%end;
|
|
117
|
+
%else %do;
|
|
118
|
+
%put &sysmacroname: No include errors found;
|
|
119
|
+
%return;
|
|
120
|
+
%end;
|
|
121
|
+
%end;
|
|
122
|
+
|
|
123
|
+
/* extract log errs / warns, if exist */
|
|
124
|
+
%local logloc logline;
|
|
125
|
+
%global logmsg; /* capture global messages */
|
|
126
|
+
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
|
|
127
|
+
%else %let logloc=%qsysfunc(getoption(LOG));
|
|
128
|
+
proc printto log=log;run;
|
|
129
|
+
%let logline=0;
|
|
130
|
+
%if %length(&logloc)>0 %then %do;
|
|
131
|
+
data _null_;
|
|
132
|
+
infile &logloc lrecl=5000;
|
|
133
|
+
input; putlog _infile_;
|
|
134
|
+
i=1;
|
|
135
|
+
retain logonce 0;
|
|
136
|
+
if (
|
|
137
|
+
_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR"
|
|
138
|
+
) and logonce=0 then
|
|
139
|
+
do;
|
|
140
|
+
call symputx('logline',_n_);
|
|
141
|
+
logonce+1;
|
|
142
|
+
end;
|
|
143
|
+
run;
|
|
144
|
+
/* capture log including lines BEFORE the err */
|
|
145
|
+
%if &logline>0 %then %do;
|
|
146
|
+
data _null_;
|
|
147
|
+
infile &logloc lrecl=5000;
|
|
148
|
+
input;
|
|
149
|
+
i=1;
|
|
150
|
+
stoploop=0;
|
|
151
|
+
if _n_ ge &logline-15 and stoploop=0 then do until (i>22);
|
|
152
|
+
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
|
|
153
|
+
input;
|
|
154
|
+
i+1;
|
|
155
|
+
stoploop=1;
|
|
156
|
+
end;
|
|
157
|
+
if stoploop=1 then stop;
|
|
158
|
+
run;
|
|
159
|
+
%end;
|
|
160
|
+
%end;
|
|
161
|
+
|
|
162
|
+
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
|
163
|
+
/* setup webout for Viya */
|
|
164
|
+
options nobomfile;
|
|
165
|
+
%if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
|
|
166
|
+
filename _webout temp lrecl=999999 mod;
|
|
167
|
+
%end;
|
|
168
|
+
%else %do;
|
|
169
|
+
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
|
170
|
+
name="_webout.json" lrecl=999999 mod;
|
|
171
|
+
%end;
|
|
172
|
+
%end;
|
|
173
|
+
%else %if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc))=0 %then %do;
|
|
174
|
+
options nobomfile;
|
|
175
|
+
/* set up http header for SASjs Server */
|
|
176
|
+
%let fid=%sysfunc(fopen(&fref,A));
|
|
177
|
+
%if &fid=0 %then %do;
|
|
178
|
+
%put %str(ERR)OR: %sysfunc(sysmsg());
|
|
179
|
+
%return;
|
|
180
|
+
%end;
|
|
181
|
+
%let rc=%sysfunc(fput(&fid,%str(Content-Type: application/json)));
|
|
182
|
+
%let rc=%sysfunc(fwrite(&fid));
|
|
183
|
+
%let rc=%sysfunc(fclose(&fid));
|
|
184
|
+
%let rc=%sysfunc(filename(&fref));
|
|
185
|
+
%end;
|
|
186
|
+
|
|
187
|
+
/* send response in SASjs JSON format */
|
|
188
|
+
data _null_;
|
|
189
|
+
file _webout mod lrecl=32000 encoding='utf-8';
|
|
190
|
+
length msg syswarningtext syserrortext $32767 mode $10 ;
|
|
191
|
+
sasdatetime=datetime();
|
|
192
|
+
msg=symget('msg');
|
|
193
|
+
%if &logline>0 %then %do;
|
|
194
|
+
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
|
|
195
|
+
%end;
|
|
196
|
+
/* escape the escapes */
|
|
197
|
+
msg=tranwrd(msg,'\','\\');
|
|
198
|
+
/* escape the quotes */
|
|
199
|
+
msg=tranwrd(msg,'"','\"');
|
|
200
|
+
/* ditch the CRLFs as chrome complains */
|
|
201
|
+
msg=compress(msg,,'kw');
|
|
202
|
+
/* quote without quoting the quotes (which are escaped instead) */
|
|
203
|
+
msg=cats('"',msg,'"');
|
|
204
|
+
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
|
205
|
+
else debug='""';
|
|
206
|
+
if symget('sasjsprocessmode')='Stored Program' then mode='SASJS';
|
|
207
|
+
if mode ne 'SASJS' then put '>>weboutBEGIN<<';
|
|
208
|
+
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
|
209
|
+
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
|
210
|
+
put ',"sasjsAbort" : [{';
|
|
211
|
+
put ' "MSG":' msg ;
|
|
212
|
+
put ' ,"MAC": "' "&mac" '"}]';
|
|
213
|
+
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
|
214
|
+
put ',"_DEBUG":' debug ;
|
|
215
|
+
if symexist('_metauser') then do;
|
|
216
|
+
_METAUSER=quote(trim(symget('_METAUSER')));
|
|
217
|
+
put ",""_METAUSER"": " _METAUSER;
|
|
218
|
+
_METAPERSON=quote(trim(symget('_METAPERSON')));
|
|
219
|
+
put ',"_METAPERSON": ' _METAPERSON;
|
|
220
|
+
end;
|
|
221
|
+
if symexist('SYS_JES_JOB_URI') then do;
|
|
222
|
+
SYS_JES_JOB_URI=quote(trim(symget('SYS_JES_JOB_URI')));
|
|
223
|
+
put ',"SYS_JES_JOB_URI": ' SYS_JES_JOB_URI;
|
|
224
|
+
end;
|
|
225
|
+
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
|
226
|
+
put ',"_PROGRAM" : ' _PROGRAM ;
|
|
227
|
+
put ",""SYSCC"" : ""&syscc"" ";
|
|
228
|
+
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"');
|
|
229
|
+
put ",""SYSERRORTEXT"" : " syserrortext;
|
|
230
|
+
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
|
231
|
+
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
|
232
|
+
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
|
233
|
+
put ",""SYSSITE"" : ""&syssite"" ";
|
|
234
|
+
sysvlong=quote(trim(symget('sysvlong')));
|
|
235
|
+
put ',"SYSVLONG" : ' sysvlong;
|
|
236
|
+
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"');
|
|
237
|
+
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
|
238
|
+
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
|
239
|
+
put "}" ;
|
|
240
|
+
if mode ne 'SASJS' then put '>>weboutEND<<';
|
|
241
|
+
run;
|
|
242
|
+
|
|
243
|
+
%put _all_;
|
|
244
|
+
|
|
245
|
+
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
|
246
|
+
data _null_;
|
|
247
|
+
putlog 'stpsrvset program err and syscc';
|
|
248
|
+
rc=stpsrvset('program error', 0);
|
|
249
|
+
call symputx("syscc",0,"g");
|
|
250
|
+
run;
|
|
251
|
+
%if &sysscp=WIN
|
|
252
|
+
and 1=0 /* deprecating this logic until we figure out a consistent abort */
|
|
253
|
+
and "%substr(%str(&sysvlong ),1,8)"="9.04.01M"
|
|
254
|
+
and "%substr(%str(&sysvlong ),9,1)">"5" %then %do;
|
|
255
|
+
/* skip approach (below) does not work in windows m6+ envs */
|
|
256
|
+
endsas;
|
|
257
|
+
%end;
|
|
258
|
+
%else %do;
|
|
259
|
+
/**
|
|
260
|
+
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
|
261
|
+
* Abort variants are ungraceful (non zero return code)
|
|
262
|
+
* This approach lets SAS run silently until the end :-)
|
|
263
|
+
* Caution - fails when called within a %include within a macro
|
|
264
|
+
* Use mp_include() to handle this.
|
|
265
|
+
*/
|
|
266
|
+
filename skip temp;
|
|
267
|
+
data _null_;
|
|
268
|
+
file skip;
|
|
269
|
+
put '%macro skip();';
|
|
270
|
+
comment '%mend skip; -> fix lint ';
|
|
271
|
+
put '%macro skippy();';
|
|
272
|
+
comment '%mend skippy; -> fix lint ';
|
|
273
|
+
run;
|
|
274
|
+
%inc skip;
|
|
275
|
+
%end;
|
|
276
|
+
%end;
|
|
277
|
+
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
|
278
|
+
/* endsas kills the session making it harder to fetch results */
|
|
279
|
+
data _null_;
|
|
280
|
+
syswarningtext=symget('syswarningtext');
|
|
281
|
+
syserrortext=symget('syserrortext');
|
|
282
|
+
abort_msg=symget('msg');
|
|
283
|
+
syscc=symget('syscc');
|
|
284
|
+
sysuserid=symget('sysuserid');
|
|
285
|
+
iftrue=symget('iftrue');
|
|
286
|
+
put (_all_)(/=);
|
|
287
|
+
call symputx('syscc',0);
|
|
288
|
+
abort cancel nolist;
|
|
289
|
+
run;
|
|
290
|
+
%end;
|
|
291
|
+
%else %do;
|
|
292
|
+
%abort cancel;
|
|
293
|
+
%end;
|
|
294
|
+
%end;
|
|
295
|
+
%else %do;
|
|
296
|
+
%put _all_;
|
|
297
|
+
%abort cancel;
|
|
298
|
+
%end;
|
|
299
|
+
%mend mp_abort;
|
|
300
|
+
|
|
301
|
+
/** @endcond */
|