@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.
Files changed (147) 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 +262 -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_gitadd.sas +45 -0
  95. package/macros/mp_gitreleaseinfo.sas +74 -0
  96. package/macros/mp_gitstatus.sas +67 -0
  97. package/macros/mp_gsubfile.sas +58 -0
  98. package/macros/mp_guesspk.sas +332 -0
  99. package/macros/mp_hashdataset.sas +91 -0
  100. package/macros/mp_hashdirectory.sas +164 -0
  101. package/macros/mp_include.sas +106 -0
  102. package/macros/mp_init.sas +76 -0
  103. package/macros/mp_jsonout.sas +406 -0
  104. package/macros/mp_lib2cards.sas +78 -0
  105. package/macros/mp_lib2inserts.sas +77 -0
  106. package/macros/mp_loadformat.sas +303 -0
  107. package/macros/mp_lockanytable.sas +243 -0
  108. package/macros/mp_lockfilecheck.sas +103 -0
  109. package/macros/mp_makedata.sas +107 -0
  110. package/macros/mp_md5.sas +58 -0
  111. package/macros/mp_perflog.sas +43 -0
  112. package/macros/mp_prevobs.sas +88 -0
  113. package/macros/mp_recursivejoin.sas +90 -0
  114. package/macros/mp_replace.sas +151 -0
  115. package/macros/mp_reseterror.sas +27 -0
  116. package/macros/mp_resetoption.sas +39 -0
  117. package/macros/mp_retainedkey.sas +247 -0
  118. package/macros/mp_runddl.sas +49 -0
  119. package/macros/mp_searchcols.sas +103 -0
  120. package/macros/mp_searchdata.sas +131 -0
  121. package/macros/mp_setkeyvalue.sas +55 -0
  122. package/macros/mp_sortinplace.sas +122 -0
  123. package/macros/mp_stackdiffs.sas +592 -0
  124. package/macros/mp_storediffs.sas +230 -0
  125. package/macros/mp_stprequests.sas +74 -0
  126. package/macros/mp_streamfile.sas +212 -0
  127. package/macros/mp_testjob.sas +92 -0
  128. package/macros/mp_testservice.sas +39 -0
  129. package/macros/mp_testwritespeedlibrary.sas +59 -0
  130. package/macros/mp_tree.sas +71 -0
  131. package/macros/mp_unzip.sas +90 -0
  132. package/macros/mp_updatevarlength.sas +93 -0
  133. package/macros/mp_validatecol.sas +97 -0
  134. package/macros/mp_wait4file.sas +35 -0
  135. package/macros/mp_webin.sas +59 -0
  136. package/macros/mp_zip.sas +85 -0
  137. package/package.json +5 -2
  138. package/scripts/copySASjsCore.ts +36 -0
  139. package/utils/index.d.ts +1 -2
  140. package/utils/index.js +2 -3
  141. package/utils/index.js.map +1 -1
  142. package/utils/utils.d.ts +1 -0
  143. package/utils/utils.js +7 -1
  144. package/utils/utils.js.map +1 -1
  145. package/utils/getNodeModulePath.d.ts +0 -2
  146. package/utils/getNodeModulePath.js +0 -83
  147. package/utils/getNodeModulePath.js.map +0 -1
@@ -0,0 +1,121 @@
1
+ /**
2
+ @file
3
+ @brief Asserts the number of observations in a dataset
4
+ @details Useful in the context of writing sasjs tests. The results of the
5
+ test are _appended_ to the &outds. table.
6
+
7
+ Example usage:
8
+
9
+ %mp_assertdsobs(sashelp.class) %* tests if any observations are present;
10
+
11
+ %mp_assertdsobs(sashelp.class,test=ATLEAST 10) %* pass if >9 obs present;
12
+
13
+ %mp_assertdsobs(sashelp.class,test=ATMOST 20) %* pass if <21 obs present;
14
+
15
+
16
+ @param [in] inds input dataset to test for presence of observations
17
+ @param [in] desc= (Testing observations) The user provided test description
18
+ @param [in] test= (HASOBS) The test to apply. Valid values are:
19
+ @li HASOBS - Test is a PASS if the input dataset has any observations
20
+ @li EMPTY - Test is a PASS if input dataset is empty
21
+ @li EQUALS [integer] - Test passes if row count matches the provided integer
22
+ @li ATLEAST [integer] - Test passes if row count is more than or equal to
23
+ the provided integer
24
+ @li ATMOST [integer] - Test passes if row count is less than or equal to
25
+ the provided integer
26
+ @param [out] outds= (work.test_results) The output dataset to contain the
27
+ results. If it does not exist, it will be created, with the following format:
28
+ |TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
29
+ |---|---|---|
30
+ |User Provided description|PASS|Dataset &inds has XX obs|
31
+
32
+ <h4> SAS Macros </h4>
33
+ @li mf_getuniquename.sas
34
+ @li mf_nobs.sas
35
+ @li mp_abort.sas
36
+
37
+ <h4> Related Macros </h4>
38
+ @li mp_assertcolvals.sas
39
+ @li mp_assert.sas
40
+ @li mp_assertcols.sas
41
+
42
+ @version 9.2
43
+ @author Allan Bowe
44
+
45
+ **/
46
+
47
+ %macro mp_assertdsobs(inds,
48
+ test=HASOBS,
49
+ desc=Testing observations,
50
+ outds=work.test_results
51
+ )/*/STORE SOURCE*/;
52
+
53
+ %local nobs ds;
54
+ %let nobs=%mf_nobs(&inds);
55
+ %let test=%upcase(&test);
56
+ %let ds=%mf_getuniquename(prefix=mp_assertdsobs);
57
+
58
+ %if %substr(&test.xxxxx,1,6)=EQUALS %then %do;
59
+ %let val=%scan(&test,2,%str( ));
60
+ %mp_abort(iftrue= (%DATATYP(&val)=CHAR)
61
+ ,mac=&sysmacroname
62
+ ,msg=%str(Invalid test - &test, expected EQUALS [integer])
63
+ )
64
+ %let test=EQUALS;
65
+ %end;
66
+ %else %if %substr(&test.xxxxxxx,1,7)=ATLEAST %then %do;
67
+ %let val=%scan(&test,2,%str( ));
68
+ %mp_abort(iftrue= (%DATATYP(&val)=CHAR)
69
+ ,mac=&sysmacroname
70
+ ,msg=%str(Invalid test - &test, expected ATLEAST [integer])
71
+ )
72
+ %let test=ATLEAST;
73
+ %end;
74
+ %else %if %substr(&test.xxxxxxx,1,7)=ATMOST %then %do;
75
+ %let val=%scan(&test,2,%str( ));
76
+ %mp_abort(iftrue= (%DATATYP(&val)=CHAR)
77
+ ,mac=&sysmacroname
78
+ ,msg=%str(Invalid test - &test, expected ATMOST [integer])
79
+ )
80
+ %let test=ATMOST;
81
+ %end;
82
+ %else %if &test ne HASOBS and &test ne EMPTY %then %do;
83
+ %mp_abort(
84
+ mac=&sysmacroname,
85
+ msg=%str(Invalid test - &test)
86
+ )
87
+ %end;
88
+
89
+ data &ds;
90
+ length test_description $256 test_result $4 test_comments $256;
91
+ test_description=symget('desc');
92
+ test_result='FAIL';
93
+ test_comments="&sysmacroname: Dataset &inds has &nobs observations.";
94
+ test_comments=test_comments!!" Test was "!!symget('test');
95
+ %if &test=HASOBS %then %do;
96
+ if &nobs>0 then test_result='PASS';
97
+ %end;
98
+ %else %if &test=EMPTY %then %do;
99
+ if &nobs=0 then test_result='PASS';
100
+ %end;
101
+ %else %if &test=EQUALS %then %do;
102
+ if &nobs=&val then test_result='PASS';
103
+ %end;
104
+ %else %if &test=ATLEAST %then %do;
105
+ if &nobs ge &val then test_result='PASS';
106
+ %end;
107
+ %else %if &test=ATMOST %then %do;
108
+ if &nobs le &val then test_result='PASS';
109
+ %end;
110
+ %else %do;
111
+ test_comments="&sysmacroname: Unsatisfied test condition - &test";
112
+ %end;
113
+ run;
114
+
115
+ proc append base=&outds data=&ds;
116
+ run;
117
+
118
+ proc sql;
119
+ drop table &ds;
120
+
121
+ %mend mp_assertdsobs;
@@ -0,0 +1,147 @@
1
+ /**
2
+ @file
3
+ @brief Used to capture scope leakage of macro variables
4
+ @details
5
+
6
+ A common 'difficult to detect' bug in macros is where a nested macro
7
+ over-writes variables in a higher level macro.
8
+
9
+ This assertion takes a snapshot of the macro variables before and after
10
+ a macro invocation. Differences are captured in the `&outds` table. This
11
+ makes it easy to detect whether any macro variables were modified or
12
+ changed.
13
+
14
+ The following variables are NOT tested (as they are known, global variables
15
+ used in SASjs):
16
+
17
+ @li &sasjs_prefix._FUNCTIONS
18
+
19
+ Global variables are initialised in mp_init.sas - which will also trigger
20
+ "strict mode" in your SAS session. Whilst this is a default in SASjs
21
+ produced apps, if you prefer not to use this mode, simply instantiate the
22
+ following variable to prevent the macro from running: `SASJS_PREFIX`
23
+
24
+ Example usage:
25
+
26
+ %mp_assertscope(SNAPSHOT)
27
+
28
+ %let oops=I did it again;
29
+
30
+ %mp_assertscope(COMPARE,
31
+ desc=Checking macro variables against previous snapshot
32
+ )
33
+
34
+ This macro is designed to work alongside `sasjs test` - for more information
35
+ about this facility, visit [cli.sasjs.io/test](https://cli.sasjs.io/test).
36
+
37
+ @param [in] action (SNAPSHOT) The action to take. Valid values:
38
+ @li SNAPSHOT - take a copy of the current macro variables
39
+ @li COMPARE - compare the current macro variables against previous values
40
+ @param [in] scope= (GLOBAL) The scope of the variables to be checked. This
41
+ corresponds to the values in the SCOPE column in `sashelp.vmacro`.
42
+ @param [in] desc= (Testing scope leakage) The user provided test description
43
+ @param [in] ignorelist= Provide a list of macro variable names to ignore from
44
+ the comparison
45
+ @param [in,out] scopeds= (work.mp_assertscope) The dataset to contain the
46
+ scope snapshot
47
+ @param [out] outds= (work.test_results) The output dataset to contain the
48
+ results. If it does not exist, it will be created, with the following format:
49
+ |TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
50
+ |---|---|---|
51
+ |User Provided description|PASS|No out of scope variables created or modified|
52
+
53
+ <h4> SAS Macros </h4>
54
+ @li mf_getquotedstr.sas
55
+ @li mp_init.sas
56
+
57
+ <h4> Related Macros </h4>
58
+ @li mp_assert.sas
59
+ @li mp_assertcols.sas
60
+ @li mp_assertcolvals.sas
61
+ @li mp_assertdsobs.sas
62
+ @li mp_assertscope.test.sas
63
+
64
+ @version 9.2
65
+ @author Allan Bowe
66
+
67
+ **/
68
+
69
+ %macro mp_assertscope(action,
70
+ desc=Testing Scope Leakage,
71
+ scope=GLOBAL,
72
+ scopeds=work.mp_assertscope,
73
+ ignorelist=,
74
+ outds=work.test_results
75
+ )/*/STORE SOURCE*/;
76
+ %local ds test_result test_comments del add mod ilist;
77
+ %let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE
78
+ SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist);
79
+
80
+ /**
81
+ * this sets up the global vars, it will also enter STRICT mode. If this
82
+ * behaviour is not desired, simply initiate the following global macro
83
+ * variable to prevent the macro from running: SASJS_PREFIX
84
+ */
85
+ %mp_init()
86
+
87
+ /* get current variables */
88
+ %if &action=SNAPSHOT %then %do;
89
+ proc sql;
90
+ create table &scopeds as
91
+ select name,offset,value
92
+ from dictionary.macros
93
+ where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
94
+ order by name,offset;
95
+ %end;
96
+ %else %if &action=COMPARE %then %do;
97
+
98
+ proc sql;
99
+ create table _data_ as
100
+ select name,offset,value
101
+ from dictionary.macros
102
+ where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
103
+ order by name,offset;
104
+
105
+ %let ds=&syslast;
106
+
107
+ proc compare
108
+ base=&scopeds(where=(upcase(name) not in (%mf_getquotedstr(&ilist))))
109
+ compare=&ds noprint;
110
+ run;
111
+
112
+ %if &sysinfo=0 %then %do;
113
+ %let test_result=PASS;
114
+ %let test_comments=&scope Variables Unmodified;
115
+ %end;
116
+ %else %do;
117
+ proc sql noprint undo_policy=none;
118
+ select distinct name into: del separated by ' ' from &scopeds
119
+ where name not in (select name from &ds);
120
+ select distinct name into: add separated by ' ' from &ds
121
+ where name not in (select name from &scopeds);
122
+ select distinct a.name into: mod separated by ' '
123
+ from &scopeds a
124
+ inner join &ds b
125
+ on a.name=b.name
126
+ and a.offset=b.offset
127
+ where a.value ne b.value;
128
+ %let test_result=FAIL;
129
+ %let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del));
130
+ %end;
131
+
132
+
133
+ data ;
134
+ length test_description $256 test_result $4 test_comments $256;
135
+ test_description=symget('desc');
136
+ test_comments=symget('test_comments');
137
+ test_result=symget('test_result');
138
+ run;
139
+
140
+ %let ds=&syslast;
141
+ proc append base=&outds data=&ds;
142
+ run;
143
+ proc sql;
144
+ drop table &ds;
145
+ %end;
146
+
147
+ %mend mp_assertscope;
@@ -0,0 +1,117 @@
1
+ /**
2
+ @file
3
+ @brief Convert a file to/from base64 format
4
+ @details Creates a new version of a file either encoded or decoded using
5
+ Base64. Inspired by this post by Michael Dixon:
6
+ https://support.selerity.com.au/hc/en-us/articles/223345708-Tip-SAS-and-Base64
7
+
8
+ Usage:
9
+
10
+ filename tmp temp;
11
+ data _null_;
12
+ file tmp;
13
+ put 'base ik ally';
14
+ run;
15
+ %mp_base64copy(inref=tmp, outref=myref, action=ENCODE)
16
+
17
+ data _null_;
18
+ infile myref;
19
+ input;
20
+ put _infile_;
21
+ run;
22
+
23
+ %mp_base64copy(inref=myref, outref=mynewref, action=DECODE)
24
+
25
+ data _null_;
26
+ infile mynewref;
27
+ input;
28
+ put _infile_;
29
+ run;
30
+
31
+ @param [in] inref= Fileref of the input file (should exist)
32
+ @param [out] outref= Output fileref. If it does not exist, it is created.
33
+ @param [in] action= (ENCODE) The action to take. Valid values:
34
+ @li ENCODE - Convert the file to base64 format
35
+ @li DECODE - Decode the file from base64 format
36
+
37
+ @version 9.2
38
+ @author Allan Bowe, source: https://github.com/sasjs/core
39
+
40
+ <h4> SAS Macros </h4>
41
+ @li mp_abort.sas
42
+
43
+
44
+ **/
45
+
46
+ %macro mp_base64copy(
47
+ inref=0,
48
+ outref=0,
49
+ action=ENCODE
50
+ )/*/STORE SOURCE*/;
51
+
52
+ %let inref=%upcase(&inref);
53
+ %let outref=%upcase(&outref);
54
+ %let action=%upcase(&action);
55
+ %local infound outfound;
56
+ %let infound=0;
57
+ %let outfound=0;
58
+ data _null_;
59
+ set sashelp.vextfl(where=(fileref="&inref" or fileref="&outref"));
60
+ if fileref="&inref" then call symputx('infound',1,'l');
61
+ if fileref="&outref" then call symputx('outfound',1,'l');
62
+ run;
63
+
64
+ %mp_abort(iftrue= (&infound=0)
65
+ ,mac=&sysmacroname
66
+ ,msg=%str(INREF &inref NOT FOUND!)
67
+ )
68
+ %mp_abort(iftrue= (&outref=0)
69
+ ,mac=&sysmacroname
70
+ ,msg=%str(OUTREF NOT PROVIDED!)
71
+ )
72
+ %mp_abort(iftrue= (&action ne ENCODE and &action ne DECODE)
73
+ ,mac=&sysmacroname
74
+ ,msg=%str(Invalid action! Should be ENCODE OR DECODE)
75
+ )
76
+
77
+ %if &outfound=0 %then %do;
78
+ filename &outref temp lrecl=2097088;
79
+ %end;
80
+
81
+ %if &action=ENCODE %then %do;
82
+ data _null_;
83
+ length b64 $ 76 line $ 57;
84
+ retain line "";
85
+ infile &inref recfm=F lrecl= 1 end=eof;
86
+ input @1 stream $char1.;
87
+ file &outref recfm=N;
88
+ substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream));
89
+ if mod(_N_,57)=0 or EOF then do;
90
+ if eof then b64=put(trim(line),$base64X76.);
91
+ else b64=put(line, $base64X76.);
92
+ put b64 + (-1) @;
93
+ line="";
94
+ end;
95
+ run;
96
+ %end;
97
+ %else %if &action=DECODE %then %do;
98
+ data _null_;
99
+ length filein 8 fileout 8;
100
+ filein = fopen("&inref",'I',4,'B');
101
+ fileout = fopen("&outref",'O',3,'B');
102
+ char= '20'x;
103
+ do while(fread(filein)=0);
104
+ length raw $4;
105
+ do i=1 to 4;
106
+ rc=fget(filein,char,1);
107
+ substr(raw,i,1)=char;
108
+ end;
109
+ rc = fput(fileout,input(raw,$base64X4.));
110
+ rc = fwrite(fileout);
111
+ end;
112
+ rc = fclose(filein);
113
+ rc = fclose(fileout);
114
+ run;
115
+ %end;
116
+
117
+ %mend mp_base64copy;
@@ -0,0 +1,79 @@
1
+ /**
2
+ @file
3
+ @brief Copy any file using binary input / output streams
4
+ @details Reads in a file byte by byte and writes it back out. Is an
5
+ os-independent method to copy files. In case of naming collision, the
6
+ default filerefs can be modified.
7
+ Note that if you have a new enough version of SAS, and you don't need features
8
+ such as APPEND, you may be better of using the fcopy() function instead.
9
+
10
+ %mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
11
+
12
+ To append to a file, use the mode option, eg:
13
+
14
+ filename tmp1 temp;
15
+ filename tmp2 temp;
16
+ data _null_;
17
+ file tmp1;
18
+ put 'stacking';
19
+ run;
20
+
21
+ %mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
22
+ %mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
23
+
24
+
25
+ @param [in] inloc quoted "path/and/filename.ext" of the file to be copied
26
+ @param [out] outloc quoted "path/and/filename.ext" of the file to be created
27
+ @param [in] inref (____in) If provided, this fileref will take precedence over
28
+ the `inloc` parameter
29
+ @param [out] outref (____in) If provided, this fileref will take precedence
30
+ over the `outloc` parameter. It must already exist!
31
+ @param [in] mode (CREATE) Valid values:
32
+ @li CREATE - Create the file (even if it already exists)
33
+ @li APPEND - Append to the file (don't overwrite)
34
+ @param iftrue= (1=1) Supply a condition for which the macro should be executed
35
+
36
+ @returns nothing
37
+
38
+ @version 9.2
39
+
40
+ **/
41
+
42
+ %macro mp_binarycopy(
43
+ inloc= /* full path and filename of the object to be copied */
44
+ ,outloc= /* full path and filename of object to be created */
45
+ ,inref=____in /* override default to use own filerefs */
46
+ ,outref=____out /* override default to use own filerefs */
47
+ ,mode=CREATE
48
+ ,iftrue=%str(1=1)
49
+ )/*/STORE SOURCE*/;
50
+ %local mod;
51
+
52
+ %if not(%eval(%unquote(&iftrue))) %then %return;
53
+
54
+ %if &mode=APPEND %then %let mod=mod;
55
+
56
+ /* these IN and OUT filerefs can point to anything */
57
+ %if &inref = ____in %then %do;
58
+ filename &inref &inloc lrecl=1048576 ;
59
+ %end;
60
+ %if &outref=____out %then %do;
61
+ filename &outref &outloc lrecl=1048576 &mod;
62
+ %end;
63
+
64
+ /* copy the file byte-for-byte */
65
+ data _null_;
66
+ infile &inref lrecl=1 recfm=n;
67
+ file &outref &mod recfm=n;
68
+ input sourcechar $char1. @@;
69
+ format sourcechar hex2.;
70
+ put sourcechar char1. @@;
71
+ run;
72
+
73
+ %if &inref = ____in %then %do;
74
+ filename &inref clear;
75
+ %end;
76
+ %if &outref=____out %then %do;
77
+ filename &outref clear;
78
+ %end;
79
+ %mend mp_binarycopy;
@@ -0,0 +1,194 @@
1
+ /**
2
+ @file
3
+ @brief Splits a file of ANY SIZE by reference to a search string.
4
+ @details Provide a fileref and a search string to chop off part of a file.
5
+
6
+ Works by reading in the file byte by byte, then marking the beginning and end
7
+ of each matched string, before finally doing the chop.
8
+
9
+ Choose whether to keep the FIRST or the LAST section of the file. Optionally,
10
+ use an OFFSET to fix the precise chop point.
11
+
12
+ Usage:
13
+
14
+ %let src="%sysfunc(pathname(work))/file.txt";
15
+ %let str=Chop here!;
16
+ %let out1="%sysfunc(pathname(work))/file1.txt";
17
+ %let out2="%sysfunc(pathname(work))/file2.txt";
18
+ %let out3="%sysfunc(pathname(work))/file3.txt";
19
+ %let out4="%sysfunc(pathname(work))/file4.txt";
20
+
21
+ data _null_;
22
+ file &src;
23
+ put "startsection&str.endsection";
24
+ run;
25
+
26
+ %mp_chop(&src, matchvar=str, keep=FIRST, outfile=&out1)
27
+ %mp_chop(&src, matchvar=str, keep=LAST, outfile=&out2)
28
+ %mp_chop(&src, matchvar=str, keep=FIRST, matchpoint=END, outfile=&out3)
29
+ %mp_chop(&src, matchvar=str, keep=LAST, matchpoint=END, outfile=&out4)
30
+
31
+ filename results (&out1 &out2 &out3 &out4);
32
+ data _null_;
33
+ infile results;
34
+ input;
35
+ list;
36
+ run;
37
+
38
+ Results:
39
+ @li `startsection`
40
+ @li `Chop here!endsection`
41
+ @li `startsectionChop here!`
42
+ @li `endsection`
43
+
44
+ For more examples, see mp_chop.test.sas
45
+
46
+ @param [in] infile The QUOTED path to the file on which to perform the chop
47
+ @param [in] matchvar= Macro variable NAME containing the string to split by
48
+ @param [in] matchpoint= (START) Valid values:
49
+ @li START - chop at the beginning of the string in `matchvar`.
50
+ @li END - chop at the end of the string in `matchvar`.
51
+ @param [in] offset= (0) An adjustment to the precise chop location, by
52
+ by reference to the `matchpoint`. Should be a positive or negative integer.
53
+ @param [in] keep= (FIRST) Valid values:
54
+ @li FIRST - keep the section of the file before the chop
55
+ @li LAST - keep the section of the file after the chop
56
+ @param [in] mdebug= (0) Set to 1 to provide macro debugging
57
+ @param outfile= (0) Optional QUOTED path to the adjusted output file (avoids
58
+ overwriting the first file).
59
+
60
+ <h4> SAS Macros </h4>
61
+ @li mf_getuniquefileref.sas
62
+ @li mf_getuniquename.sas
63
+
64
+ <h4> Related Macros </h4>
65
+ @li mp_abort.sas
66
+ @li mp_gsubfile.sas
67
+ @li mp_replace.sas
68
+ @li mp_chop.test.sas
69
+
70
+ @version 9.4
71
+ @author Allan Bowe
72
+
73
+ **/
74
+
75
+ %macro mp_chop(infile,
76
+ matchvar=,
77
+ matchpoint=START,
78
+ keep=FIRST,
79
+ offset=0,
80
+ mdebug=0,
81
+ outfile=0
82
+ )/*/STORE SOURCE*/;
83
+
84
+ %local fref0 dttm ds1 outref;
85
+ %let fref0=%mf_getuniquefileref();
86
+ %let ds1=%mf_getuniquename(prefix=allchars);
87
+ %let ds2=%mf_getuniquename(prefix=startmark);
88
+
89
+ %if &outfile=0 %then %let outfile=&infile;
90
+
91
+ %mp_abort(iftrue= (%length(%superq(&matchvar))=0)
92
+ ,mac=mp_chop.sas
93
+ ,msg=%str(&matchvar is an empty variable)
94
+ )
95
+
96
+ /* START */
97
+ %let dttm=%sysfunc(datetime());
98
+
99
+ filename &fref0 &infile lrecl=1 recfm=n;
100
+
101
+ /* create dataset with one char per row */
102
+ data &ds1;
103
+ infile &fref0;
104
+ input sourcechar $char1. @@;
105
+ format sourcechar hex2.;
106
+ run;
107
+
108
+ /* get start & stop position of first matchvar string (one row, two vars) */
109
+ data &ds2;
110
+ /* set find string to length in bytes to cover trailing spaces */
111
+ length string $ %length(%superq(&matchvar));
112
+ string =symget("&matchvar");
113
+ drop string;
114
+
115
+ firstchar=char(string,1);
116
+ findlen=lengthm(string); /* <- for trailing bytes */
117
+
118
+ do _N_=1 to nobs;
119
+ set &ds1 nobs=nobs point=_N_;
120
+ if sourcechar=firstchar then do;
121
+ pos=1;
122
+ s=0;
123
+ do point=_N_ to min(_N_ + findlen -1,nobs);
124
+ set &ds1 point=point;
125
+ if sourcechar=char(string, pos) then s + 1;
126
+ else goto _leave_;
127
+ pos+1;
128
+ end;
129
+ _leave_:
130
+ if s=findlen then do;
131
+ START =_N_;
132
+ _N_ =_N_+ s - 1;
133
+ STOP =_N_;
134
+ output;
135
+ /* matched! */
136
+ stop;
137
+ end;
138
+ end;
139
+ end;
140
+ stop;
141
+ keep START STOP;
142
+ run;
143
+
144
+ %local split;
145
+ %let split=0;
146
+ data _null_;
147
+ set &ds2;
148
+ if "&matchpoint"='START' then do;
149
+ if "&keep"='FIRST' then mp=start;
150
+ else if "&keep"='LAST' then mp=start-1;
151
+ end;
152
+ else if "&matchpoint"='END' then do;
153
+ if "&keep"='FIRST' then mp=stop+1;
154
+ else if "&keep"='LAST' then mp=stop;
155
+ end;
156
+ split=mp+&offset;
157
+ call symputx('split',split,'l');
158
+ %if &mdebug=1 %then %do;
159
+ put (_all_)(=);
160
+ %put &=offset;
161
+ %end;
162
+ run;
163
+ %if &split=0 %then %do;
164
+ %put &sysmacroname: No match found in &infile for string %superq(&matchvar);
165
+ %return;
166
+ %end;
167
+
168
+ data _null_;
169
+ file &outfile recfm=n;
170
+ set &ds1;
171
+ %if &keep=FIRST %then %do;
172
+ if _n_ ge &split then stop;
173
+ %end;
174
+ %else %do;
175
+ if _n_ gt &split;
176
+ %end;
177
+ put sourcechar char1.;
178
+ run;
179
+
180
+ %if &mdebug=0 %then %do;
181
+ filename &fref0 clear;
182
+ %end;
183
+ %else %do;
184
+ data _null_;
185
+ infile &outfile lrecl=32767;
186
+ input;
187
+ list;
188
+ if _n_>50 then stop;
189
+ run;
190
+ %end;
191
+ /* END */
192
+ %put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
193
+
194
+ %mend mp_chop;