@sasjs/utils 2.49.0 → 2.49.1

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.
Files changed (144) hide show
  1. package/fs/internal/helper.js +19 -24
  2. package/fs/internal/helper.js.map +1 -1
  3. package/macros/mf_abort.sas +27 -0
  4. package/macros/mf_dedup.sas +53 -0
  5. package/macros/mf_deletefile.sas +31 -0
  6. package/macros/mf_existds.sas +30 -0
  7. package/macros/mf_existfeature.sas +59 -0
  8. package/macros/mf_existfileref.sas +33 -0
  9. package/macros/mf_existfunction.sas +37 -0
  10. package/macros/mf_existvar.sas +39 -0
  11. package/macros/mf_existvarlist.sas +56 -0
  12. package/macros/mf_fmtdttm.sas +42 -0
  13. package/macros/mf_getapploc.sas +78 -0
  14. package/macros/mf_getattrc.sas +34 -0
  15. package/macros/mf_getattrn.sas +34 -0
  16. package/macros/mf_getengine.sas +54 -0
  17. package/macros/mf_getfilesize.sas +64 -0
  18. package/macros/mf_getfmtlist.sas +61 -0
  19. package/macros/mf_getfmtname.sas +44 -0
  20. package/macros/mf_getkeyvalue.sas +32 -0
  21. package/macros/mf_getplatform.sas +75 -0
  22. package/macros/mf_getquotedstr.sas +57 -0
  23. package/macros/mf_getschema.sas +43 -0
  24. package/macros/mf_getuniquefileref.sas +60 -0
  25. package/macros/mf_getuniquelibref.sas +57 -0
  26. package/macros/mf_getuniquename.sas +22 -0
  27. package/macros/mf_getuser.sas +44 -0
  28. package/macros/mf_getvalue.sas +36 -0
  29. package/macros/mf_getvarcount.sas +49 -0
  30. package/macros/mf_getvarformat.sas +72 -0
  31. package/macros/mf_getvarlen.sas +56 -0
  32. package/macros/mf_getvarlist.sas +74 -0
  33. package/macros/mf_getvarnum.sas +58 -0
  34. package/macros/mf_getvartype.sas +52 -0
  35. package/macros/mf_getxengine.sas +43 -0
  36. package/macros/mf_increment.sas +29 -0
  37. package/macros/mf_isblank.sas +27 -0
  38. package/macros/mf_isdir.sas +34 -0
  39. package/macros/mf_isint.sas +36 -0
  40. package/macros/mf_islibds.sas +40 -0
  41. package/macros/mf_loc.sas +29 -0
  42. package/macros/mf_mkdir.sas +67 -0
  43. package/macros/mf_mval.sas +19 -0
  44. package/macros/mf_nobs.sas +26 -0
  45. package/macros/mf_trimstr.sas +50 -0
  46. package/macros/mf_uid.sas +21 -0
  47. package/macros/mf_verifymacvars.sas +72 -0
  48. package/macros/mf_wordsinstr1andstr2.sas +53 -0
  49. package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
  50. package/macros/mf_writefile.sas +67 -0
  51. package/macros/mp_abort.sas +301 -0
  52. package/macros/mp_appendfile.sas +57 -0
  53. package/macros/mp_applyformats.sas +181 -0
  54. package/macros/mp_assert.sas +56 -0
  55. package/macros/mp_assertcols.sas +145 -0
  56. package/macros/mp_assertcolvals.sas +168 -0
  57. package/macros/mp_assertdsobs.sas +121 -0
  58. package/macros/mp_assertscope.sas +147 -0
  59. package/macros/mp_base64copy.sas +117 -0
  60. package/macros/mp_binarycopy.sas +79 -0
  61. package/macros/mp_chop.sas +194 -0
  62. package/macros/mp_cleancsv.sas +78 -0
  63. package/macros/mp_cntlout.sas +85 -0
  64. package/macros/mp_copyfolder.sas +84 -0
  65. package/macros/mp_coretable.sas +73 -0
  66. package/macros/mp_createconstraints.sas +70 -0
  67. package/macros/mp_createwebservice.sas +32 -0
  68. package/macros/mp_csv2ds.sas +140 -0
  69. package/macros/mp_deleteconstraints.sas +52 -0
  70. package/macros/mp_deletefolder.sas +70 -0
  71. package/macros/mp_dictionary.sas +52 -0
  72. package/macros/mp_dirlist.sas +263 -0
  73. package/macros/mp_distinctfmtvalues.sas +50 -0
  74. package/macros/mp_dropmembers.sas +46 -0
  75. package/macros/mp_ds2cards.sas +291 -0
  76. package/macros/mp_ds2csv.sas +212 -0
  77. package/macros/mp_ds2ddl.sas +30 -0
  78. package/macros/mp_ds2fmtds.sas +105 -0
  79. package/macros/mp_ds2inserts.sas +180 -0
  80. package/macros/mp_ds2md.sas +103 -0
  81. package/macros/mp_ds2squeeze.sas +120 -0
  82. package/macros/mp_dsmeta.sas +110 -0
  83. package/macros/mp_filtercheck.sas +251 -0
  84. package/macros/mp_filtergenerate.sas +105 -0
  85. package/macros/mp_filterstore.sas +254 -0
  86. package/macros/mp_filtervalidate.sas +105 -0
  87. package/macros/mp_getcols.sas +67 -0
  88. package/macros/mp_getconstraints.sas +115 -0
  89. package/macros/mp_getdbml.sas +335 -0
  90. package/macros/mp_getddl.sas +400 -0
  91. package/macros/mp_getformats.sas +124 -0
  92. package/macros/mp_getmaxvarlengths.sas +130 -0
  93. package/macros/mp_getpk.sas +281 -0
  94. package/macros/mp_gsubfile.sas +58 -0
  95. package/macros/mp_guesspk.sas +332 -0
  96. package/macros/mp_hashdataset.sas +91 -0
  97. package/macros/mp_hashdirectory.sas +162 -0
  98. package/macros/mp_include.sas +106 -0
  99. package/macros/mp_init.sas +76 -0
  100. package/macros/mp_jsonout.sas +406 -0
  101. package/macros/mp_lib2cards.sas +78 -0
  102. package/macros/mp_lib2inserts.sas +77 -0
  103. package/macros/mp_loadformat.sas +303 -0
  104. package/macros/mp_lockanytable.sas +243 -0
  105. package/macros/mp_lockfilecheck.sas +103 -0
  106. package/macros/mp_makedata.sas +107 -0
  107. package/macros/mp_md5.sas +58 -0
  108. package/macros/mp_perflog.sas +43 -0
  109. package/macros/mp_prevobs.sas +88 -0
  110. package/macros/mp_recursivejoin.sas +90 -0
  111. package/macros/mp_replace.sas +151 -0
  112. package/macros/mp_reseterror.sas +27 -0
  113. package/macros/mp_resetoption.sas +39 -0
  114. package/macros/mp_retainedkey.sas +247 -0
  115. package/macros/mp_runddl.sas +49 -0
  116. package/macros/mp_searchcols.sas +103 -0
  117. package/macros/mp_searchdata.sas +131 -0
  118. package/macros/mp_setkeyvalue.sas +55 -0
  119. package/macros/mp_sortinplace.sas +122 -0
  120. package/macros/mp_stackdiffs.sas +592 -0
  121. package/macros/mp_storediffs.sas +230 -0
  122. package/macros/mp_stprequests.sas +74 -0
  123. package/macros/mp_streamfile.sas +212 -0
  124. package/macros/mp_testjob.sas +92 -0
  125. package/macros/mp_testservice.sas +39 -0
  126. package/macros/mp_testwritespeedlibrary.sas +59 -0
  127. package/macros/mp_tree.sas +71 -0
  128. package/macros/mp_unzip.sas +90 -0
  129. package/macros/mp_updatevarlength.sas +93 -0
  130. package/macros/mp_validatecol.sas +97 -0
  131. package/macros/mp_wait4file.sas +35 -0
  132. package/macros/mp_webin.sas +59 -0
  133. package/macros/mp_zip.sas +85 -0
  134. package/package.json +4 -1
  135. package/scripts/copySASjsCore.ts +36 -0
  136. package/utils/index.d.ts +1 -2
  137. package/utils/index.js +2 -3
  138. package/utils/index.js.map +1 -1
  139. package/utils/utils.d.ts +1 -0
  140. package/utils/utils.js +7 -1
  141. package/utils/utils.js.map +1 -1
  142. package/utils/getNodeModulePath.d.ts +0 -2
  143. package/utils/getNodeModulePath.js +0 -83
  144. package/utils/getNodeModulePath.js.map +0 -1
@@ -0,0 +1,91 @@
1
+ /**
2
+ @file
3
+ @brief Returns a unique hash for a dataset
4
+ @details Ignores metadata attributes, used only to hash values. If used to
5
+ compare datasets, they must have their columns and rows in the same order.
6
+
7
+ %mp_hashdataset(sashelp.class,outds=myhash)
8
+
9
+ data _null_;
10
+ set work.myhash;
11
+ put hashkey=;
12
+ run;
13
+
14
+ ![sas md5 hash dataset log results](https://i.4gl.io/1/KorUKoyE05.png/raw)
15
+
16
+ <h4> SAS Macros </h4>
17
+ @li mf_getattrn.sas
18
+ @li mf_getuniquename.sas
19
+ @li mf_getvarlist.sas
20
+ @li mp_md5.sas
21
+
22
+ <h4> Related Files </h4>
23
+ @li mp_hashdataset.test.sas
24
+ @li mp_hashdirectory.sas
25
+
26
+ @param [in] libds dataset to hash
27
+ @param [in] salt= Provide a salt (could be, for instance, the dataset name)
28
+ @param [in] iftrue= (1=1) A condition under which the macro should be executed
29
+ @param [out] outds= (work._data_) The output dataset to create. This
30
+ will contain one column (hashkey) with one observation (a $hex32.
31
+ representation of the input hash)
32
+ |hashkey:$32.|
33
+ |---|
34
+ |28ABC74ABFC45F50794237BA5566E6CA|
35
+
36
+ @version 9.2
37
+ @author Allan Bowe
38
+ **/
39
+
40
+ %macro mp_hashdataset(
41
+ libds,
42
+ outds=work._data_,
43
+ salt=,
44
+ iftrue=%str(1=1)
45
+ )/*/STORE SOURCE*/;
46
+
47
+ %local keyvar /* roll up the md5 */
48
+ prevkeyvar /* retain prev record md5 */
49
+ lastvar /* last var in input ds */
50
+ cvars nvars;
51
+
52
+ %if not(%eval(%unquote(&iftrue))) %then %return;
53
+
54
+ /* avoid naming conflict for hash key vars */
55
+ %let keyvar=%mf_getuniquename();
56
+ %let prevkeyvar=%mf_getuniquename();
57
+ %let lastvar=%mf_getuniquename();
58
+
59
+ %if %mf_getattrn(&libds,NLOBS)=0 %then %do;
60
+ data &outds;
61
+ length hashkey $32;
62
+ hashkey=put(md5("&salt"),$hex32.);
63
+ output;
64
+ stop;
65
+ run;
66
+ %put &sysmacroname: Dataset &libds is empty, or is not a dataset;
67
+ %put &sysmacroname: hashkey of &outds is based on salt (&salt) only;
68
+ %end;
69
+ %else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
70
+ %put %str(ERR)OR: Dataset &libds is not a dataset;
71
+ %end;
72
+ %else %do;
73
+ data &outds(rename=(&keyvar=hashkey) keep=&keyvar)
74
+ %if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
75
+ /nonote2err
76
+ %end;
77
+ ;
78
+ length &prevkeyvar &keyvar $32;
79
+ retain &prevkeyvar;
80
+ if _n_=1 then &prevkeyvar=put(md5("&salt"),$hex32.);
81
+ set &libds end=&lastvar;
82
+ /* hash should include previous row */
83
+ &keyvar=%mp_md5(
84
+ cvars=%mf_getvarlist(&libds,typefilter=C) &prevkeyvar,
85
+ nvars=%mf_getvarlist(&libds,typefilter=N)
86
+ );
87
+ &prevkeyvar=&keyvar;
88
+ if &lastvar then output;
89
+ run;
90
+ %end;
91
+ %mend mp_hashdataset;
@@ -0,0 +1,162 @@
1
+ /**
2
+ @file
3
+ @brief Returns a unique hash for each file in a directory
4
+ @details Hashes each file in each directory, and then hashes the hashes to
5
+ create a hash for each directory also.
6
+
7
+ This makes use of the new `hashing_file()` and `hashing` functions, available
8
+ since 9.4m6. Interestingly, these can even be used in pure macro, eg:
9
+
10
+ %put %sysfunc(hashing_file(md5,/path/to/file.blob,0));
11
+
12
+ Actual usage:
13
+
14
+ %let fpath=/some/directory;
15
+
16
+ %mp_hashdirectory(&fpath,outds=myhash,maxdepth=2)
17
+
18
+ data _null_;
19
+ set work.myhash;
20
+ put (_all_)(=);
21
+ run;
22
+
23
+ Whilst files are hashed in their entirety, the logic for creating a folder
24
+ hash is as follows:
25
+
26
+ @li Sort the files by filename (case sensitive, uppercase then lower)
27
+ @li Take the first 100 hashes, concatenate and hash
28
+ @li Concatenate this hash with another 100 hashes and hash again
29
+ @li Continue until the end of the folder. This is the folder hash
30
+ @li If a folder contains other folders, start from the bottom of the tree -
31
+ the folder hashes cascade upwards so you know immediately if there is a
32
+ change in a sub/sub directory
33
+ @li If the folder has no content (empty) then it is ignored. No hash created.
34
+ @li If the file is empty, it is also ignored / no hash created.
35
+
36
+ <h4> SAS Macros </h4>
37
+ @li mp_dirlist.sas
38
+
39
+ <h4> Related Files </h4>
40
+ @li mp_hashdataset.sas
41
+ @li mp_hashdirectory.test.sas
42
+ @li mp_md5.sas
43
+
44
+ @param [in] inloc Full filepath of the file to be hashed (unquoted)
45
+ @param [in] iftrue= (1=1) A condition under which the macro should be executed
46
+ @param [in] maxdepth= (0) Set to a positive integer to indicate the level of
47
+ subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited
48
+ recursion, set to MAX.
49
+ @param [in] method= (MD5) the hashing method to use. Available options:
50
+ @li MD5
51
+ @li SH1
52
+ @li SHA256
53
+ @li SHA384
54
+ @li SHA512
55
+ @li CRC32
56
+ @param [out] outds= (work.mp_hashdirectory) The output dataset. Contains:
57
+ @li directory - the parent folder
58
+ @li file_hash - the hash output
59
+ @li hash_duration - how long the hash took (first hash always takes longer)
60
+ @li file_path - /full/path/to/each/file.ext
61
+ @li file_or_folder - contains either "file" or "folder"
62
+ @li level - the depth of the directory (top level is 0)
63
+
64
+ @version 9.4m6
65
+ @author Allan Bowe
66
+ **/
67
+
68
+ %macro mp_hashdirectory(inloc,
69
+ outds=work.mp_hashdirectory,
70
+ method=MD5,
71
+ maxdepth=0,
72
+ iftrue=%str(1=1)
73
+ )/*/STORE SOURCE*/;
74
+
75
+ %local curlevel tempds ;
76
+
77
+ %if not(%eval(%unquote(&iftrue))) %then %return;
78
+
79
+ /* get the directory listing */
80
+ %mp_dirlist(path=&inloc, outds=&outds, maxdepth=&maxdepth, showparent=YES)
81
+
82
+ /* create the hashes */
83
+ data &outds;
84
+ set &outds (rename=(filepath=file_path));
85
+ length FILE_HASH $32 HASH_DURATION 8;
86
+ keep directory file_hash hash_duration file_path file_or_folder level;
87
+
88
+ ts=datetime();
89
+ if file_or_folder='file' then do;
90
+ /* if file is empty, hashing_file will break - so ignore / delete */
91
+ length fname val $8;
92
+ drop fname val fid is_empty;
93
+ rc=filename(fname,file_path);
94
+ fid=fopen(fname);
95
+ if fid > 0 then do;
96
+ rc=fread(fid);
97
+ is_empty=fget(fid,val);
98
+ end;
99
+ rc=fclose(fid);
100
+ rc=filename(fname);
101
+ if is_empty ne 0 then delete;
102
+ else file_hash=hashing_file("&method",cats(file_path),0);
103
+ end;
104
+ hash_duration=datetime()-ts;
105
+ run;
106
+
107
+ proc sort data=&outds ;
108
+ by descending level directory file_path;
109
+ run;
110
+
111
+ data _null_;
112
+ set &outds;
113
+ call symputx('maxlevel',level,'l');
114
+ stop;
115
+ run;
116
+
117
+ /* now hash the hashes to populate folder hashes, starting from the bottom */
118
+ %do curlevel=&maxlevel %to 0 %by -1;
119
+ data work._data_ (keep=directory file_hash);
120
+ set &outds;
121
+ where level=&curlevel;
122
+ by descending level directory file_path;
123
+ length str $32767 tmp_hash $32;
124
+ retain str tmp_hash ;
125
+ /* reset vars when starting a new directory */
126
+ if first.directory then do;
127
+ str='';
128
+ tmp_hash='';
129
+ i=0;
130
+ end;
131
+ /* hash each chunk of 100 file paths */
132
+ i+1;
133
+ str=cats(str,file_hash);
134
+ if mod(i,100)=0 or last.directory then do;
135
+ tmp_hash=hashing("&method",cats(tmp_hash,str));
136
+ str='';
137
+ end;
138
+ /* output the hash at directory level */
139
+ if last.directory then do;
140
+ file_hash=tmp_hash;
141
+ output;
142
+ end;
143
+ if last.level then stop;
144
+ run;
145
+ %let tempds=&syslast;
146
+ /* join the hash back into the main table */
147
+ proc sql undo_policy=none;
148
+ create table &outds as
149
+ select a.directory
150
+ ,coalesce(b.file_hash,a.file_hash) as file_hash
151
+ ,a.hash_duration
152
+ ,a.file_path
153
+ ,a.file_or_folder
154
+ ,a.level
155
+ from &outds a
156
+ left join &tempds b
157
+ on a.file_path=b.directory
158
+ order by level desc, directory, file_path;
159
+ drop table &tempds;
160
+ %end;
161
+
162
+ %mend mp_hashdirectory;
@@ -0,0 +1,106 @@
1
+ /**
2
+ @file
3
+ @brief Performs a wrapped \%include
4
+ @details This macro wrapper is necessary if you need your included code to
5
+ know that it is being \%included.
6
+
7
+ If you are using %include in a regular program, you could make use of the
8
+ following macro variables:
9
+
10
+ @li SYSINCLUDEFILEDEVICE
11
+ @li SYSINCLUDEFILEDIR
12
+ @li SYSINCLUDEFILEFILEREF
13
+ @li SYSINCLUDEFILENAME
14
+
15
+ However these variables are NOT available inside a macro, as documented here:
16
+ https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/n1j5tcc0n2xczyn1kg1o0606gsv9.htm
17
+
18
+ This macro can be used in place of the %include statement, and will insert
19
+ the following (equivalent) global variables:
20
+
21
+ @li _SYSINCLUDEFILEDEVICE
22
+ @li _SYSINCLUDEFILEDIR
23
+ @li _SYSINCLUDEFILEFILEREF
24
+ @li _SYSINCLUDEFILENAME
25
+
26
+ These can be used whenever testing _within a macro_. Outside of the macro,
27
+ the regular automatic variables will still be available (thanks to a
28
+ concatenated file list in the include statement).
29
+
30
+ Example usage:
31
+
32
+ filename example temp;
33
+ data _null_;
34
+ file example;
35
+ put '%macro test();';
36
+ put '%put &=_SYSINCLUDEFILEFILEREF;';
37
+ put '%put &=SYSINCLUDEFILEFILEREF;';
38
+ put '%mend; %test()';
39
+ put '%put &=SYSINCLUDEFILEFILEREF;';
40
+ run;
41
+ %mp_include(example)
42
+
43
+ @param [in] fileref The fileref of the file to be included. Must be provided.
44
+ @param [in] prefix= (_) The prefix to apply to the global variables.
45
+ @param [in] opts= (SOURCE2) The options to apply to the %inc statement
46
+ @param [in] errds= (work.mp_abort_errds) There is no clean way to end a
47
+ process within a %include called within a macro. Furthermore, there is no
48
+ way to test if a macro is called within a %include. To handle this
49
+ particular scenario, the %mp_abort() macro will test for the existence of
50
+ the `_SYSINCLUDEFILEDEVICE` variable and return the outputs (msg,mac) inside
51
+ this dataset.
52
+ It will then run an abort cancel FILE to stop the include running, and pass
53
+ the dataset back.
54
+
55
+ IMPORTANT NOTE - it is NOT possible to read this dataset as part of _this_
56
+ macro! When running abort cancel FILE, ALL macros are closed, so instead it
57
+ is necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of macro wrappers.
58
+
59
+
60
+ @version 9.4
61
+ @author Allan Bowe
62
+
63
+ <h4> SAS Macros </h4>
64
+ @li mf_getuniquefileref.sas
65
+ @li mp_abort.sas
66
+
67
+ **/
68
+
69
+ %macro mp_include(fileref
70
+ ,prefix=_
71
+ ,opts=SOURCE2
72
+ ,errds=work.mp_abort_errds
73
+ )/*/STORE SOURCE*/;
74
+
75
+ /* prepare precode */
76
+ %local tempref;
77
+ %let tempref=%mf_getuniquefileref();
78
+ data _null_;
79
+ file &tempref;
80
+ set sashelp.vextfl(where=(fileref="%upcase(&fileref)"));
81
+ put '%let _SYSINCLUDEFILEDEVICE=' xengine ';';
82
+ name=scan(xpath,-1,'/\');
83
+ put '%let _SYSINCLUDEFILENAME=' name ';';
84
+ path=subpad(xpath,1,length(xpath)-length(name)-1);
85
+ put '%let _SYSINCLUDEFILEDIR=' path ';';
86
+ put '%let _SYSINCLUDEFILEFILEREF=' "&fileref;";
87
+ run;
88
+
89
+ /* prepare the errds */
90
+ data &errds;
91
+ length msg mac $1000;
92
+ call missing(msg,mac);
93
+ iftrue='1=0';
94
+ run;
95
+
96
+ /* include the include */
97
+ %inc &tempref &fileref/&opts;
98
+
99
+ %mp_abort(iftrue= (&syscc ne 0)
100
+ ,mac=%str(&_SYSINCLUDEFILEDIR/&_SYSINCLUDEFILENAME)
101
+ ,msg=%str(syscc=&syscc after executing &_SYSINCLUDEFILENAME)
102
+ )
103
+
104
+ filename &tempref clear;
105
+
106
+ %mend mp_include;
@@ -0,0 +1,76 @@
1
+ /**
2
+ @file
3
+ @brief Initialise session with useful settings and variables
4
+ @details Implements a "strict" set of SAS options for use in defensive
5
+ programming. Highly recommended, if you want your code to run on some
6
+ other machine.
7
+
8
+ This macro is recommended to be compiled and invoked in the `initProgram`
9
+ for SASjs [Jobs](https://cli.sasjs.io/sasjsconfig.html#jobConfig_initProgram
10
+ ), [Services](
11
+ https://cli.sasjs.io/sasjsconfig.html#serviceConfig_initProgram) and [Tests]
12
+ (https://cli.sasjs.io/sasjsconfig.html#testConfig_initProgram).
13
+
14
+ For non SASjs projects, you could invoke in the autoexec, or in your own
15
+ solution initialisation macro.
16
+
17
+
18
+ If you have a good idea for another useful option, setting, or global
19
+ variable - feel free to [raise an issue](
20
+ https://github.com/sasjs/core/issues/new)!
21
+
22
+ All global variables are prefixed with "SASJS" (unless modified with the
23
+ prefix parameter).
24
+
25
+ @param [in] prefix= (SASJS) The prefix to apply to the global macro variables
26
+
27
+
28
+ @version 9.2
29
+ @author Allan Bowe
30
+
31
+ **/
32
+
33
+ %macro mp_init(prefix=SASJS
34
+ )/*/STORE SOURCE*/;
35
+
36
+ %if %symexist(SASJS_PREFIX) %then %return; /* only run once */
37
+
38
+ %global
39
+ SASJS_PREFIX /* the ONLY hard-coded global macro variable in SASjs */
40
+ &prefix._FUNCTIONS /* used in mcf_init() to track core function compilation */
41
+ &prefix._INIT_NUM /* initialisation time as numeric */
42
+ &prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
43
+ &prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
44
+ ;
45
+
46
+ %let sasjs_prefix=&prefix;
47
+
48
+ data _null_;
49
+ dttm=datetime();
50
+ call symputx("&sasjs_prefix._init_num",dttm,'g');
51
+ call symputx("&sasjs_prefix._init_dttm",put(dttm,E8601DT26.6),'g');
52
+ call symputx("&sasjs_prefix.work",pathname('WORK'),'g');
53
+ run;
54
+
55
+ options
56
+ compress=CHAR /* default is none so ensure we have something! */
57
+ datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
58
+ errorcheck=STRICT /* catch errs in libname/filename statements */
59
+ fmterr /* ensure err when a format cannot be found */
60
+ mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
61
+ missing=. /* changing this can cause hard to detect errs */
62
+ noquotelenmax /* avoid warnings for long strings */
63
+ noreplace /* avoid overwriting permanent datasets */
64
+ ps=max /* reduce log size slightly */
65
+ ls=max /* reduce log even more and avoid word truncation */
66
+ validmemname=COMPATIBLE /* avoid special characters etc in table names */
67
+ validvarname=V7 /* avoid special characters etc in variable names */
68
+ varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
69
+ varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
70
+ %if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
71
+ noautocorrect /* disallow misspelled procedure names */
72
+ dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
73
+ %end;
74
+ ;
75
+
76
+ %mend mp_init;