mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 06:24:35 +00:00
Compare commits
1270 Commits
v2.8.0
...
db15c66e68
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db15c66e68 | ||
|
|
62796ab6e6 | ||
|
|
7eca3b5e07 | ||
|
|
66ceb738c8 | ||
|
|
9d37856fc2 | ||
|
|
14987e3914 | ||
|
|
10857b2153 | ||
|
|
ac2a054c84 | ||
|
|
f60b298bbb | ||
|
|
bf6beded5f | ||
|
|
f98d401bcd | ||
|
|
b808c69e93 | ||
|
|
7c2b7dca1f | ||
|
|
cb94a94a21 | ||
|
|
c23262198b | ||
|
|
f2b0988b42 | ||
|
|
b3178a87ee | ||
|
|
cc57907c0c | ||
|
|
7b24faaa21 | ||
|
|
a3c0ba92cc | ||
|
|
c15e7db1c6 | ||
|
|
3faf4cf325 | ||
|
|
b49ac96766 | ||
|
|
b3298143c7 | ||
|
|
366b6e7fa4 | ||
|
|
9bf2870357 | ||
|
|
f71681c352 | ||
|
|
6008db999c | ||
|
|
b6f020e897 | ||
|
|
9d0533fe3b | ||
|
|
7dd2597041 | ||
|
|
426c0bf9f2 | ||
|
|
1cd8ba03c5 | ||
|
|
569533b218 | ||
|
|
14aeb585ae | ||
|
|
7dd219e9f1 | ||
|
|
cdd2b88b09 | ||
|
|
7e4fb4a640 | ||
|
|
a428b4f66c | ||
|
|
e2f0577e78 | ||
|
|
d53eff7771 | ||
|
|
5b56c85455 | ||
|
|
ff519c7f39 | ||
|
|
7d7778fd36 | ||
|
|
b47f31cfe6 | ||
|
|
542039b425 | ||
|
|
cc908a82bc | ||
|
|
71c31046f4 | ||
|
|
33a487b2b4 | ||
|
|
7240cf08d6 | ||
|
|
1cb702149c | ||
|
|
a12ea6a7cb | ||
|
|
a6b52b5d9e | ||
|
|
0faba3581b | ||
|
|
749309b749 | ||
|
|
e54de44d4b | ||
|
|
40436be14f | ||
|
|
909fef7143 | ||
|
|
bcb93e62d4 | ||
|
|
6dbfd32dba | ||
|
|
5706483886 | ||
|
|
ce73e2bebd | ||
|
|
bc77e5a5d1 | ||
|
|
daa4e4e762 | ||
|
|
9c1f68944f | ||
|
|
3978ac5e05 | ||
|
|
6b378749e5 | ||
|
|
77c0e35c9d | ||
|
|
52d33ccafb | ||
|
|
10087dd6a6 | ||
|
|
3bd2148ae9 | ||
|
|
60570b2e13 | ||
|
|
6e033afb7b | ||
|
|
4f5fa414e1 | ||
|
|
4b142f1f45 | ||
|
|
3f73a565a6 | ||
|
|
d3f1c8e960 | ||
|
|
0d10441b89 | ||
|
|
85e1f56400 | ||
|
|
c5ec21c7a0 | ||
|
|
e049ab99a7 | ||
|
|
e9deab3885 | ||
|
|
8d2f084316 | ||
|
|
33cb36fb8f | ||
|
|
752e6305c9 | ||
|
|
d24f0f3e25 | ||
|
|
6c6561deba | ||
|
|
a1c24b2e4a | ||
|
|
d4f1df5bc0 | ||
|
|
2ddded9600 | ||
|
|
2de6fba5bb | ||
|
|
7117e2e8e9 | ||
|
|
376800d464 | ||
|
|
de32d12b51 | ||
|
|
285ca791d8 | ||
|
|
667198e5c0 | ||
|
|
51042cbd47 | ||
|
|
15c0576207 | ||
|
|
f542ddec99 | ||
|
|
8f147d3e01 | ||
|
|
9f7f27507c | ||
|
|
bfa48ef172 | ||
|
|
47a265a706 | ||
|
|
5fce4c8a83 | ||
|
|
57131e5fa6 | ||
|
|
3d5e3895ab | ||
|
|
c5cc6e1a87 | ||
|
|
c05993ca6b | ||
|
|
e2d2de6f76 | ||
|
|
61cce649cb | ||
|
|
9f85b3e1b2 | ||
|
|
37475e227d | ||
|
|
cbeb954d37 | ||
|
|
a9ae874a45 | ||
| 77038b48c2 | |||
| e848690984 | |||
|
|
2ad8f0b44b | ||
| 6b41386667 | |||
| c363cfe458 | |||
|
|
608dbd1085 | ||
| c6d9e6fdb2 | |||
|
|
c7d46416ce | ||
|
|
86606c6f18 | ||
|
|
9730715558 | ||
|
|
eff0f4eda3 | ||
|
|
f60b06844c | ||
|
|
85ef2ecb84 | ||
|
|
6b470e76fb | ||
|
|
46ca83a4d5 | ||
|
|
2bb1df86ec | ||
|
|
b1bff1b0a4 | ||
|
|
5c3ac8a123 | ||
|
|
2765d8c2ec | ||
|
|
bd4610f0b8 | ||
|
|
ae92e14660 | ||
|
|
424ae548d0 | ||
|
|
69fe9ebaed | ||
|
|
1b16383fd8 | ||
|
|
08f291367d | ||
|
|
f1c761d5c1 | ||
|
|
f88b219da1 | ||
|
|
900120df1b | ||
|
|
2a13ba72f6 | ||
|
|
21d6671a5f | ||
|
|
5367126428 | ||
|
|
8485d9ebdf | ||
|
|
b7718fae6b | ||
|
|
6e3b100170 | ||
|
|
414fe9ebde | ||
|
|
2bdd83b2e5 | ||
|
|
862b1896fe | ||
|
|
22f0cb67a5 | ||
|
|
e6da373853 | ||
|
|
ed20bcaa5c | ||
|
|
96e8b096c5 | ||
|
|
7413266a8e | ||
|
|
cf70c33bde | ||
|
|
934629d46d | ||
|
|
16a3b63161 | ||
|
|
d7288b7fa1 | ||
|
|
015749a9b2 | ||
|
|
556c7bdb28 | ||
|
|
602758c3c3 | ||
|
|
a244a0b27b | ||
|
|
3bb632d60d | ||
|
|
bdd348483c | ||
|
|
92f575551d | ||
|
|
e616bc940f | ||
|
|
b7bca48129 | ||
|
|
6a2dcbb23f | ||
|
|
6da578e336 | ||
|
|
c874b31b63 | ||
|
|
532e0d535a | ||
|
|
ee5688f97f | ||
|
|
359b007f85 | ||
|
|
3294767c1b | ||
|
|
9d6f87c87a | ||
|
|
ec14b9cef8 | ||
|
|
94af8661b0 | ||
|
|
c9e431142c | ||
|
|
2b2aa5eb58 | ||
|
|
1ac2b480a6 | ||
|
|
4e53544b66 | ||
|
|
9b5f1cf170 | ||
|
|
703fe4ef38 | ||
|
|
f4a4263046 | ||
|
|
02bf9c85db | ||
|
|
5835cfaa83 | ||
|
|
b50521a8de | ||
|
|
fccd6fcc44 | ||
|
|
487ff5faa9 | ||
|
|
5efc20eacc | ||
|
|
cbd62fbfab | ||
|
|
2808145302 | ||
|
|
815e5f3e0e | ||
|
|
843d6e5c2d | ||
|
|
b084f4e84b | ||
|
|
5b5116070e | ||
|
|
a2002db838 | ||
|
|
dc6bcdd69e | ||
|
|
c97dc9a16d | ||
|
|
ef669db622 | ||
|
|
26499d2058 | ||
|
|
17e5d0f0e0 | ||
|
|
fc9205e355 | ||
|
|
ce344fc8e2 | ||
|
|
40239c53d8 | ||
|
|
814ecec94f | ||
|
|
934c501fec | ||
|
|
091b2e28be | ||
|
|
d8ea29bf8c | ||
|
|
993dec4610 | ||
|
|
f905387d66 | ||
|
|
7512423b04 | ||
|
|
50e6d416a4 | ||
|
|
18b6cadce6 | ||
|
|
413743bbe6 | ||
|
|
fcafb1026e | ||
|
|
b8f24264d4 | ||
|
|
5eb87a754e | ||
|
|
3a5fd4bfc5 | ||
|
|
b7ae9a2737 | ||
|
|
4057ac4b2e | ||
|
|
fa0a6ab22d | ||
|
|
2ae7a60be5 | ||
|
|
0a24f3ff7b | ||
|
|
592f477063 | ||
|
|
a91db81894 | ||
|
|
236e7cc4c0 | ||
|
|
2b6882cb9c | ||
|
|
2a3071708a | ||
|
|
3890aefccf | ||
|
|
f378a5637f | ||
|
|
121c692e09 | ||
|
|
759d6bd144 | ||
|
|
76d248f302 | ||
|
|
f30e30c024 | ||
|
|
30637b5025 | ||
|
|
9f16d090f5 | ||
|
|
74143bdf29 | ||
|
|
1c4c9793f6 | ||
|
|
d42fd4ebac | ||
|
|
d39b1be7a8 | ||
|
|
65d4c7969d | ||
|
|
fa152cb375 | ||
|
|
91a2d9039b | ||
|
|
73f919ffe7 | ||
|
|
68c11334df | ||
|
|
3bb83be0c5 | ||
|
|
76a20838ec | ||
|
|
9eec2e4920 | ||
|
|
bd18d4c32d | ||
|
|
d7763e276f | ||
|
|
7dadcf20f4 | ||
|
|
a497976eae | ||
|
|
6c64de651d | ||
|
|
48c17beb20 | ||
|
|
c46bb92c39 | ||
|
|
1e894bae98 | ||
|
|
461cda45ee | ||
|
|
7b6d34028b | ||
|
|
cb05ee2b9a | ||
|
|
e41b91f495 | ||
|
|
d21958cf0b | ||
|
|
c4b445db77 | ||
|
|
ebe764a7c0 | ||
|
|
7bba51a60e | ||
|
|
bce810caa0 | ||
|
|
222161d589 | ||
|
|
70cac82d78 | ||
|
|
6e0b8ae13b | ||
|
|
0dc4bbab62 | ||
|
|
da5244cda9 | ||
|
|
724de80d0f | ||
|
|
8de2dd4e7c | ||
|
|
e46165c140 | ||
|
|
a9185a2bf2 | ||
|
|
f0b77dfc6a | ||
|
|
91c4b87496 | ||
|
|
111d0dffc3 | ||
|
|
4f481ec8b4 | ||
|
|
b8cec22a88 | ||
|
|
6b1accdd6b | ||
|
|
949b406c23 | ||
|
|
fc90a7f928 | ||
|
|
d0bd88907f | ||
| 4e21772207 | |||
|
|
39f700bed2 | ||
|
|
dcb7958950 | ||
|
|
146610b5a7 | ||
|
|
9887efcf60 | ||
|
|
a2c7bdafb4 | ||
|
|
c58b5c7a52 | ||
|
|
22c7e5b4dd | ||
|
|
244171f8c4 | ||
|
|
fd765e2d68 | ||
| 840cb5ef44 | |||
| 918ce96fce | |||
| f1712c34e8 | |||
| 11ec20b472 | |||
| f42f111462 | |||
| 907725c5ba | |||
| 95b78b91e1 | |||
| e4771b9c14 | |||
| ba8190883e | |||
|
|
32dd057e83 | ||
|
|
7471bd42a4 | ||
|
|
702a4ecd3a | ||
|
|
5da97295ff | ||
|
|
dc556bdef0 | ||
|
|
111731bf35 | ||
|
|
2c526cf9dd | ||
|
|
660e02193f | ||
|
|
00b4dee86e | ||
|
|
3913825c22 | ||
|
|
0f143d603b | ||
|
|
f1d5fa2c0a | ||
|
|
a88689428f | ||
|
|
8843fa8bfc | ||
|
|
22d046cf5c | ||
|
|
29e3eb34aa | ||
|
|
1af52a6683 | ||
|
|
fc0c96dd94 | ||
|
|
b9c4882553 | ||
|
|
011b2b185c | ||
|
|
dbc23550ac | ||
|
|
8910840ccc | ||
|
|
4ef571032d | ||
|
|
e01cd8cd16 | ||
|
|
00628ec78a | ||
|
|
f4e6a487f3 | ||
|
|
b7afecdf81 | ||
|
|
19eb348f0e | ||
|
|
f420ac2abf | ||
|
|
7edec1ad8a | ||
|
|
62d7bce249 | ||
|
|
fe6c9a793b | ||
|
|
8e13943356 | ||
|
|
04df9600e0 | ||
|
|
e2b0aabfa4 | ||
|
|
c52a623630 | ||
|
|
cf348e8016 | ||
|
|
6502fc4982 | ||
|
|
ef574f6319 | ||
|
|
5b251006cd | ||
|
|
b353acec47 | ||
|
|
8b148c3916 | ||
|
|
2efdcec54c | ||
|
|
f832e93f4b | ||
|
|
f37c2e5867 | ||
|
|
6f8ec5d5a8 | ||
|
|
6521ade608 | ||
|
|
2666bbc85e | ||
|
|
ee35f47f4f | ||
|
|
7f867e2a5c | ||
|
|
c6af6ce578 | ||
|
|
a1aac785c0 | ||
|
|
dbe8b0b1c3 | ||
|
|
2ee9a4cee4 | ||
|
|
3a7afdffb7 | ||
|
|
c78211aa1c | ||
|
|
76c49e96f2 | ||
|
|
984ea44f5d | ||
|
|
88f1222abd | ||
|
|
d88f028ee3 | ||
|
|
07d7c9df4b | ||
|
|
6765a1d025 | ||
|
|
952f28a872 | ||
|
|
8246b5a42c | ||
|
|
72123aeeb7 | ||
|
|
236d1ae25f | ||
|
|
b75369b28d | ||
|
|
63871db170 | ||
|
|
6456c2f6e2 | ||
|
|
36faa194a8 | ||
|
|
093dc87aad | ||
|
|
ca045e3ebf | ||
|
|
be5e2f371d | ||
|
|
6d15465bac | ||
|
|
2031a5b0c0 | ||
|
|
7b3844a391 | ||
|
|
202de36042 | ||
|
|
62837b512b | ||
|
|
5d5a99fd77 | ||
|
|
1b5effd584 | ||
|
|
1613ab2c9e | ||
|
|
a2df4e35be | ||
|
|
aabbcfdf6b | ||
|
|
7b7759e1ce | ||
|
|
e5a3053600 | ||
|
|
9856d0ef58 | ||
|
|
77b37e5503 | ||
|
|
793319fe38 | ||
|
|
594a895ddd | ||
|
|
0d59266b8d | ||
|
|
4863aafaa8 | ||
|
|
6015320145 | ||
|
|
8c09c0bce0 | ||
|
|
437943b779 | ||
|
|
6a090e45b6 | ||
|
|
a7dc314204 | ||
|
|
37076eae89 | ||
|
|
9a9f8dc847 | ||
|
|
719b657267 | ||
|
|
671a615501 | ||
|
|
884b45bf12 | ||
|
|
ff6ae1b066 | ||
|
|
d581fec55e | ||
|
|
a5613a79bb | ||
|
|
c6703e16e8 | ||
|
|
6587dce95b | ||
|
|
b60e6448b9 | ||
|
|
46d9b58b32 | ||
|
|
349cbabc94 | ||
|
|
9de056a3fc | ||
|
|
ad497b322f | ||
|
|
7a6408ee44 | ||
|
|
336743f2b4 | ||
|
|
6e32eb3bd6 | ||
|
|
b377b83442 | ||
|
|
899b94bb6e | ||
|
|
d97efdff61 | ||
|
|
1097afbcb8 | ||
|
|
165b2d3568 | ||
|
|
44a80c8985 | ||
|
|
6e32d9b743 | ||
|
|
6b167e7a4c | ||
|
|
011672b1ed | ||
|
|
a7eb926810 | ||
|
|
cad7f13a0e | ||
|
|
65fcea817a | ||
|
|
22fade13e7 | ||
|
|
7146310072 | ||
|
|
b7de1c25ec | ||
|
|
f4c7f47ffe | ||
|
|
cdf339d077 | ||
|
|
31702df19b | ||
|
|
cf0d1c0473 | ||
|
|
1f369f9848 | ||
|
|
2372ff5f4f | ||
|
|
6d0e34ba1d | ||
|
|
7a69698178 | ||
|
|
532bf84e06 | ||
|
|
e1afbc02c4 | ||
|
|
756f00d88d | ||
|
|
b72e404d52 | ||
|
|
e31cdeef42 | ||
|
|
8a4e32cc27 | ||
|
|
f285505b79 | ||
|
|
67f5c50300 | ||
|
|
ce39e4f779 | ||
|
|
9c80f5664c | ||
|
|
83466c001b | ||
|
|
ad315be503 | ||
|
|
c41ae2dcc8 | ||
| d9f8e92fac | |||
|
|
d42ede15db | ||
|
|
08ea9f7c00 | ||
|
|
c327e1fc0d | ||
|
|
02fddcf9a1 | ||
|
|
4752bfbb05 | ||
|
|
767ddd7add | ||
|
|
54a24ced83 | ||
|
|
57ae2981f1 | ||
|
|
a3043ac685 | ||
|
|
2bdb90b0be | ||
|
|
2cd846d504 | ||
|
|
f593c7bec9 | ||
|
|
c8805db0b5 | ||
|
|
1eb6d8cec9 | ||
|
|
ed19ee03af | ||
|
|
a1c931b5e6 | ||
|
|
cb553a31ab | ||
|
|
557df272ff | ||
|
|
0cb3c96c15 | ||
|
|
1cb39d4d61 | ||
|
|
934b7d4f8a | ||
|
|
24c50cde56 | ||
|
|
055e8d2f13 | ||
|
|
abfe7fe339 | ||
|
|
16ed91f6a9 | ||
|
|
67ba2a5286 | ||
|
|
3d7f9b71e1 | ||
|
|
1d972fad11 | ||
|
|
e23bc461c4 | ||
|
|
28ed458b83 | ||
|
|
827210e010 | ||
|
|
de2f32da36 | ||
|
|
6fa0fc5dc6 | ||
|
|
73e3d9d419 | ||
|
|
5f2229e3d5 | ||
|
|
d19c4a517c | ||
|
|
c47480f60c | ||
|
|
295211bb72 | ||
|
|
818bc3cc2b | ||
|
|
bb6111e2b3 | ||
|
|
512f05c0b2 | ||
|
|
500fb8124f | ||
|
|
88ddba2a4b | ||
|
|
86f6d06b85 | ||
|
|
1cefc0e7ee | ||
|
|
412182a022 | ||
|
|
43b8ee1c7e | ||
|
|
83eea02240 | ||
|
|
a14e31804a | ||
|
|
3fa639ebf7 | ||
|
|
ed11d44fe8 | ||
|
|
de4ea8888f | ||
|
|
ea0a936871 | ||
|
|
042987c91e | ||
|
|
6669e74baa | ||
|
|
906f9a139d | ||
|
|
b31f960635 | ||
|
|
1ed3cb31b5 | ||
|
|
ca7c332f20 | ||
|
|
d587b44b34 | ||
|
|
e43aac972a | ||
|
|
7dbe31b5d3 | ||
|
|
1672c96340 | ||
|
|
453aee2c1f | ||
|
|
00abbdcd65 | ||
|
|
88685dc585 | ||
|
|
cf8147d6ca | ||
|
|
f28f6b1530 | ||
|
|
cb4ea71e81 | ||
|
|
fe94d3781a | ||
|
|
7c17b39dad | ||
|
|
73dab4c651 | ||
|
|
5d72843167 | ||
|
|
f43df47cff | ||
|
|
aaca26770b | ||
|
|
4a124d5bd8 | ||
|
|
03cd52a01a | ||
|
|
da79181b00 | ||
|
|
a405104052 | ||
|
|
56fdaa65d2 | ||
|
|
9d60c49c9f | ||
|
|
380170d5ba | ||
|
|
4b450f2091 | ||
|
|
1b013fbf1c | ||
|
|
bf7459bd2d | ||
|
|
1096db0846 | ||
|
|
fc9b765246 | ||
|
|
4a8f7bb014 | ||
|
|
e0469be0d8 | ||
|
|
e9e576b5ec | ||
|
|
1a32d114f1 | ||
|
|
94e83f6b8d | ||
|
|
35a6dede6f | ||
|
|
039ec397dd | ||
|
|
dce4630eb8 | ||
|
|
1e142f042b | ||
|
|
7caca2f139 | ||
|
|
61556b2de8 | ||
|
|
9e12409389 | ||
|
|
c8050f5a79 | ||
|
|
cb4f71c7cd | ||
|
|
a39f4e4eee | ||
|
|
b525b4171d | ||
|
|
f2d80b3b63 | ||
|
|
96dda87f37 | ||
|
|
3435509eec | ||
|
|
42f2767129 | ||
|
|
099a5f7840 | ||
|
|
c83ea705a2 | ||
|
|
9ea6c875f2 | ||
|
|
0728f72c4f | ||
|
|
a90a6f00cf | ||
|
|
f71e53af8d | ||
|
|
cc1b971e19 | ||
|
|
8484c752ed | ||
|
|
8bd31e6c97 | ||
|
|
f9b0f87f44 | ||
|
|
d7e9f10291 | ||
|
|
3edc3587b3 | ||
|
|
63bf00e28f | ||
|
|
6b2574947a | ||
|
|
eb9027ecb6 | ||
|
|
2ad931a566 | ||
|
|
cebe119304 | ||
|
|
bd3082d7e3 | ||
|
|
11da53f1cb | ||
|
|
c4cb0b2395 | ||
|
|
58614e9a3d | ||
|
|
c94c334c4b | ||
|
|
3bf44405f8 | ||
|
|
db68a256cb | ||
|
|
611fac6338 | ||
|
|
ddd120bb75 | ||
|
|
0d75e0bad8 | ||
|
|
ba8c4ac844 | ||
|
|
6938a42896 | ||
|
|
efff77c94e | ||
|
|
2b10cf6192 | ||
|
|
134b91f266 | ||
|
|
969f551e10 | ||
|
|
26623ba085 | ||
|
|
8eb495890d | ||
|
|
c1a30977f1 | ||
|
|
9a6be61651 | ||
|
|
388839039e | ||
|
|
e760a89a6a | ||
|
|
d2e30267e8 | ||
|
|
190dbddfe3 | ||
|
|
05e769794e | ||
|
|
558ebaf6f2 | ||
|
|
970b56fe5a | ||
|
|
c2597bd07b | ||
|
|
c4baca477b | ||
|
|
7726b0e0b0 | ||
|
|
0a536245f3 | ||
|
|
edfa9ecc07 | ||
|
|
f4982c85ca | ||
|
|
3ce771d587 | ||
|
|
72d6b446c3 | ||
|
|
40d694eec8 | ||
|
|
6af1423666 | ||
|
|
23a01347f1 | ||
|
|
7c86d6163a | ||
|
|
d7233208f1 | ||
|
|
7f587ba720 | ||
|
|
21ecc1b675 | ||
|
|
6b13dc2b87 | ||
|
|
bb89184212 | ||
|
|
56338caaca | ||
|
|
d7e2ff8ac9 | ||
|
|
582ec0a1f9 | ||
|
|
53785f5644 | ||
|
|
a8acadb8f1 | ||
|
|
23dbda302e | ||
|
|
7e7ab4275d | ||
|
|
a455a3d98d | ||
|
|
588d987c25 | ||
|
|
8ffd06343a | ||
|
|
76207c443c | ||
|
|
7e9e0fac07 | ||
|
|
1fdbc7cce9 | ||
|
|
312369b200 | ||
|
|
c030174bfb | ||
|
|
faf466e79a | ||
|
|
856ffc1b72 | ||
|
|
c0924af06b | ||
|
|
33cec61a13 | ||
|
|
854ff696d8 | ||
|
|
cc3435d13d | ||
|
|
5ceaac195d | ||
|
|
5d5df977a6 | ||
|
|
245e85ef36 | ||
|
|
b96df6f14f | ||
|
|
1932c1e138 | ||
|
|
f7ee012be3 | ||
|
|
b49e11bc79 | ||
|
|
f709a11dfb | ||
|
|
17ed2240d3 | ||
|
|
a8b5107b1a | ||
|
|
735bab5d26 | ||
|
|
86f7876f50 | ||
|
|
46c96bc7ec | ||
|
|
cba3f5972b | ||
|
|
ed48c49964 | ||
|
|
203ff3f80d | ||
|
|
cfe90a8d0d | ||
|
|
0749ea0819 | ||
|
|
e09a39e748 | ||
|
|
20dcefaefd | ||
|
|
4c8347516a | ||
|
|
e497d226a0 | ||
|
|
ccf8f1acc0 | ||
|
|
fe9a2ed979 | ||
|
|
078815e83e | ||
|
|
bb80c7af5a | ||
|
|
842662aae1 | ||
|
|
876fac2332 | ||
|
|
427deca350 | ||
|
|
07bde4b25c | ||
|
|
80b06af581 | ||
|
|
3c026811e9 | ||
|
|
cf547ce7e4 | ||
|
|
6952c79899 | ||
|
|
09e3f63da7 | ||
|
|
d6956f4122 | ||
|
|
6fca73e7da | ||
|
|
880df4138c | ||
|
|
badf5b5761 | ||
|
|
b174aa25b3 | ||
|
|
bc6eac6977 | ||
|
|
2d4d595e5d | ||
|
|
7111fe14fb | ||
|
|
8499e38c55 | ||
|
|
682d80b1b8 | ||
|
|
4fe6f233f2 | ||
|
|
6ba3588eff | ||
|
|
53aa403630 | ||
|
|
cba9255732 | ||
|
|
a7b78c73c4 | ||
|
|
85e0b6a4a9 | ||
|
|
3c7e762eeb | ||
|
|
9a1f7d0985 | ||
|
|
dfd60200fb | ||
|
|
713f7544cd | ||
|
|
de4e96ab01 | ||
|
|
3e7b15c7db | ||
|
|
eb2ccfbbca | ||
|
|
70e508e583 | ||
|
|
8b0acf2eae | ||
|
|
d254870439 | ||
|
|
303225cb85 | ||
|
|
90de167643 | ||
|
|
8ee997de8e | ||
|
|
e27f6ac716 | ||
|
|
ec4de95fcf | ||
|
|
df0fa95519 | ||
|
|
2fe7fba79b | ||
|
|
e40234ee29 | ||
|
|
a287cc27a7 | ||
|
|
921186eb74 | ||
|
|
6fd215ceff | ||
|
|
0297509aa0 | ||
|
|
c5a8bc745d | ||
|
|
36aa466561 | ||
|
|
009485e5b9 | ||
|
|
eb01c8772d | ||
|
|
e3fb69928c | ||
|
|
65afa14466 | ||
|
|
0176b19616 | ||
|
|
9f3dfd9a59 | ||
|
|
513ea354ab | ||
|
|
7b686e11c9 | ||
|
|
3997000266 | ||
|
|
6e177d4cae | ||
|
|
3554991ff8 | ||
|
|
58d2d6382a | ||
|
|
67f28a366c | ||
|
|
64f53acce2 | ||
|
|
2e790f69a1 | ||
|
|
e62011d97e | ||
|
|
cd8d16d09f | ||
|
|
9c61965d4b | ||
|
|
61b8cb5dea | ||
|
|
899f6d9558 | ||
|
|
899de27617 | ||
|
|
322c488e72 | ||
|
|
5d5e66a1c5 | ||
|
|
5f4e9d541d | ||
|
|
306ea93be2 | ||
|
|
3fd83a3160 | ||
|
|
56c1397547 | ||
|
|
90adf8dcdd | ||
|
|
6e0fe0ff25 | ||
|
|
794ceec33c | ||
|
|
11d073c10a | ||
|
|
c160b5058b | ||
|
|
2f49738cf9 | ||
|
|
bfe4b1ec8b | ||
|
|
6224844915 | ||
|
|
81a17bc0c2 | ||
|
|
f4c2be7411 | ||
|
|
16489a9494 | ||
|
|
0e03b06a4b | ||
|
|
c3b89c7f7d | ||
|
|
142b46570d | ||
|
|
f7fac50108 | ||
|
|
f7078957cf | ||
|
|
f258d4f2f1 | ||
|
|
ae5fbcf857 | ||
|
|
2579b4c929 | ||
|
|
b69c3b7a78 | ||
|
|
67df4dffeb | ||
|
|
9cf2cc3c96 | ||
|
|
dd94215c3b | ||
|
|
1fd1a8e7ce | ||
|
|
90a831f59b | ||
|
|
9fb218f0be | ||
|
|
bdd22abc55 | ||
|
|
75f712a305 | ||
|
|
e3991c46e2 | ||
|
|
ccc9dfa4aa | ||
|
|
a37a72b7db | ||
|
|
724d3b91a0 | ||
|
|
887c797e13 | ||
|
|
0fd1e470e8 | ||
|
|
13ecab8390 | ||
|
|
15d9db822b | ||
|
|
dd355d1ddf | ||
|
|
c6dcf919e2 | ||
|
|
42541373af | ||
|
|
208c88f5a4 | ||
|
|
5605bc74df | ||
|
|
4bec574011 | ||
|
|
8cfa37ce8b | ||
|
|
351ceeb357 | ||
|
|
259bcc0173 | ||
|
|
db195a8311 | ||
|
|
4307bfb1b5 | ||
|
|
df46ee6939 | ||
|
|
70b9b71104 | ||
|
|
cd33355418 | ||
|
|
77d1cdb753 | ||
|
|
545218e3b9 | ||
|
|
cb07305a87 | ||
|
|
76a39cad20 | ||
|
|
ebd567af48 | ||
|
|
a9c418e3f2 | ||
|
|
e143acd67d | ||
|
|
84eb2f1845 | ||
|
|
b075e5d5d5 | ||
|
|
a08f6aeea2 | ||
|
|
469bd574ac | ||
|
|
c41918c0a8 | ||
|
|
0361ca574d | ||
|
|
c75c169b80 | ||
|
|
eac47bd5db | ||
|
|
d302ef266d | ||
|
|
fdfe9b8250 | ||
|
|
9b1f0d7bcb | ||
|
|
98b1c44283 | ||
|
|
ce026f19b5 | ||
|
|
8e723d06b0 | ||
|
|
a6d84cc65a | ||
|
|
536ce8e95d | ||
|
|
bc1d9e619b | ||
|
|
1062a97cfe | ||
|
|
51db64c90a | ||
|
|
7c4278c3f9 | ||
|
|
6c6b55dcea | ||
|
|
66b0c9e77e | ||
|
|
caf3b95269 | ||
|
|
3866b97416 | ||
|
|
d687658687 | ||
|
|
9f815c73e9 | ||
|
|
a13c782074 | ||
|
|
f2991cfd63 | ||
|
|
8eb4f0844c | ||
|
|
f90dc069dc | ||
|
|
436b430389 | ||
|
|
6667b91ced | ||
|
|
bce56d8105 | ||
|
|
2ec440b321 | ||
|
|
3d2ad531cf | ||
|
|
09136cfdbb | ||
|
|
0ca16f3d04 | ||
|
|
1e72f13f2d | ||
|
|
5e8e8e02d3 | ||
|
|
b2e2c7c798 | ||
|
|
b29dd38188 | ||
|
|
2e122c2ada | ||
|
|
8b68c3bb27 | ||
|
|
8c7523deda | ||
|
|
e8f656f48a | ||
|
|
1eb90202b9 | ||
|
|
82108f4b97 | ||
|
|
ab1030afb1 | ||
|
|
26292740bb | ||
|
|
96cc131305 | ||
|
|
724cd72876 | ||
|
|
fa5d9ef744 | ||
|
|
dc63b4adf5 | ||
|
|
3f20ca03dd | ||
|
|
3a826dccf1 | ||
|
|
a1ce68ce56 | ||
|
|
a45384aacb | ||
|
|
032c4f318e | ||
|
|
5faaa4a4cd | ||
|
|
4e41182521 | ||
|
|
7185032680 | ||
|
|
c9d8df0a48 | ||
|
|
d93693ba55 | ||
|
|
d49b21f3f1 | ||
|
|
a45d280a51 | ||
|
|
2536e299ad | ||
|
|
8b5238230b | ||
|
|
0ce7efee3e | ||
|
|
357677e45c | ||
|
|
a4a332926e | ||
|
|
0a29006914 | ||
|
|
0885bad859 | ||
|
|
42bd1750bd | ||
|
|
58784b2f28 | ||
|
|
e125aace9b | ||
|
|
b02a9e3478 | ||
|
|
3d3c76c836 | ||
|
|
e039f1cd83 | ||
|
|
6c8165601d | ||
|
|
596624c1bf | ||
|
|
4aca34d4c2 | ||
|
|
858b378658 | ||
|
|
8af41a8cc3 | ||
|
|
b13c33cbde | ||
|
|
6906f025d6 | ||
|
|
8b355b8056 | ||
|
|
8938553588 | ||
|
|
14852f3647 | ||
|
|
b55e91784d | ||
|
|
fc14aaa37f | ||
|
|
3295f3845e | ||
|
|
bbf734fbf6 | ||
|
|
c4e9ab7b3e | ||
|
|
11c181ef8b | ||
|
|
1d0754d705 | ||
|
|
80acecd3e6 | ||
|
|
cb2a8db087 | ||
|
|
17b58d775a | ||
|
|
a801e5c1f1 | ||
|
|
966f2cf78d | ||
|
|
8c21b9397f | ||
|
|
6056b739bf | ||
|
|
7823933cf7 | ||
|
|
7623abb1f7 | ||
|
|
f7335b78c3 | ||
|
|
914dc51aca | ||
|
|
7ce480db6a | ||
|
|
3120ba68ad | ||
|
|
9eff1e0e83 | ||
|
|
678250ba27 | ||
|
|
6845a63196 | ||
|
|
3103abe3c8 | ||
|
|
318fd1ddde | ||
|
|
7b2b81a501 | ||
|
|
02de4e42bf | ||
|
|
ddd831fe7a | ||
|
|
42a46b32e9 | ||
|
|
3b395b3ae5 | ||
|
|
9e84e47563 | ||
|
|
aff29427e2 | ||
|
|
474f1b3cc6 | ||
|
|
22bf8065bb | ||
|
|
7bb089e269 | ||
|
|
a6e9158814 | ||
|
|
19a1336720 | ||
|
|
79d5d42e6b | ||
|
|
2d81de5841 | ||
|
|
107ab836d6 | ||
|
|
5225e74465 | ||
|
|
39253d2828 | ||
|
|
171c169537 | ||
|
|
76af9fa33c | ||
|
|
37ccc8a2aa | ||
|
|
d0a0274990 | ||
|
|
b3d374f1b1 | ||
|
|
1c4458faf6 | ||
|
|
96e1d146f4 | ||
|
|
aadc4fb83d | ||
|
|
988ee89cdb | ||
|
|
51cbfbf4bc | ||
|
|
4b69e91362 | ||
|
|
8f9715035a | ||
|
|
35ddccaa16 | ||
|
|
cb0ddfb61c | ||
|
|
c3b6f06b3a | ||
|
|
8046d5a0b1 | ||
|
|
aed07f2943 | ||
|
|
5bf87a78b8 | ||
|
|
0851523d18 | ||
|
|
9e2de81dae | ||
|
|
4887f355c8 | ||
|
|
9b32e6e3f2 | ||
|
|
74790ec80e | ||
|
|
afd8a754b4 | ||
|
|
bc1f7b3baa | ||
|
|
51690e68dc | ||
|
|
0fa076cb73 | ||
|
|
6506993704 | ||
|
|
a69db2ebfb | ||
|
|
d72ca7cb24 | ||
|
|
52dfa7b8f7 | ||
|
|
dae03c5730 | ||
|
|
14efe5d3fd | ||
|
|
653244d737 | ||
|
|
086831b3f5 | ||
|
|
6eca585fc1 | ||
|
|
f6ba36fc28 | ||
|
|
7406288d79 | ||
|
|
2e7fcbe5b8 | ||
|
|
3e7b9f8c14 | ||
|
|
e9189ccc06 | ||
|
|
8c00d715c2 | ||
|
|
d47a369cdf | ||
|
|
52bf6019fd | ||
|
|
25e61fd8ef | ||
|
|
3038be83a0 | ||
|
|
6e2447c70a | ||
|
|
486aba84ca | ||
|
|
b5944181e1 | ||
|
|
3f69cf506a | ||
|
|
6013897c50 | ||
|
|
27cf2a2532 | ||
|
|
d096cbddeb | ||
|
|
117503f214 | ||
|
|
5cc5fae750 | ||
|
|
d93032e1a9 | ||
|
|
507557b2cb | ||
|
|
ee5c3c185a | ||
|
|
e5592a2eb2 | ||
|
|
59200a6e73 | ||
|
|
f468f60ae1 | ||
|
|
9f60d827b6 | ||
|
|
5c936ddb65 | ||
|
|
d0bde62594 | ||
|
|
ada9192337 | ||
|
|
6161f588a9 | ||
|
|
67079d8c17 | ||
|
|
75bd39adb0 | ||
|
|
078bdbeecf | ||
|
|
8ddb86785c | ||
|
|
005af0ecf8 | ||
|
|
bc410a9135 | ||
|
|
fc8ba2e36c | ||
|
|
756441384a | ||
|
|
10f9eecf9e | ||
| 470ebb50a7 | |||
|
|
26cd5d9d31 | ||
|
|
0b694bb878 | ||
|
|
b403c02bba | ||
|
|
0b555bb31c | ||
|
|
40b513a9e3 | ||
|
|
4eacf4deae | ||
|
|
5824423c13 | ||
|
|
ce5bfd41dc | ||
|
|
0c67a07e42 | ||
|
|
187504600a | ||
|
|
658d67feaa | ||
|
|
5207a77591 | ||
|
|
4456adf1dc | ||
|
|
03962c2a50 | ||
|
|
6d2fc7e265 | ||
|
|
39b2e7c5f9 | ||
|
|
f99adf5c3e | ||
|
|
69f8e91a2d | ||
|
|
5b5d01993f | ||
|
|
00fa464a7c | ||
|
|
a5baf46233 | ||
|
|
d63d2a4ec1 | ||
|
|
900f694065 | ||
|
|
838324c15e | ||
|
|
e3205ec06c | ||
|
|
154a33434e | ||
|
|
bfa1bbaeb1 | ||
|
|
1f0128aec4 | ||
|
|
69f03f4e14 | ||
|
|
a932f321d8 | ||
|
|
21200c11c1 | ||
|
|
825c97c49c | ||
|
|
f301899269 | ||
|
|
fc81f62d2f | ||
|
|
93aea5ed02 | ||
|
|
55d4c7238a | ||
|
|
cd75bf263a | ||
|
|
929a1a9974 | ||
|
|
7cafb4fb36 | ||
|
|
a8d222a0f8 | ||
|
|
ac0ddf38b0 | ||
|
|
ecd389c935 | ||
|
|
06a5ea06f8 | ||
|
|
955471ed3c | ||
|
|
c8d3b43b12 | ||
|
|
3e313b06a9 | ||
|
|
d7371a4505 | ||
|
|
32a6d15c2e | ||
|
|
b109e7cead | ||
| d291d3e287 | |||
|
|
5a2968e798 | ||
|
|
120ad9a7da | ||
|
|
67a81b2690 | ||
|
|
506cf1812f | ||
|
|
8cc0eb0dd7 | ||
|
|
4c1f69da3a | ||
|
|
f160ebe705 | ||
|
|
3f49925d01 | ||
|
|
53ed5dc916 | ||
|
|
808b24e31b | ||
|
|
c51c9c2ca9 | ||
|
|
4d6edf5566 | ||
|
|
41a24677f5 | ||
|
|
e7d8d8ffb3 | ||
|
|
60d23dd618 | ||
|
|
d2764c3cd1 | ||
|
|
f9f4355143 | ||
|
|
18bc6c889d | ||
|
|
42a16ef496 | ||
|
|
8178b801fb | ||
|
|
ce331a23c8 | ||
|
|
1cc9213467 | ||
|
|
aabbf4d0f9 | ||
|
|
2bea8be70d | ||
|
|
9b2368443e | ||
|
|
7915ba2c41 | ||
|
|
cc61e48868 | ||
|
|
62db83dcf6 | ||
|
|
7ea9e0f8e9 | ||
|
|
1c852515f5 | ||
|
|
b7e677bd8e | ||
|
|
f47f0d2cee | ||
|
|
3d0f426a98 | ||
|
|
2cb51f6164 | ||
|
|
b73bf998da | ||
|
|
c9ad38ee98 | ||
|
|
76b1b951c0 | ||
|
|
996054b17a | ||
|
|
7fca3d4e3f | ||
|
|
c4e599c861 | ||
|
|
0f6ff2cc1e | ||
|
|
7cac4c71fb | ||
|
|
1322bdab92 | ||
|
|
f201df606a | ||
|
|
a56fce86b1 | ||
|
|
41ccc5fdd9 | ||
|
|
b2877bd493 | ||
|
|
df8f8893e7 | ||
|
|
27fbdf193b | ||
|
|
6ae892989d | ||
|
|
39a7b332da | ||
|
|
c81794b542 | ||
|
|
e456da846a | ||
|
|
5c144be05b | ||
|
|
055669c133 | ||
|
|
4b67e13b24 | ||
|
|
f1ec3eda81 | ||
|
|
f2d5859675 | ||
|
|
ea057d4655 | ||
|
|
26c085b354 | ||
|
|
d13ac52739 | ||
|
|
bbbc28ad6d | ||
|
|
530cd6e95c | ||
|
|
c4e17e43e8 | ||
|
|
fed217eec3 | ||
|
|
1934dc8332 | ||
|
|
9de512cfc7 | ||
|
|
cadafcc86b | ||
|
|
5f805b006f | ||
|
|
c6b65366b7 | ||
|
|
51ddd9c1e5 | ||
|
|
20bf3b86af | ||
|
|
de67cd329b | ||
|
|
779e4942c7 | ||
|
|
a69a1ac7f0 | ||
|
|
2a644d6c2b | ||
|
|
843930c666 | ||
|
|
90d69af7ee | ||
|
|
b7bafb49f4 | ||
|
|
2fa9e48286 | ||
|
|
5cee93c7bd | ||
|
|
1a595c64c6 | ||
|
|
2c901831b7 | ||
|
|
28209950ab | ||
|
|
44069e9867 | ||
|
|
e26af5c09a | ||
|
|
4ee13c9389 | ||
|
|
15f903aa42 | ||
|
|
58a0cce39e | ||
|
|
9a5574ea0e | ||
|
|
e6146dcbcf | ||
|
|
583c7e0c83 | ||
|
|
223bdd5983 | ||
|
|
aef14543f0 | ||
|
|
c88764c1d8 | ||
|
|
2c952c8b01 | ||
|
|
de3610d1aa | ||
|
|
d35d597437 | ||
|
|
3e8deda008 | ||
|
|
a27496c7b3 | ||
|
|
265389befc | ||
|
|
db2531e0b3 | ||
|
|
5e77494aa6 | ||
|
|
6a2ac51925 | ||
|
|
f625b04189 | ||
|
|
68aee776d3 | ||
|
|
38d2195d32 | ||
|
|
4e564b5409 | ||
|
|
298acc4e50 | ||
|
|
af98909753 | ||
|
|
b9d33b38bf | ||
|
|
b61b5f1856 | ||
|
|
805474bb46 | ||
|
|
61701f3c6a | ||
|
|
f20d7476bf | ||
|
|
04a3189a89 | ||
|
|
b1380983ec | ||
|
|
b4834f9b40 | ||
|
|
1b5ad93cad | ||
|
|
f2942f2032 | ||
|
|
4198448b81 | ||
|
|
47a33452e0 | ||
|
|
fb21a0adfd | ||
|
|
e01b06b640 | ||
|
|
24380ddf26 | ||
|
|
1ef42d45af | ||
|
|
6ee13a2779 | ||
|
|
ffd2e135dc | ||
|
|
7f2ad5fc66 | ||
| ff1eb54cc3 | |||
|
|
d6235c6357 | ||
|
|
98118adb9a | ||
|
|
369c4412f3 | ||
|
|
7d7608f06c | ||
|
|
3791cb8a2c | ||
|
|
ff82f7d75c | ||
|
|
fdd566e8ce | ||
|
|
328f8c260b | ||
|
|
029169ac80 | ||
| 66ff1de7a9 | |||
| 053290c7df | |||
| af71a5e53b | |||
| ecdce86287 | |||
| ba1272aaf7 | |||
| d6056b9397 | |||
|
|
00511c72c2 | ||
|
|
1d6f04fd56 | ||
| af4dbb5632 | |||
|
|
f48c291dce | ||
|
|
18be74a1c2 | ||
|
|
456d10a90e | ||
|
|
a7fdb52231 | ||
|
|
066ed00e44 | ||
|
|
49fbc210ad | ||
|
|
951aa474f2 | ||
|
|
961dd54ee0 | ||
|
|
921354dac7 | ||
|
|
48212f8797 | ||
| cb8992dade | |||
| 7dec3120be | |||
| 9568b17f20 | |||
| 0a38056c69 | |||
|
|
096bf4fa11 | ||
|
|
030c4a4fc1 | ||
| 1b70205cab | |||
| 539447ed06 | |||
| e3c333ea39 | |||
| ae72446f85 | |||
| 2b6bf4bd02 | |||
| 6dbb3760e0 | |||
| 200d9a5761 | |||
|
|
01a9a5b823 | ||
|
|
35eadd0e9d | ||
| 5cdca95216 | |||
|
|
81b75a32ed | ||
| b7f5a2ec00 | |||
| db859bbf1d | |||
| 27b56e8efd | |||
| 28ea218d02 | |||
| f356e1f351 | |||
| 4b375e0b8c | |||
| 7db207dd1c | |||
|
|
ffdfc57aa6 | ||
|
|
6fc8408988 | ||
|
|
eeb25fa5bc | ||
|
|
521d128afe | ||
|
|
0135dd6c8f | ||
|
|
1b66c59dc0 | ||
| 96be5c65dc | |||
| 8f6ef569e1 | |||
| ff45c5a8b8 | |||
| fb5f1c820a | |||
| c0e33175cf | |||
| 2bfa72f48f | |||
| fdc2e8ac8a | |||
| 2a894419ab | |||
| 58bfc7b4aa | |||
| 818c0f5eae | |||
| dff9e2f387 | |||
| 6c9256e097 | |||
| 0631a05a78 | |||
| 268bdca4e0 | |||
|
|
e38f331ad5 | ||
| 8d64b30419 | |||
| 4a6c8ffbb3 | |||
| b5c86e7031 | |||
| 9783edd0e3 | |||
| 961728a987 | |||
|
|
fbd8196230 | ||
|
|
5720caaf86 |
170
.all-contributorsrc
Normal file
170
.all-contributorsrc
Normal file
@@ -0,0 +1,170 @@
|
||||
{
|
||||
"projectName": "core",
|
||||
"projectOwner": "sasjs",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
"files": [
|
||||
"README.md"
|
||||
],
|
||||
"imageSize": 100,
|
||||
"commit": false,
|
||||
"commitConvention": "angular",
|
||||
"contributors": [
|
||||
{
|
||||
"login": "allanbowe",
|
||||
"name": "Allan Bowe",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4420615?v=4",
|
||||
"profile": "https://github.com/allanbowe",
|
||||
"contributions": [
|
||||
"business",
|
||||
"code",
|
||||
"content",
|
||||
"doc",
|
||||
"infra",
|
||||
"maintenance",
|
||||
"mentoring",
|
||||
"question",
|
||||
"review",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rafgag",
|
||||
"name": "rafgag",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/69139928?v=4",
|
||||
"profile": "https://github.com/rafgag",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "tmoody",
|
||||
"name": "Trevor Moody",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/79837106?v=4",
|
||||
"profile": "https://github.com/tmoody",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "krishna-acondy",
|
||||
"name": "Krishna Acondy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2980428?v=4",
|
||||
"profile": "https://krishna-acondy.io/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"infra",
|
||||
"blog",
|
||||
"content",
|
||||
"ideas",
|
||||
"video"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "saadjutt01",
|
||||
"name": "Muhammad Saad ",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8914650?v=4",
|
||||
"profile": "https://github.com/saadjutt01",
|
||||
"contributions": [
|
||||
"code",
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "YuryShkoda",
|
||||
"name": "Yury Shkoda",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/25773492?v=4",
|
||||
"profile": "https://www.erudicat.com/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"infra",
|
||||
"video"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "medjedovicm",
|
||||
"name": "Mihajlo Medjedovic",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/18329105?v=4",
|
||||
"profile": "https://github.com/medjedovicm",
|
||||
"contributions": [
|
||||
"infra"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kkchandok",
|
||||
"name": "kkchandok",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/46090627?v=4",
|
||||
"profile": "https://github.com/kkchandok",
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "VladislavParhomchik",
|
||||
"name": "Vladislav Parhomchik",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/83717836?v=4",
|
||||
"profile": "https://github.com/VladislavParhomchik",
|
||||
"contributions": [
|
||||
"test",
|
||||
"review"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vznesh",
|
||||
"name": "Vignesh T.",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28916792?v=4",
|
||||
"profile": "https://github.com/vznesh",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "yabwon",
|
||||
"name": "Bart Jablonski",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9314894?v=4",
|
||||
"profile": "https://github.com/yabwon",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "eltociear",
|
||||
"name": "Ikko Ashimine",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22633385?v=4",
|
||||
"profile": "https://bandism.net/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "henrik-forsell",
|
||||
"name": "Henrik Forsell",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/109935936?v=4",
|
||||
"profile": "https://github.com/henrik-forsell",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rudvfaden",
|
||||
"name": "Rud Faden",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2445577?v=4",
|
||||
"profile": "http://rudvfaden.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "andyjessen",
|
||||
"name": "andyjessen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/62343929?v=4",
|
||||
"profile": "https://github.com/andyjessen",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true,
|
||||
"commitType": "docs"
|
||||
}
|
||||
8
.devcontainer/Dockerfile
Normal file
8
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/javascript-node/.devcontainer/base.Dockerfile
|
||||
|
||||
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||
ARG VARIANT="18-bullseye"
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y doxygen
|
||||
26
.devcontainer/devcontainer.json
Normal file
26
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,26 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/typescript-node
|
||||
{
|
||||
"name": "Node.js & TypeScript",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
// Update 'VARIANT' to pick a Node version: 18, 16, 14.
|
||||
// Append -bullseye or -buster to pin to an OS version.
|
||||
// Use -bullseye variants on local on arm64/Apple Silicon.
|
||||
"args": {
|
||||
"VARIANT": "16-bullseye"
|
||||
}
|
||||
},
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"SASjs.sasjs-for-vscode"
|
||||
],
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "npm i && npm i -g @sasjs/cli",
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "node"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
|
||||
18
.git-hooks/commit-msg
Executable file
18
.git-hooks/commit-msg
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
RED="\033[1;31m"
|
||||
GREEN="\033[1;32m"
|
||||
|
||||
# Get the commit message (the parameter we're given is just the path to the
|
||||
# temporary file which holds the message).
|
||||
commit_message=$(cat "$1")
|
||||
|
||||
if (echo "$commit_message" | grep -Eq "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z \-]+\))?!?: .+$") then
|
||||
echo "${GREEN} ✔ Commit message meets Conventional Commit standards"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "${RED}❌ Commit message does not meet the Conventional Commit standard!"
|
||||
echo "An example of a valid message is:"
|
||||
echo " feat(login): add the 'remember me' button"
|
||||
echo "ℹ More details at: https://www.conventionalcommits.org/en/v1.0.0/#summary"
|
||||
exit 1
|
||||
18
.git-hooks/pre-commit
Executable file
18
.git-hooks/pre-commit
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Ensure lint is passing
|
||||
LINT=`sasjs lint`
|
||||
if [[ "$LINT" != *"All matched files use @sasjs/lint code style!" ]]; then
|
||||
echo "$LINT"
|
||||
echo "To commit in spite of these warnings, use the -n parameter."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Avoid commits to the master branch
|
||||
BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||
|
||||
if [[ "$BRANCH" =~ ^(master|main|develop)$ ]]; then
|
||||
echo "You are on branch $BRANCH. Are you sure you want to commit to this branch?"
|
||||
echo "If so, commit with -n to bypass the pre-commit hook."
|
||||
exit 1
|
||||
fi
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
5
.github/CHANGELOG.md
vendored
Normal file
5
.github/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# CHANGELOG
|
||||
|
||||
As the changes are managed automatically in github, we don't generate an additional changelog. To view the fixes/features in each release, check out the releases page below:
|
||||
|
||||
[https://github.com/sasjs/core/releases](https://github.com/sasjs/core/releases)
|
||||
128
.github/CODE_OF_CONDUCT.md
vendored
Normal file
128
.github/CODE_OF_CONDUCT.md
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
https://sasapps.io/contact-us.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
32
.github/CONTRIBUTING.md
vendored
Normal file
32
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are warmly welcomed! To avoid any misunderstandings, do please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before submitting a PR.
|
||||
|
||||
Please note we have a [code of conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/), please follow it in all your interactions with the project.
|
||||
|
||||
# Environment Setup
|
||||
|
||||
This repository makes use of the [SASjs](https://sasjs.io) framework for code organisation, compilation, documentation, and deployment. The following tools are highly recommended:
|
||||
|
||||
* [NPM](https://sasjs.io/windows/#npm) - the runtime and dependency manager for [SASjs CLI](https://cli.sasjs.io) (batteries included)
|
||||
* [VSCode](https://sasjs.io/windows/#vscode) - feature packed IDE for code editing (warning - highly effective!)
|
||||
* [GIT](https://sasjs.io/windows/#git) - a safety net you cannot (and should not) do without.
|
||||
|
||||
For generating the documentation (`sasjs doc`) it is also necessary to install [doxygen](https://www.doxygen.nl/manual/install.html) and GraphViz (`sudo port install graphviz` on mac, or `sudo apt-get install graphviz` on Ubuntu).
|
||||
|
||||
|
||||
To get configured:
|
||||
|
||||
1. Clone the repository
|
||||
2. Install local dependencies (`npm install`)
|
||||
3. Install the SASjs CLI globally (`npm install -g @sasjs/cli`)
|
||||
4. Add a target, and authentication (`npm add`). See [docs](https://cli.sasjs.io/add/).
|
||||
|
||||
To contribute:
|
||||
|
||||
1. Create your feature branch (`git checkout -b myfeature`)
|
||||
2. Make your change
|
||||
3. Update the `all.sas` file (`python3 build.py`)
|
||||
4. Commit using a [Conventional Commit](https://www.conventionalcommits.org)
|
||||
5. Push and make a PR
|
||||
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
custom: https://getalby.com/p/sasjs
|
||||
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
## Issue
|
||||
|
||||
Link any related issue(s) in this section.
|
||||
|
||||
## Intent
|
||||
|
||||
What this PR intends to achieve.
|
||||
|
||||
## Implementation
|
||||
|
||||
What code changes have been made to achieve the intent.
|
||||
|
||||
## Checks
|
||||
|
||||
- [ ] Code is formatted correctly (`sasjs lint`).
|
||||
- [ ] Any new functionality has been unit tested.
|
||||
- [ ] All unit tests are passing (`sasjs test`).
|
||||
- [ ] The PR desc or underlying commits follow the [Conventional Commit](https://www.conventionalcommits.org) standard
|
||||
18
.github/SECURITY.md
vendored
Normal file
18
.github/SECURITY.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Security Policy
|
||||
|
||||
Security is an extremely high priority when it comes to the SASjs product suite. We take a number of steps across all repositories to minimise risk, such as:
|
||||
|
||||
* Regular dependabot updates
|
||||
* Snyk reports
|
||||
* Minimising dependencies, especially production dependencies (sasjs/core has NONE)
|
||||
* Testing & Code review process
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We support only the latest version
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We welcome disclosures of all kinds in relation to all the SASjs libraries. You can submit them here: [https://sasapps.io/contact-us](https://sasapps.io/contact-us)
|
||||
|
||||
|
||||
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: monthly
|
||||
open-pull-requests-limit: 3
|
||||
allow:
|
||||
- dependency-type: "production"
|
||||
27
.github/vpn/config.ovpn
vendored
Normal file
27
.github/vpn/config.ovpn
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Client
|
||||
client
|
||||
tls-client
|
||||
dev tun
|
||||
# this will connect with whatever proto DNS tells us (https://community.openvpn.net/openvpn/ticket/934)
|
||||
proto udp
|
||||
remote vpn.4gl.io 7194
|
||||
resolv-retry infinite
|
||||
# this will fallback from udp6 to udp4 as well
|
||||
connect-timeout 5
|
||||
data-ciphers AES-256-CBC:AES-256-GCM
|
||||
auth SHA256
|
||||
script-security 2
|
||||
keepalive 10 120
|
||||
remote-cert-tls server
|
||||
|
||||
# Keys
|
||||
ca ca.crt
|
||||
cert user.crt
|
||||
key user.key
|
||||
tls-auth tls.key 1
|
||||
|
||||
# Security
|
||||
nobind
|
||||
persist-key
|
||||
persist-tun
|
||||
verb 3
|
||||
53
.github/workflows/main.yml
vendored
53
.github/workflows/main.yml
vendored
@@ -13,9 +13,54 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Semantic Release
|
||||
uses: cycjimmy/semantic-release-action@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm ci
|
||||
|
||||
- name: Check code style (aborts if errors found)
|
||||
run: npx @sasjs/cli lint
|
||||
|
||||
- name: Write VPN Files
|
||||
run: |
|
||||
echo "$CA_CRT" > .github/vpn/ca.crt
|
||||
echo "$USER_CRT" > .github/vpn/user.crt
|
||||
echo "$USER_KEY" > .github/vpn/user.key
|
||||
echo "$TLS_KEY" > .github/vpn/tls.key
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
CA_CRT: ${{ secrets.CA_CRT}}
|
||||
USER_CRT: ${{ secrets.USER_CRT }}
|
||||
USER_KEY: ${{ secrets.USER_KEY }}
|
||||
TLS_KEY: ${{ secrets.TLS_KEY }}
|
||||
|
||||
- name: Install Open VPN
|
||||
run: |
|
||||
#sudo apt install apt-transport-https
|
||||
#sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub
|
||||
#sudo apt-key add openvpn-repo-pkg-key.pub
|
||||
#sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-jammy.list
|
||||
#sudo apt update
|
||||
#sudo apt install openvpn3=17~betaUb22042+jammy
|
||||
- name: Start Open VPN 3
|
||||
run: |
|
||||
# openvpn3 session-start --config .github/vpn/config.ovpn
|
||||
|
||||
- name: Add credentials
|
||||
run: |
|
||||
echo "CLIENT=${{secrets.SAS9_4GL_IO_CLIENT}}"> .env.server
|
||||
echo "ACCESS_TOKEN=${{secrets.SAS9_4GL_IO_ACCESS_TOKEN}}" >> .env.server
|
||||
echo "REFRESH_TOKEN=${{secrets.SAS9_4GL_IO_REFRESH_TOKEN}}" >> .env.server
|
||||
|
||||
- name: Semantic Release
|
||||
uses: cycjimmy/semantic-release-action@v4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: SAS Packages Release
|
||||
run: |
|
||||
npx @sasjs/cli compile job -s sasjs/utils/create_sas_package.sas -o sasjsbuild -t server
|
||||
# need long duration token per https://github.com/sasjs/server/issues/307
|
||||
# npx @sasjs/cli run sasjsbuild/jobs/utils/create_sas_package.sas -t server
|
||||
|
||||
32
.github/workflows/notmain.yml
vendored
Normal file
32
.github/workflows/notmain.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
|
||||
|
||||
name: SASjs Core - Update all.sas
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm ci
|
||||
npm i -g @sasjs/cli@latest
|
||||
|
||||
- name: Ensure all.sas is always up to date
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
python3 build.py
|
||||
git add all.sas
|
||||
git commit -m "chore: updating all.sas" --allow-empty
|
||||
git push
|
||||
|
||||
|
||||
86
.github/workflows/run-tests.yml
vendored
Normal file
86
.github/workflows/run-tests.yml
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [lts/hydrogen]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Write VPN Files
|
||||
run: |
|
||||
echo "$CA_CRT" > .github/vpn/ca.crt
|
||||
echo "$USER_CRT" > .github/vpn/user.crt
|
||||
echo "$USER_KEY" > .github/vpn/user.key
|
||||
echo "$TLS_KEY" > .github/vpn/tls.key
|
||||
shell: bash
|
||||
env:
|
||||
CA_CRT: ${{ secrets.CA_CRT}}
|
||||
USER_CRT: ${{ secrets.USER_CRT }}
|
||||
USER_KEY: ${{ secrets.USER_KEY }}
|
||||
TLS_KEY: ${{ secrets.TLS_KEY }}
|
||||
|
||||
- name: Chmod VPN files
|
||||
run: |
|
||||
chmod 600 .github/vpn/ca.crt .github/vpn/user.crt .github/vpn/user.key .github/vpn/tls.key
|
||||
|
||||
- name: Install Open VPN
|
||||
run: |
|
||||
sudo apt install apt-transport-https
|
||||
sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub
|
||||
sudo apt-key add openvpn-repo-pkg-key.pub
|
||||
sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-jammy.list
|
||||
sudo apt update
|
||||
sudo apt install openvpn3=17~betaUb22042+jammy
|
||||
|
||||
- name: Start Open VPN 3
|
||||
run: openvpn3 session-start --config .github/vpn/config.ovpn
|
||||
|
||||
- name: Fetch SASJS server
|
||||
run: curl ${{ secrets.SASJS_SERVER_URL }}/SASjsApi/info
|
||||
|
||||
- name: Install Doxygen
|
||||
run: sudo apt-get install doxygen
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Check code style (aborts if errors found)
|
||||
run: npx @sasjs/cli lint
|
||||
|
||||
- name: Add client
|
||||
run: echo "CLIENT=${{secrets.SAS9_4GL_IO_CLIENT}}"> .env.server
|
||||
|
||||
- name: Add access token
|
||||
run: echo "ACCESS_TOKEN=${{secrets.SAS9_4GL_IO_ACCESS_TOKEN}}" >> .env.server
|
||||
|
||||
- name: Add refresh token
|
||||
run: echo "REFRESH_TOKEN=${{secrets.SAS9_4GL_IO_REFRESH_TOKEN}}" >> .env.server
|
||||
|
||||
- name: Build & Deploy Project to SAS server
|
||||
run: npx @sasjs/cli cbd -t server
|
||||
|
||||
- name: Run all tests
|
||||
run: npx @sasjs/cli test -t server
|
||||
env:
|
||||
CI: true
|
||||
CLIENT: ${{secrets.CLIENT}}
|
||||
SECRET: ${{secrets.SECRET}}
|
||||
SAS_USERNAME: ${{secrets.SAS_USERNAME}}
|
||||
SAS_PASSWORD: ${{secrets.SAS_PASSWORD}}
|
||||
ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}}
|
||||
REFRESH_TOKEN: ${{secrets.REFRESH_TOKEN}}
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,2 +1,16 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
sasjsbuild/
|
||||
sasjsresults/
|
||||
|
||||
# avoid filenames with spaces being committed to source control
|
||||
**\ **
|
||||
|
||||
# ignore the mc_* files - containing macros for individual libraries
|
||||
mc_*
|
||||
|
||||
# ignore .env files as they can contain sasjs access tokens
|
||||
*.env*
|
||||
|
||||
~
|
||||
|
||||
|
||||
6
.gitpod.dockerfile
Normal file
6
.gitpod.dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
RUN sudo apt-get update \
|
||||
&& sudo apt-get install -y doxygen \
|
||||
&& sudo apt-get install -y graphviz \
|
||||
&& sudo rm -rf /var/lib/apt/lists/*
|
||||
27
.gitpod.yml
Normal file
27
.gitpod.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
tasks:
|
||||
- init: npm install -g npm
|
||||
- command: npm i
|
||||
- command: npm i -g @sasjs/cli
|
||||
|
||||
image:
|
||||
file: .gitpod.dockerfile
|
||||
vscode:
|
||||
extensions:
|
||||
- sasjs.sasjs-for-vscode
|
||||
|
||||
github:
|
||||
prebuilds:
|
||||
# enable for the master/default branch (defaults to true)
|
||||
master: true
|
||||
# enable for all branches in this repo (defaults to false)
|
||||
branches: false
|
||||
# enable for pull requests coming from this repo (defaults to true)
|
||||
pullRequests: true
|
||||
# enable for pull requests coming from forks (defaults to false)
|
||||
pullRequestsFromForks: true
|
||||
# add a "Review in Gitpod" button as a comment to pull requests (defaults to true)
|
||||
addComment: true
|
||||
# add a "Review in Gitpod" button to pull requests (defaults to false)
|
||||
addBadge: false
|
||||
# add a label once the prebuild is ready to pull requests (defaults to false)
|
||||
addLabel: prebuilt-in-gitpod
|
||||
11
.npmignore
Normal file
11
.npmignore
Normal file
@@ -0,0 +1,11 @@
|
||||
all.sas
|
||||
build.py
|
||||
.gitpod*
|
||||
tests/
|
||||
sasjs/
|
||||
.github/
|
||||
.git-hooks/
|
||||
.vscode/
|
||||
make_singlefile.sh
|
||||
*.md
|
||||
.all-contributorsrc
|
||||
15
.sasjslint
Normal file
15
.sasjslint
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"noTrailingSpaces": true,
|
||||
"noEncodedPasswords": true,
|
||||
"hasDoxygenHeader": true,
|
||||
"hasMacroNameInMend": true,
|
||||
"hasMacroParentheses": true,
|
||||
"lineEndings": "lf",
|
||||
"noGremlins": true,
|
||||
"noNestedMacros": false,
|
||||
"noSpacesInFileNames": true,
|
||||
"maxLineLength": 300,
|
||||
"lowerCaseFileNames": true,
|
||||
"noTabs": true,
|
||||
"indentationMultiple": 2
|
||||
}
|
||||
9
.vscode/.editorconfig
vendored
Normal file
9
.vscode/.editorconfig
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"**/sasjsbuild/**": true,
|
||||
"**/dist/**":true
|
||||
},
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2,
|
||||
"trim_trailing_whitespace": true
|
||||
}
|
||||
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"sasjs.sasjs-for-vscode"
|
||||
]
|
||||
}
|
||||
12
.vscode/settings.json
vendored
Normal file
12
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.detectIndentation": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [
|
||||
80
|
||||
],
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"sasjs-for-vscode.target": "docsonly",
|
||||
"sasjs-for-vscode.isLocal": true
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
Contributions to the macrocore library are warmly welcomed! To avoid any
|
||||
misunderstandings, do please first discuss the change you wish to make via issue,
|
||||
email, or any other method with the owners of this repository before submitting
|
||||
a PR.
|
||||
|
||||
Please note we have a code of conduct, please follow it in all your interactions
|
||||
with the project.
|
||||
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
### Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
### Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
### Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
### Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
### Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at support@macropeople.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
### Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright 2020 (Allan Bowe)
|
||||
Copyright 2021 (Allan Bowe)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
236
README.md
236
README.md
@@ -1,6 +1,15 @@
|
||||
# Macro Core
|
||||
|
||||
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of Application Development on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed.
|
||||
[](http://npmjs.org/package/@sasjs/core)
|
||||
[](https://github.com/sasjs/core/blob/main/.github/workflows/main.yml)
|
||||

|
||||

|
||||
[](https://github.com/sasjs/core/issues?q=is%3Aissue+is%3Aclosed)
|
||||
[](https://github.com/sasjs/core/issues)
|
||||

|
||||
|
||||
|
||||
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/.github/CONTRIBUTING.md) are welcome.
|
||||
|
||||
You can download and compile them all in just two lines of SAS code:
|
||||
|
||||
@@ -9,65 +18,108 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
```
|
||||
|
||||
Documentation: https://sasjs.github.io/core.github.io/files.html
|
||||
Documentation: https://core.sasjs.io
|
||||
|
||||
# Components
|
||||
## Components
|
||||
|
||||
**base** library (SAS9/Viya)
|
||||
### BASE folder (All Platforms)
|
||||
|
||||
- OS independent
|
||||
- Not metadata aware
|
||||
- Works on all SAS Platforms
|
||||
- No X command
|
||||
- Prefixes: _mf_, _mp_
|
||||
- Prefixes: `mf_`, `mp_`
|
||||
|
||||
**meta** library (SAS9 only)
|
||||
### DDL folder (All Platforms)
|
||||
|
||||
- OS independent
|
||||
- Metadata aware
|
||||
- Works on all SAS Platforms
|
||||
- No X command
|
||||
- Prefixes: _mm_
|
||||
- Prefixes: `mddl_(lib)_` -> where lib can be "SAS" (in relation to a SAS component) or "DC" (in relation to a Data Controller component)
|
||||
|
||||
**viya** library (Viya only)
|
||||
This library will not be used for storing data entries (such as formats or datalines). Where this becomes necessary in the future, a new repo will be created, in order to keep the NPM bundle size down (for the benefit of those looking to embed purely macros in their applications).
|
||||
|
||||
- OS independent
|
||||
- No X command
|
||||
- Prefixes: _mv_
|
||||
### FCMP folder (All Platforms)
|
||||
|
||||
**metax** library (SAS9 only)
|
||||
- Function and macro names are identical, except for special cases
|
||||
- Prefixes: `mcf_`
|
||||
|
||||
- OS specific
|
||||
- Metadata aware
|
||||
- X command enabled
|
||||
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||
The fcmp macros are used to generate fcmp functions, and can be used with or without the `proc fcmp` wrapper.
|
||||
|
||||
**lua** library
|
||||
### LUA folder
|
||||
|
||||
Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper.
|
||||
|
||||
To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert your LUA into a data step with put statements, and create the macro wrapper with a `ml_` prefix. You can then use your module in any program by running:
|
||||
To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert all files with a ".lua" extension into a macro wrapper with an `ml_` prefix (embedding the necessary data step put statements). You can then use your module in any program by running:
|
||||
|
||||
```
|
||||
```sas
|
||||
/* compile the lua module */
|
||||
%ml_yourmodule()
|
||||
|
||||
/* Execute. Do not use the restart keyword! */
|
||||
proc lua;
|
||||
proc lua;
|
||||
submit;
|
||||
print(yourStuff);
|
||||
endsubmit;
|
||||
run;
|
||||
```
|
||||
|
||||
- Prefixes: `ml_`
|
||||
|
||||
### META folder (SAS9 only)
|
||||
|
||||
Macros used in SAS EBI, which connect to the metadata server.
|
||||
|
||||
- OS independent
|
||||
- Metadata aware
|
||||
- No X command
|
||||
- Prefixes: `mm_`
|
||||
|
||||
### METAX folder (SAS9 only)
|
||||
|
||||
- OS specific
|
||||
- Metadata aware
|
||||
- X command enabled
|
||||
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||
- Prefixes: `mmx_`
|
||||
|
||||
# Installation
|
||||
### SERVER folder (@sasjs/server only)
|
||||
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
|
||||
|
||||
First, download the repo to a location your SAS system can access. Then update your sasautos path to include the components you wish to have available,eg:
|
||||
- OS independent
|
||||
- @sasjs/server aware
|
||||
- No X command
|
||||
- Prefixes: `ms_`
|
||||
|
||||
### VIYA folder (Viya only)
|
||||
|
||||
Macros used for interfacing with SAS Viya.
|
||||
|
||||
- OS independent
|
||||
- No X command
|
||||
- Prefixes: `mv_`, `mvf_`
|
||||
|
||||
### XPLATFORM folder (Viya, Meta, and Server)
|
||||
|
||||
Sometimes it is helpful to use a macro that can be used interchangeably regardless of the server type on which is is running (SASVIYA, SAS9, SASJS).
|
||||
|
||||
- OS independent
|
||||
- No X command
|
||||
- Prefixes: `mx_`
|
||||
|
||||
## Installation
|
||||
|
||||
First, download the repo to a location your SAS system can access. Then update your sasautos path to include the components you wish to have available, eg:
|
||||
|
||||
```sas
|
||||
options insert=(sasautos="/your/path/macrocore/base");
|
||||
options insert=(sasautos="/your/path/macrocore/meta");
|
||||
%let repoloc=/your/path/core;
|
||||
options insert=(sasautos="&repoloc/base");
|
||||
options insert=(sasautos="&repoloc/ddl");
|
||||
options insert=(sasautos="&repoloc/fcmp");
|
||||
options insert=(sasautos="&repoloc/lua");
|
||||
options insert=(sasautos="&repoloc/meta");
|
||||
options insert=(sasautos="&repoloc/metax");
|
||||
options insert=(sasautos="&repoloc/server");
|
||||
options insert=(sasautos="&repoloc/viya");
|
||||
options insert=(sasautos="&repoloc/xplatform");
|
||||
```
|
||||
|
||||
The above can be done directly in your sas program, via an autoexec, or an initialisation program.
|
||||
@@ -79,29 +131,33 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
```
|
||||
|
||||
# Standards
|
||||
## Standards
|
||||
|
||||
## File Properties
|
||||
### File Properties
|
||||
|
||||
- filenames much match macro names
|
||||
- filenames must be lowercase
|
||||
- filenames must be lowercase, without spaces
|
||||
- macro names must be lowercase
|
||||
- one macro per file
|
||||
- prefixes:
|
||||
- _mf_ for macro functions (can be used in open code).
|
||||
- _mp_ for macro procedures (which generate sas code)
|
||||
- _mm_ for metadata macros (interface with the metadata server).
|
||||
- _mmx_ for macros that use metadata and are XCMD enabled
|
||||
- _mx_ for macros that are XCMD enabled
|
||||
- _ml_ for macros that are used to compile LUA modules
|
||||
- _mv_ for macros that will only work in Viya
|
||||
- _mcf__: macro compiled functions (proc fcmp)
|
||||
- _mddl__: macros containing DDL (Data Definition Language)
|
||||
- _mf__: macro functions (can be used in open code).
|
||||
- _mfv__: macro functions that work only in Viya
|
||||
- _ml__: macros that are used to compile LUA modules
|
||||
- _mm__: metadata macros (interface with the metadata server).
|
||||
- _mmx__: macros that use metadata and are XCMD enabled (working on both windows and unix)
|
||||
- _mp__: macro procedures (which generate sas code)
|
||||
- _ms__: macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server)
|
||||
- _mv__: macro procedures that will only work in Viya
|
||||
- _mx__: macros that work on Viya, SAS 9 EBI and SASjs Server
|
||||
- follow verb-noun convention
|
||||
- unix style line endings (lf)
|
||||
- individual lines should be no more than 80 characters long
|
||||
- UTF-8
|
||||
- no trailing white space
|
||||
|
||||
## Header Properties
|
||||
|
||||
### Header Properties
|
||||
|
||||
The **Macro Core** documentation is created using [doxygen](http://www.doxygen.nl). A full list of attributes can be found [here](http://www.doxygen.nl/manual/commands.html) but the following are most relevant:
|
||||
|
||||
@@ -115,35 +171,115 @@ The **Macro Core** documentation is created using [doxygen](http://www.doxygen.n
|
||||
|
||||
All macros must be commented in the doxygen format, to enable the [online documentation](https://core.sasjs.io).
|
||||
|
||||
### Dependencies
|
||||
SAS code can contain one of two types of dependency - SAS Macros, and SAS Programs. When compiling projects using the [SASjs CLI](https://cli.sasjs.io) the doxygen header is scanned for ` @li` items under the following headers:
|
||||
#### Dependencies
|
||||
SAS code can contain one of two types of dependency - SAS Macros, and SAS Includes. When compiling projects using the [SASjs CLI](https://cli.sasjs.io) the doxygen header is scanned for ` @li` items under the following headers:
|
||||
|
||||
```
|
||||
```sas
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mm_assignlib.sas
|
||||
|
||||
<h4> SAS Programs </h4>
|
||||
<h4> SAS Includes </h4>
|
||||
@li somefile.ddl SOMEFREF
|
||||
@li someprogram.sas FREFTWO
|
||||
```
|
||||
|
||||
The CLI can then extract all the dependencies and insert as precode (SAS Macros) or in a temp engine fileref (SAS Programs) when creating SAS Jobs and Services.
|
||||
The CLI can then extract all the dependencies and insert as precode (SAS Macros) or in a temp engine fileref (SAS Includes) when creating SAS Jobs and Services (and Tests).
|
||||
|
||||
When contributing to this library, it is therefore important to ensure that all dependencies are listed in the header in this format.
|
||||
|
||||
|
||||
## Coding Standards
|
||||
### Coding Standards
|
||||
|
||||
- Indentation = 2 spaces. No tabs!
|
||||
- no trailing white space
|
||||
- no invisible characters, other than spaces. If invisibles are needed, use hex literals.
|
||||
- Macro variables should not have the trailing dot (`&var` not `&var.`) unless necessary to prevent incorrect resolution
|
||||
- The closing `%mend;` should not contain the macro name.
|
||||
- The closing `%mend;` should **not** contain the macro name.
|
||||
- All macros should be defined with brackets, even if no variables are needed - ie `%macro x();` not `%macro x;`
|
||||
- Mandatory parameters should be positional, all optional parameters should be keyword (var=) style.
|
||||
- All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect.
|
||||
- All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect, or the [USER](https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/lrcon/n18m1vkqmeo4esn1moikt23zhp8s.htm) library is active.
|
||||
- Avoid naming collisions! All macro variables should be local scope. Use system generated work tables where possible - eg `data ; set sashelp.class; run; data &output; set &syslast; run;`
|
||||
- If you have a long-running SQL query, the use of a `quit;` statement is recommended in order to benefit from the timing statistics.
|
||||
- Where global macro variables are absolutely necessary, they should make use of `&sasjs_prefix` - see mp_init.sas
|
||||
- The use of `quit;` for `proc sql` is optional unless you are looking to benefit from the timing statistics.
|
||||
- Use [sasjs lint](https://github.com/sasjs/lint)!
|
||||
|
||||
# General Notes
|
||||
## General Notes
|
||||
|
||||
- All macros should be compatible with SAS versions from support level B and above (so currently 9.2 and later). If an earlier version is not supported, then the macro should say as such in the header documentation, and exit gracefully (eg `%if %sysevalf(&sysver<9.3) %then %return`).
|
||||
- All macros should be compatible with SAS versions from support level B and above (so currently 9.3 and later). If an earlier version is not supported, then the macro should say as such in the header documentation, and exit gracefully.
|
||||
- It's [best to avoid](https://git.datacontroller.io/dc/dc/issues/50) special / non-ASCII characters for compatibility with the widest variety of SAS installations.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major/breaking release (v5) becomes necessary:
|
||||
|
||||
* mcf_xxx macros to have `insert_cmplib` option deprecated (the option is now checked automatically with value inserted only if needed)
|
||||
* mcf_xxx macros to have `wrap=` option defaulted to YES for convenience. Set this option explicitly to avoid issues.
|
||||
* mf_getuniquelibref.sas to have the deprecated maxtries parameter removed (no longer needed)
|
||||
* mp_abort.sas will have the redundant type= parameter removed.
|
||||
* mp_coretable.sas will be replaced by the standalone macros in the `ddl` folder (which are already available)
|
||||
* mp_getddl.sas to be renamed to mp_ds2ddl.sas (consistent with other ds2xxx macros). A wrapper macro is already in place, and you are able to use this immediately. The default for SHOWLOG will also be YES instead of NO.
|
||||
* mp_testservice.sas to be renamed as mp_execute.sas (as it doesn't actually test anything)
|
||||
|
||||
## Star Gazing
|
||||
|
||||
If you find this library useful, please leave a [star](https://github.com/sasjs/core/stargazers) and help us grow our star graph!
|
||||
|
||||

|
||||
|
||||
## Other SAS Repositories
|
||||
|
||||
The following repositories are also worth checking out:
|
||||
|
||||
* [chris-swenson/sasmacros](https://github.com/chris-swenson/sasmacros)
|
||||
* [Criptic/sas_snippets](https://github.com/Criptic/sas_snippets)
|
||||
* [greg-wotton/sas-programs](https://github.com/greg-wootton/sas-programs)
|
||||
* [KatjaGlassConsulting/SMILE-SmartSASMacros](https://github.com/KatjaGlassConsulting/SMILE-SmartSASMacros)
|
||||
* [paul-canals/toolbox](https://github.com/paul-canals/toolbox)
|
||||
* [rogerjdeangelis](https://github.com/rogerjdeangelis)
|
||||
* [SASJedi/sas-macros](https://github.com/SASJedi/sas-macros)
|
||||
* [scottbass/sas](https://github.com/scottbass/SAS)
|
||||
* [xieliaing/SAS](https://github.com/xieliaing/SAS)
|
||||
* [yabwon/sas_packages](https://github.com/yabwon/SAS_PACKAGES)
|
||||
|
||||
## Contributors ✨
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt="Allan Bowe"/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="#business-allanbowe" title="Business development">💼</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Code">💻</a> <a href="#content-allanbowe" title="Content">🖋</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Documentation">📖</a> <a href="#infra-allanbowe" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-allanbowe" title="Maintenance">🚧</a> <a href="#mentoring-allanbowe" title="Mentoring">🧑🏫</a> <a href="#question-allanbowe" title="Answering Questions">💬</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rafgag"><img src="https://avatars.githubusercontent.com/u/69139928?v=4?s=100" width="100px;" alt="rafgag"/><br /><sub><b>rafgag</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=rafgag" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tmoody"><img src="https://avatars.githubusercontent.com/u/79837106?v=4?s=100" width="100px;" alt="Trevor Moody"/><br /><sub><b>Trevor Moody</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=tmoody" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt="Krishna Acondy"/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=krishna-acondy" title="Code">💻</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#blog-krishna-acondy" title="Blogposts">📝</a> <a href="#content-krishna-acondy" title="Content">🖋</a> <a href="#ideas-krishna-acondy" title="Ideas, Planning, & Feedback">🤔</a> <a href="#video-krishna-acondy" title="Videos">📹</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt="Muhammad Saad "/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=saadjutt01" title="Code">💻</a> <a href="#ideas-saadjutt01" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt="Yury Shkoda"/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=YuryShkoda" title="Code">💻</a> <a href="#infra-YuryShkoda" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#video-YuryShkoda" title="Videos">📹</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt="Mihajlo Medjedovic"/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kkchandok"><img src="https://avatars.githubusercontent.com/u/46090627?v=4?s=100" width="100px;" alt="kkchandok"/><br /><sub><b>kkchandok</b></sub></a><br /><a href="#ideas-kkchandok" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt="Vladislav Parhomchik"/><br /><sub><b>Vladislav Parhomchik</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vznesh"><img src="https://avatars.githubusercontent.com/u/28916792?v=4?s=100" width="100px;" alt="Vignesh T."/><br /><sub><b>Vignesh T.</b></sub></a><br /><a href="https://github.com/sasjs/core/issues?q=author%3Avznesh" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yabwon"><img src="https://avatars.githubusercontent.com/u/9314894?v=4?s=100" width="100px;" alt="Bart Jablonski"/><br /><sub><b>Bart Jablonski</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=yabwon" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=100" width="100px;" alt="Ikko Ashimine"/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=eltociear" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/henrik-forsell"><img src="https://avatars.githubusercontent.com/u/109935936?v=4?s=100" width="100px;" alt="Henrik Forsell"/><br /><sub><b>Henrik Forsell</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=henrik-forsell" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://rudvfaden.github.io/"><img src="https://avatars.githubusercontent.com/u/2445577?v=4?s=100" width="100px;" alt="Rud Faden"/><br /><sub><b>Rud Faden</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=rudvfaden" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/andyjessen"><img src="https://avatars.githubusercontent.com/u/62343929?v=4?s=100" width="100px;" alt="andyjessen"/><br /><sub><b>andyjessen</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=andyjessen" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
/**
|
||||
@file
|
||||
@brief abort gracefully according to context
|
||||
@details Do not use directly! See bottom of explanation for details.
|
||||
@brief Abort, ungracefully
|
||||
@details Will abort with a straightforward %abort if the condition is true.
|
||||
|
||||
Configures an abort mechanism according to site specific policies or the
|
||||
particulars of an environment. For instance, can stream custom
|
||||
results back to the client in an STP Web App context, or completely stop
|
||||
in the case of a batch run.
|
||||
@param [in] mac= (mf_abort.sas) Name of calling macro (is printed to the log)
|
||||
@param [in] msg= ( ) Additional string to print to the log
|
||||
@param [in] iftrue= (%str(1=1)) Conditional logic under which to perform the
|
||||
abort
|
||||
|
||||
For the sharp eyed readers - this is no longer a macro function!! It became
|
||||
a macro procedure during a project and now it's kinda stuck that way until
|
||||
that project is updated (if it's ever updated). In the meantime we created
|
||||
`mp_abort` which is just a wrapper for this one, and so we recomend you use
|
||||
that for forwards compatibility reasons.
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param type= deprecated. Not used.
|
||||
@param msg= message to be returned
|
||||
@param iftrue= supply a condition under which the macro should be executed.
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
)/*/STORE SOURCE*/;
|
||||
%macro mf_abort(mac=mf_abort.sas, msg=, iftrue=%str(1=1)
|
||||
)/des='ungraceful abort' /*STORE SOURCE*/;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
@@ -33,112 +25,8 @@
|
||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||
%put NOTE - &msg;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson) or "&SYSPROCESSNAME"="Compute Server" %then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
/* extract log err / warn, if exist */
|
||||
%local logloc logline;
|
||||
%global logmsg; /* capture global messages */
|
||||
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
|
||||
%else %let logloc=%qsysfunc(getoption(LOG));
|
||||
proc printto log=log;run;
|
||||
%if %length(&logloc)>0 %then %do;
|
||||
%let logline=0;
|
||||
data _null_;
|
||||
infile &logloc lrecl=5000;
|
||||
input; putlog _infile_;
|
||||
i=1;
|
||||
retain logonce 0;
|
||||
if (_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR") and logonce=0 then do;
|
||||
call symputx('logline',_n_);
|
||||
logonce+1;
|
||||
end;
|
||||
run;
|
||||
/* capture log including lines BEFORE the err */
|
||||
%if &logline>0 %then %do;
|
||||
data _null_;
|
||||
infile &logloc lrecl=5000;
|
||||
input;
|
||||
i=1;
|
||||
stoploop=0;
|
||||
if _n_ ge &logline-5 and stoploop=0 then do until (i>12);
|
||||
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
|
||||
input;
|
||||
i+1;
|
||||
stoploop=1;
|
||||
end;
|
||||
if stoploop=1 then stop;
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
%abort;
|
||||
|
||||
/* send response in SASjs JSON format */
|
||||
data _null_;
|
||||
file _webout mod lrecl=32000;
|
||||
length msg $32767;
|
||||
sasdatetime=datetime();
|
||||
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
||||
/* escape the quotes */
|
||||
msg=tranwrd(msg,'"','\"');
|
||||
/* ditch the CRLFs as chrome complains */
|
||||
msg=compress(msg,,'kw');
|
||||
/* quote without quoting the quotes (which are escaped instead) */
|
||||
msg=cats('"',msg,'"');
|
||||
if symexist('_debug') then debug=symget('_debug');
|
||||
if debug ge 131 then put '>>weboutBEGIN<<';
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
put ',"sasjsAbort" : [{';
|
||||
put ' "MSG":' msg ;
|
||||
put ' ,"MAC": "' "&mac" '"}]';
|
||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||
if symexist('_metauser') then do;
|
||||
_METAUSER=quote(trim(symget('_METAUSER')));
|
||||
put ",""_METAUSER"": " _METAUSER;
|
||||
_METAPERSON=quote(trim(symget('_METAPERSON')));
|
||||
put ',"_METAPERSON": ' _METAPERSON;
|
||||
end;
|
||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put "}" @;
|
||||
%if &_debug ge 131 %then %do;
|
||||
put '>>weboutEND<<';
|
||||
%end;
|
||||
run;
|
||||
%let syscc=0;
|
||||
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
||||
/* refer web service output to file service in one hit */
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json";
|
||||
%let rc=%sysfunc(fcopy(_web,_webout));
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
if symexist('sysprocessmode')
|
||||
then if symget("sysprocessmode")="SAS Stored Process Server"
|
||||
then rc=stpsrvset('program error', 0);
|
||||
run;
|
||||
%end;
|
||||
/**
|
||||
* endsas is reliable but kills some deployments.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
*/
|
||||
%put _all_;
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip(); %macro skippy();';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %do;
|
||||
%put _all_;
|
||||
%abort cancel;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_abort;
|
||||
|
||||
/** @endcond */
|
||||
/** @endcond */
|
||||
|
||||
53
base/mf_dedup.sas
Normal file
53
base/mf_dedup.sas
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
@file
|
||||
@brief de-duplicates a macro string
|
||||
@details Removes all duplicates from a string of words. A delimeter can be
|
||||
chosen. Is inspired heavily by this excellent [macro](
|
||||
https://github.com/scottbass/SAS/blob/master/Macro/dedup_mstring.sas) from
|
||||
[Scott Base](https://www.linkedin.com/in/scottbass). Case sensitive.
|
||||
|
||||
Usage:
|
||||
|
||||
%let str=One two one two and through and through;
|
||||
%put %mf_dedup(&str);
|
||||
%put %mf_dedup(&str,outdlm=%str(,));
|
||||
|
||||
Which returns:
|
||||
|
||||
> One two one and through
|
||||
> One,two,one,and,through
|
||||
|
||||
@param [in] str String to be deduplicated
|
||||
@param [in] indlm= ( ) Delimeter of the input string
|
||||
@param [out] outdlm= ( ) Delimiter of the output string
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_trimstr.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_dedup(str
|
||||
,indlm=%str( )
|
||||
,outdlm=%str( )
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local num word i pos out;
|
||||
|
||||
%* loop over each token, searching the target for that token ;
|
||||
%let num=%sysfunc(countc(%superq(str),%str(&indlm)));
|
||||
%do i=1 %to %eval(&num+1);
|
||||
%let word=%scan(%superq(str),&i,%str(&indlm));
|
||||
%let pos=%sysfunc(indexw(&out,&word,%str(&outdlm)));
|
||||
%if (&pos eq 0) %then %do;
|
||||
%if (&i gt 1) %then %let out=&out%str(&outdlm);
|
||||
%let out=&out&word;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
%unquote(&out)
|
||||
|
||||
%mend mf_dedup;
|
||||
|
||||
|
||||
31
base/mf_deletefile.sas
Normal file
31
base/mf_deletefile.sas
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
@file
|
||||
@brief Deletes a physical file, if it exists
|
||||
@details Usage:
|
||||
|
||||
%mf_writefile(&sasjswork/myfile.txt,l1=some content)
|
||||
|
||||
%mf_deletefile(&sasjswork/myfile.txt)
|
||||
|
||||
%mf_deletefile(&sasjswork/myfile.txt)
|
||||
|
||||
|
||||
@param [in] file Full path to the target file
|
||||
|
||||
@returns The return code from the fdelete() invocation
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_deletefile.test.sas
|
||||
@li mf_writefile.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_deletefile(file
|
||||
)/*/STORE SOURCE*/;
|
||||
%local rc fref;
|
||||
%let rc= %sysfunc(filename(fref,&file));
|
||||
%if %sysfunc(fdelete(&fref)) ne 0 %then %put %sysfunc(sysmsg());
|
||||
%let rc= %sysfunc(filename(fref));
|
||||
%mend mf_deletefile;
|
||||
@@ -10,8 +10,12 @@
|
||||
expected results (depending on whether you 'expect' the result to be
|
||||
case insensitive in this context!)
|
||||
|
||||
@param libds library.dataset
|
||||
@param [in] libds library.dataset
|
||||
@return output returns 1 or 0
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_existds.test.sas
|
||||
|
||||
@warning Untested on tables registered in metadata but not physically present
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -23,4 +27,4 @@
|
||||
%if %sysfunc(exist(&libds)) ne 1 & %sysfunc(exist(&libds,VIEW)) ne 1 %then 0;
|
||||
%else 1;
|
||||
|
||||
%mend;
|
||||
%mend mf_existds;
|
||||
|
||||
@@ -5,23 +5,21 @@
|
||||
Run without arguments to see a list of detectable features.
|
||||
Note - this list is based on known versions of SAS rather than
|
||||
actual feature detection, as that is tricky / impossible to do
|
||||
without generating errors in most cases.
|
||||
without generating errs in most cases.
|
||||
|
||||
%put %mf_existfeature(PROCLUA);
|
||||
|
||||
@param feature the feature to detect. Leave blank to list all in log.
|
||||
@param [in] feature The feature to detect.
|
||||
|
||||
@return output returns 1 or 0 (or -1 if not found)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
|
||||
|
||||
@version 8
|
||||
@author Allan Bowe
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
%macro mf_existfeature(feature
|
||||
)/*/STORE SOURCE*/;
|
||||
%let feature=%upcase(&feature);
|
||||
@@ -29,17 +27,33 @@
|
||||
%let platform=%mf_getplatform();
|
||||
|
||||
%if &feature= %then %do;
|
||||
%put Supported features: PROCLUA;
|
||||
%put No feature was requested for detection;
|
||||
%end;
|
||||
%else %if &feature=COLCONSTRAINTS %then %do;
|
||||
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 0;
|
||||
%else 1;
|
||||
%end;
|
||||
%else %if &feature=PROCLUA %then %do;
|
||||
/* https://blogs.sas.com/content/sasdummy/2015/08/03/using-lua-within-your-sas-programs */
|
||||
%if &platform=SASVIYA %then 1;
|
||||
%else %if "&sysver"="9.3" or "&sysver"="9.4" %then 1;
|
||||
%else %if "&sysver"="9.2" or "&sysver"="9.3" %then 0;
|
||||
%else %if "&SYSVLONG" < "9.04.01M3" %then 0;
|
||||
%else 1;
|
||||
%end;
|
||||
%else %if &feature=DBMS_MEMTYPE %then %do;
|
||||
/* does dbms_memtype exist in dictionary.tables? */
|
||||
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 0;
|
||||
%else 1;
|
||||
%end;
|
||||
%else %if &feature=EXPORTXLS %then %do;
|
||||
/* is it possible to PROC EXPORT an excel file? */
|
||||
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 1;
|
||||
%else %if %sysfunc(sysprod(SAS/ACCESS Interface to PC Files)) = 1 %then 1;
|
||||
%else 0;
|
||||
%end;
|
||||
%else %do;
|
||||
-1
|
||||
%put &sysmacroname: &feature not found;
|
||||
%end;
|
||||
%mend;
|
||||
|
||||
/** @endcond */
|
||||
%mend mf_existfeature;
|
||||
/** @endcond */
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@details You can probably do without this macro as it is just a one liner.
|
||||
Mainly it is here as a convenient way to remember the syntax!
|
||||
|
||||
@param fref the fileref to detect
|
||||
@param [in] fref the fileref to detect
|
||||
|
||||
@return output Returns 1 if found and 0 if not found. Note - it is possible
|
||||
that the fileref is found, but the file does not (yet) exist. If you need
|
||||
@@ -17,11 +17,17 @@
|
||||
%macro mf_existfileref(fref
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if %sysfunc(fileref(&fref))=0 %then %do;
|
||||
%local rc;
|
||||
%let rc=%sysfunc(fileref(&fref));
|
||||
%if &rc=0 %then %do;
|
||||
1
|
||||
%end;
|
||||
%else %if &rc<0 %then %do;
|
||||
%put &sysmacroname: Fileref &fref exists but the underlying file does not;
|
||||
1
|
||||
%end;
|
||||
%else %do;
|
||||
0
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mf_existfileref;
|
||||
|
||||
37
base/mf_existfunction.sas
Normal file
37
base/mf_existfunction.sas
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
@file
|
||||
@brief Checks if a function exists
|
||||
@details Returns 1 if the function exists, else 0. Note that this function
|
||||
can be slow as it needs to open the sashelp.vfuncs table.
|
||||
|
||||
Usage:
|
||||
|
||||
%put %mf_existfunction(CAT);
|
||||
%put %mf_existfunction(DOG);
|
||||
|
||||
Full credit to [Bart](https://sasensei.com/user/305) for the vfunc pointer
|
||||
and the tidy approach for pure macro data set filtering.
|
||||
Check out his [SAS Packages](https://github.com/yabwon/SAS_PACKAGES)
|
||||
framework! Where you can find the same [function](
|
||||
https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionexists-macro
|
||||
).
|
||||
|
||||
@param [in] name function name
|
||||
|
||||
@author Allan Bowe
|
||||
**/
|
||||
/** @cond */
|
||||
%macro mf_existfunction(name
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local dsid rc exist;
|
||||
%let dsid=%sysfunc(open(sashelp.vfunc(where=(fncname="%upcase(&name)"))));
|
||||
%let exist=1;
|
||||
%let exist=%sysfunc(fetch(&dsid, NOSET));
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
|
||||
%sysevalf(0 = &exist)
|
||||
|
||||
%mend mf_existfunction;
|
||||
|
||||
/** @endcond */
|
||||
@@ -1,14 +1,18 @@
|
||||
/**
|
||||
@file
|
||||
@brief Checks if a variable exists in a data set.
|
||||
@details Returns 0 if the variable does NOT exist, and return the position of
|
||||
the var if it does.
|
||||
Usage:
|
||||
@details Returns 0 if the variable does NOT exist, and the position of the var
|
||||
if it does.
|
||||
Usage:
|
||||
|
||||
%put %mf_existvar(work.someds, somevar)
|
||||
%put %mf_existvar(work.someds, somevar)
|
||||
|
||||
@param [in] libds 2 part dataset or view reference
|
||||
@param [in] var variable name
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_existvar.test.sas
|
||||
|
||||
@param libds (positional) - 2 part dataset or view reference
|
||||
@param var (positional) - variable name
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
@@ -21,15 +25,19 @@
|
||||
%local dsid rc;
|
||||
%let dsid=%sysfunc(open(&libds,is));
|
||||
|
||||
%if &dsid=0 or %length(&var)=0 %then %do;
|
||||
%if &dsid=0 %then %do;
|
||||
%put %sysfunc(sysmsg());
|
||||
0
|
||||
0
|
||||
%end;
|
||||
%else %if %length(&var)=0 %then %do;
|
||||
0
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %do;
|
||||
%sysfunc(varnum(&dsid,&var))
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%sysfunc(varnum(&dsid,&var))
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mf_existvar;
|
||||
|
||||
/** @endcond */
|
||||
@@ -2,15 +2,12 @@
|
||||
@file
|
||||
@brief Checks if a set of variables ALL exist in a data set.
|
||||
@details Returns 0 if ANY of the variables do not exist, or 1 if they ALL do.
|
||||
Usage:
|
||||
Usage:
|
||||
|
||||
%put %mf_existVarList(sashelp.class, age sex name dummyvar)
|
||||
%put %mf_existVarList(sashelp.class, age sex name dummyvar);
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_abort.sas
|
||||
|
||||
@param libds 2 part dataset or view reference
|
||||
@param varlist space separated variable names
|
||||
@param [in] libds 2 part dataset or view reference
|
||||
@param [in] varlist space separated variable names
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -29,7 +26,7 @@
|
||||
%let dsid=%sysfunc(open(&libds,is));
|
||||
|
||||
%if &dsid=0 %then %do;
|
||||
%put WARNING: unable to open &libds in mf_existvarlist (&dsid);
|
||||
%put %str(WARN)ING: unable to open &libds in mf_existvarlist (&dsid);
|
||||
%end;
|
||||
|
||||
%if %sysfunc(attrn(&dsid,NVARS))=0 %then %do;
|
||||
@@ -54,6 +51,6 @@
|
||||
0
|
||||
%put Vars not found: &found;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_existvarlist;
|
||||
|
||||
/** @endcond */
|
||||
42
base/mf_fmtdttm.sas
Normal file
42
base/mf_fmtdttm.sas
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns E8601DT26.6 if compatible else DATETIME19.3
|
||||
@details From our experience in [Data Controller for SAS]
|
||||
(https://datacontroller.io) deployments, the E8601DT26.6 datetime format has
|
||||
the widest support when it comes to pass-through SQL queries.
|
||||
|
||||
However, it is not supported in WPS or early versions of SAS 9 (M3 and below)
|
||||
when used as a datetime literal, eg:
|
||||
|
||||
data _null_;
|
||||
demo="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
demo=;
|
||||
run;
|
||||
|
||||
This macro will therefore return DATEITME19.3 as an alternative format
|
||||
based on the runtime environment so that it can be used in such cases, eg:
|
||||
|
||||
data _null_;
|
||||
demo="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
demo=;
|
||||
run;
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_fmtdttm.test.sas
|
||||
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_fmtdttm(
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if "&sysver"="9.2" or "&sysver"="9.3"
|
||||
or ("&sysver"="9.4" and "%substr(&SYSVLONG,9,1)" le "3")
|
||||
or "%substr(&sysver,1,1)"="4"
|
||||
or "%substr(&sysver,1,1)"="5"
|
||||
%then %do;DATETIME19.3%end;
|
||||
%else %do;E8601DT26.6%end;
|
||||
|
||||
%mend mf_fmtdttm;
|
||||
|
||||
|
||||
79
base/mf_getapploc.sas
Normal file
79
base/mf_getapploc.sas
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns the appLoc from the _program variable
|
||||
@details When working with SASjs apps, web services / tests / jobs are always
|
||||
deployed to a root (app) location in the SAS logical folder tree.
|
||||
|
||||
When building apps for use in other environments, you do not necessarily know
|
||||
where the backend services will be deployed. Therefore a function like this
|
||||
is handy in order to dynamically figure out the appLoc, and enable other
|
||||
services to be connected by a relative reference.
|
||||
|
||||
SASjs apps always have the same immediate substructure (one or more of the
|
||||
following):
|
||||
|
||||
@li /data
|
||||
@li /jobs
|
||||
@li /services
|
||||
@li /tests
|
||||
@li /tests/jobs
|
||||
@li /tests/services
|
||||
@li /tests/macros
|
||||
|
||||
This function works by testing for the existence of any of the above in the
|
||||
automatic _program variable, and returning the part to the left of it.
|
||||
|
||||
Usage:
|
||||
|
||||
%put %mf_getapploc(&_program)
|
||||
|
||||
%put %mf_getapploc(/some/location/services/admin/myservice);
|
||||
%put %mf_getapploc(/some/location/jobs/extract/somejob/);
|
||||
%put %mf_getapploc(/some/location/tests/jobs/somejob/);
|
||||
|
||||
@param [in] pgm The _program value from which to extract the appLoc
|
||||
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getapploc(pgm);
|
||||
%if "&pgm"="" %then %do;
|
||||
%if %symexist(_program) %then %let pgm=&_program;
|
||||
%else %do;
|
||||
%put &sysmacroname: No value provided and no _program variable available;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
%local root;
|
||||
|
||||
/**
|
||||
* First check we are not in the tests/macros folder (which has no subfolders)
|
||||
* or specifically in the testsetup or testteardown services
|
||||
*/
|
||||
%if %index(&pgm,/tests/macros/)
|
||||
or %index(&pgm,/tests/testsetup)
|
||||
or %index(&pgm,/tests/testteardown)
|
||||
%then %do;
|
||||
%let root=%substr(&pgm,1,%index(&pgm,/tests)-1);
|
||||
&root
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Next, move up two levels to avoid matches on subfolder or service name
|
||||
*/
|
||||
%let root=%substr(&pgm,1,%length(&pgm)-%length(%scan(&pgm,-1,/))-1);
|
||||
%let root=%substr(&root,1,%length(&root)-%length(%scan(&root,-1,/))-1);
|
||||
|
||||
%if %index(&root,/tests/) %then %do;
|
||||
%let root=%substr(&root,1,%index(&root,/tests/)-1);
|
||||
%end;
|
||||
%else %if %index(&root,/services) %then %do;
|
||||
%let root=%substr(&root,1,%index(&root,/services)-1);
|
||||
%end;
|
||||
%else %if %index(&root,/jobs) %then %do;
|
||||
%let root=%substr(&root,1,%index(&root,/jobs)-1);
|
||||
%end;
|
||||
%else %put &sysmacroname: Could not find an app location from &pgm;
|
||||
&root
|
||||
%mend mf_getapploc ;
|
||||
@@ -6,24 +6,24 @@
|
||||
%put Dataset label = %mf_getattrc(sashelp.class,LABEL);
|
||||
%put Member Type = %mf_getattrc(sashelp.class,MTYPE);
|
||||
|
||||
@param libds library.dataset
|
||||
@param attr full list in [documentation](
|
||||
@param [in] libds library.dataset
|
||||
@param [in] attr full list in [documentation](
|
||||
https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000147794.htm)
|
||||
@return output returns result of the attrc value supplied, or -1 and log
|
||||
message if error.
|
||||
message if err.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getattrc(
|
||||
libds
|
||||
libds
|
||||
,attr
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dsid rc;
|
||||
%let dsid=%sysfunc(open(&libds,is));
|
||||
%if &dsid = 0 %then %do;
|
||||
%put WARNING: Cannot open %trim(&libds), system message below;
|
||||
%put %str(WARN)ING: Cannot open %trim(&libds), system message below;
|
||||
%put %sysfunc(sysmsg());
|
||||
-1
|
||||
%end;
|
||||
@@ -31,4 +31,4 @@
|
||||
%sysfunc(attrc(&dsid,&attr))
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_getattrc;
|
||||
@@ -6,24 +6,24 @@
|
||||
%put Number of observations=%mf_getattrn(sashelp.class,NLOBS);
|
||||
%put Number of variables = %mf_getattrn(sashelp.class,NVARS);
|
||||
|
||||
@param libds library.dataset
|
||||
@param attr Common values are NLOBS and NVARS, full list in [documentation](
|
||||
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212040.htm)
|
||||
@param [in] libds library.dataset
|
||||
@param [in] attr Common values are NLOBS and NVARS, full list in [documentation](
|
||||
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000212040.htm)
|
||||
@return output returns result of the attrn value supplied, or -1 and log
|
||||
message if error.
|
||||
message if err.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getattrn(
|
||||
libds
|
||||
libds
|
||||
,attr
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dsid rc;
|
||||
%let dsid=%sysfunc(open(&libds,is));
|
||||
%if &dsid = 0 %then %do;
|
||||
%put WARNING: Cannot open %trim(&libds), system message below;
|
||||
%put %str(WARN)ING: Cannot open %trim(&libds), system message below;
|
||||
%put %sysfunc(sysmsg());
|
||||
-1
|
||||
%end;
|
||||
@@ -31,4 +31,4 @@
|
||||
%sysfunc(attrn(&dsid,&attr))
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_getattrn;
|
||||
@@ -12,9 +12,10 @@
|
||||
contributors of Chris Hemedingers blog [post](
|
||||
http://blogs.sas.com/content/sasdummy/2013/06/04/find-a-sas-library-engine/)
|
||||
|
||||
@param libref Library reference (also accepts a 2 level libds ref).
|
||||
@param [in] libref Library reference (also accepts a 2 level libds ref).
|
||||
|
||||
@return output returns the library engine for the FIRST library encountered.
|
||||
@return output returns the library engine (uppercase) for the FIRST library
|
||||
encountered.
|
||||
|
||||
@warning will only return the FIRST library engine - for concatenated
|
||||
libraries, with different engines, inconsistent results may be encountered.
|
||||
@@ -22,6 +23,9 @@
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_getxengine.sas
|
||||
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
@@ -32,7 +36,9 @@
|
||||
/* in case the parameter is a libref.tablename, pull off just the libref */
|
||||
%let libref = %upcase(%scan(&libref, 1, %str(.)));
|
||||
|
||||
%let dsid=%sysfunc(open(sashelp.vlibnam(where=(libname="%upcase(&libref)")),i));
|
||||
%let dsid=%sysfunc(
|
||||
open(sashelp.vlibnam(where=(libname="%upcase(&libref)")),i)
|
||||
);
|
||||
%if (&dsid ^= 0) %then %do;
|
||||
%let engnum=%sysfunc(varnum(&dsid,ENGINE));
|
||||
%let rc=%sysfunc(fetch(&dsid));
|
||||
@@ -41,8 +47,8 @@
|
||||
%let rc= %sysfunc(close(&dsid));
|
||||
%end;
|
||||
|
||||
&engine
|
||||
%upcase(&engine)
|
||||
|
||||
%mend;
|
||||
%mend mf_getengine;
|
||||
|
||||
/** @endcond */
|
||||
@@ -5,18 +5,19 @@
|
||||
|
||||
%put %mf_getfilesize(fpath=C:\temp\myfile.txt);
|
||||
|
||||
or
|
||||
or, provide a libds value as follows:
|
||||
|
||||
data x;do x=1 to 100000;y=x;output;end;run;
|
||||
%put %mf_getfilesize(libds=work.x,format=yes);
|
||||
|
||||
gives:
|
||||
Which gives:
|
||||
|
||||
2mb
|
||||
> 2mb
|
||||
|
||||
@param [in] fpath= Full path and filename. Provide this OR the libds value.
|
||||
@param [in] libds= (0) Library.dataset value (assumes library is BASE engine)
|
||||
@param [in] format= (NO) Set to yes to apply sizekmg. format
|
||||
|
||||
@param fpath= full path and filename. Provide this OR the libds value.
|
||||
@param libds= library.dataset value (assumes library is BASE engine)
|
||||
@param format= set to yes to apply sizekmg. format
|
||||
@returns bytes
|
||||
|
||||
@version 9.2
|
||||
@@ -26,22 +27,38 @@
|
||||
%macro mf_getfilesize(fpath=,libds=0,format=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local rc fid fref bytes dsid lib vnum;
|
||||
|
||||
%if &libds ne 0 %then %do;
|
||||
%let fpath=%sysfunc(pathname(%scan(&libds,1,.)))/%scan(&libds,2,.).sas7bdat;
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
%let dsid=%sysfunc(open(
|
||||
sashelp.vtable(where=(libname="&lib" and memname="%scan(&libds,-1,.)")
|
||||
keep=libname memname filesize
|
||||
)
|
||||
));
|
||||
%if (&dsid ^= 0) %then %do;
|
||||
%let vnum=%sysfunc(varnum(&dsid,FILESIZE));
|
||||
%let rc=%sysfunc(fetch(&dsid));
|
||||
%let bytes=%sysfunc(getvarn(&dsid,&vnum));
|
||||
%let rc= %sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %put &sysmacroname: &libds could not be opened! %sysfunc(sysmsg());
|
||||
%end;
|
||||
%else %do;
|
||||
%let rc=%sysfunc(filename(fref,&fpath));
|
||||
%let fid=%sysfunc(fopen(&fref));
|
||||
%let bytes=%sysfunc(finfo(&fid,File Size (bytes)));
|
||||
%let rc=%sysfunc(fclose(&fid));
|
||||
%let rc=%sysfunc(filename(fref));
|
||||
%end;
|
||||
|
||||
%local rc fid fref bytes;
|
||||
%let rc=%sysfunc(filename(fref,&fpath));
|
||||
%let fid=%sysfunc(fopen(&fref));
|
||||
%let bytes=%sysfunc(finfo(&fid,File Size (bytes)));
|
||||
%let rc=%sysfunc(fclose(&fid));
|
||||
%let rc=%sysfunc(filename(fref));
|
||||
|
||||
%if &format=NO %then %do;
|
||||
&bytes
|
||||
&bytes
|
||||
%end;
|
||||
%else %do;
|
||||
%sysfunc(INPUTN(&bytes, best.),sizekmg.)
|
||||
%end;
|
||||
|
||||
%mend ;
|
||||
%mend mf_getfilesize ;
|
||||
60
base/mf_getfmtlist.sas
Normal file
60
base/mf_getfmtlist.sas
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns a distinct list of formats from a table
|
||||
@details Reads the dataset header and returns a distinct list of formats
|
||||
applied.
|
||||
|
||||
%put NOTE- %mf_getfmtlist(sashelp.prdsale);
|
||||
%put NOTE- %mf_getfmtlist(sashelp.shoes);
|
||||
%put NOTE- %mf_getfmtlist(sashelp.demographics);
|
||||
|
||||
returns:
|
||||
|
||||
DOLLAR $CHAR W MONNAME
|
||||
$CHAR BEST DOLLAR
|
||||
BEST Z $CHAR COMMA PERCENTN
|
||||
|
||||
@param [in] libds Two part library.dataset reference.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getfmtname.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_getfmtlist(libds
|
||||
)/*/STORE SOURCE*/;
|
||||
/* declare local vars */
|
||||
%local out dsid nvars x rc fmt;
|
||||
|
||||
/* open dataset in macro */
|
||||
%let dsid=%sysfunc(open(&libds));
|
||||
|
||||
/* continue if dataset exists */
|
||||
%if &dsid %then %do;
|
||||
/* loop each variable in the dataset */
|
||||
%let nvars=%sysfunc(attrn(&dsid,NVARS));
|
||||
%do x=1 %to &nvars;
|
||||
/* grab format and check it exists */
|
||||
%let fmt=%sysfunc(varfmt(&dsid,&x));
|
||||
%if %quote(&fmt) ne %quote() %then %let fmt=%mf_getfmtname(&fmt);
|
||||
%else %do;
|
||||
/* assign default format depending on variable type */
|
||||
%if %sysfunc(vartype(&dsid, &x))=C %then %let fmt=$CHAR;
|
||||
%else %let fmt=BEST;
|
||||
%end;
|
||||
/* concatenate unique list of formats */
|
||||
%if %sysfunc(indexw(&out,&fmt,%str( )))=0 %then %let out=&out &fmt;
|
||||
%end;
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname: Unable to open &libds (rc=&dsid);
|
||||
%put &sysmacroname: SYSMSG= %sysfunc(sysmsg());
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
/* send them out without spaces or quote markers */
|
||||
%do;%unquote(&out)%end;
|
||||
%mend mf_getfmtlist;
|
||||
44
base/mf_getfmtname.sas
Normal file
44
base/mf_getfmtname.sas
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
@file
|
||||
@brief Extracts a format name from a fully defined format
|
||||
@details Converts formats in like $thi3. and th13.2 $THI and TH.
|
||||
Usage:
|
||||
|
||||
%put %mf_getfmtname(8.);
|
||||
%put %mf_getfmtname($4.);
|
||||
%put %mf_getfmtname(comma14.10);
|
||||
|
||||
Returns:
|
||||
|
||||
> W
|
||||
> $CHAR
|
||||
> COMMA
|
||||
|
||||
Note that system defaults are inferred from the values provided.
|
||||
|
||||
@param [in] fmt The fully defined format. If left blank, nothing is returned.
|
||||
|
||||
@returns The name (without width or decimal) of the format.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_getfmtname(fmt
|
||||
)/*/STORE SOURCE*/ /minoperator mindelimiter=' ';
|
||||
|
||||
%local out dsid nvars x rc fmt;
|
||||
|
||||
/* extract actual format name from the format definition */
|
||||
%let fmt=%scan(&fmt,1,.);
|
||||
%do %while(%substr(&fmt,%length(&fmt),1) in 1 2 3 4 5 6 7 8 9 0);
|
||||
%if %length(&fmt)=1 %then %let fmt=W;
|
||||
%else %let fmt=%substr(&fmt,1,%length(&fmt)-1);
|
||||
%end;
|
||||
|
||||
%if &fmt=$ %then %let fmt=$CHAR;
|
||||
|
||||
/* send them out without spaces or quote markers */
|
||||
%do;%unquote(%upcase(&fmt))%end;
|
||||
%mend mf_getfmtname;
|
||||
37
base/mf_getgitbranch.sas
Normal file
37
base/mf_getgitbranch.sas
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
@file
|
||||
@brief Retrieves the current branch from a local GIT repo
|
||||
@details In a local git repository, the current branch is always available in
|
||||
the `.git/HEAD` file in a format like this: `ref: refs/heads/master`
|
||||
|
||||
This macro simply reads the file and returns the last word (eg `master`).
|
||||
|
||||
Example usage:
|
||||
|
||||
%let gitdir=%sysfunc(pathname(work))/core;
|
||||
%let repo=https://github.com/sasjs/core;
|
||||
%put source clone rc=%sysfunc(GITFN_CLONE(&repo,&gitdir));
|
||||
|
||||
%put The current branch is %mf_getgitbranch(&gitdir);
|
||||
|
||||
@param [in] gitdir The directory containing the GIT repository
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_readfile.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_gitadd.sas
|
||||
@li mp_gitlog.sas
|
||||
@li mp_gitreleaseinfo.sas
|
||||
@li mp_gitstatus.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getgitbranch(gitdir
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%scan(%mf_readfile(&gitdir/.git/HEAD),-1)
|
||||
|
||||
%mend mf_getgitbranch;
|
||||
@@ -3,12 +3,13 @@
|
||||
@brief retrieves a key value pair from a control dataset
|
||||
@details By default, control dataset is work.mp_setkeyvalue. Usage:
|
||||
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%put %mf_getkeyvalue(someindex)
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%put %mf_getkeyvalue(someindex)
|
||||
|
||||
|
||||
@param key Provide a key on which to perform the lookup
|
||||
@param libds= define the target table which holds the parameters
|
||||
@param [in] key Provide a key on which to perform the lookup
|
||||
@param [in] libds= (work.mp_setkeyvalue) The library.dataset which holds the
|
||||
parameters
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -16,7 +17,7 @@
|
||||
|
||||
%macro mf_getkeyvalue(key,libds=work.mp_setkeyvalue
|
||||
)/*/STORE SOURCE*/;
|
||||
%local ds dsid key valc valn type rc;
|
||||
%local ds dsid key valc valn type rc;
|
||||
%let dsid=%sysfunc(open(&libds(where=(key="&key"))));
|
||||
%syscall set(dsid);
|
||||
%let rc = %sysfunc(fetch(&dsid));
|
||||
@@ -29,4 +30,4 @@
|
||||
&valc
|
||||
%end;
|
||||
%else %put %str(ERR)OR: Unable to find key &key in ds &libds;
|
||||
%mend;
|
||||
%mend mf_getkeyvalue;
|
||||
@@ -5,10 +5,14 @@
|
||||
|
||||
%put %mf_getplatform();
|
||||
|
||||
returns:
|
||||
SASMETA (or SASVIYA)
|
||||
returns one of:
|
||||
|
||||
@param switch the param for which to return a platform specific variable
|
||||
@li SASMETA
|
||||
@li SASVIYA
|
||||
@li SASJS
|
||||
@li BASESAS
|
||||
|
||||
@param [in] switch the param for which to return a platform specific variable
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_mval.sas
|
||||
@@ -22,26 +26,34 @@
|
||||
)/*/STORE SOURCE*/;
|
||||
%local a b c;
|
||||
%if &switch.NONE=NONE %then %do;
|
||||
%if %symexist(sasjsprocessmode) %then %do;
|
||||
%if &sasjsprocessmode=Stored Program %then %do;
|
||||
SASJS
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
%if %symexist(sysprocessmode) %then %do;
|
||||
%if "&sysprocessmode"="SAS Object Server"
|
||||
%if "&sysprocessmode"="SAS Object Server"
|
||||
or "&sysprocessmode"= "SAS Compute Server" %then %do;
|
||||
SASVIYA
|
||||
%end;
|
||||
%else %if "&sysprocessmode"="SAS Stored Process Server" %then %do;
|
||||
%else %if "&sysprocessmode"="SAS Stored Process Server"
|
||||
or "&sysprocessmode"="SAS Workspace Server"
|
||||
%then %do;
|
||||
SASMETA
|
||||
%return;
|
||||
%end;
|
||||
%else %do;
|
||||
SAS
|
||||
BASESAS
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
%else %if %symexist(_metaport) %then %do;
|
||||
%else %if %symexist(_metaport) or %symexist(_metauser) %then %do;
|
||||
SASMETA
|
||||
%return;
|
||||
%end;
|
||||
%else %do;
|
||||
SAS
|
||||
BASESAS
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
@@ -60,4 +72,4 @@
|
||||
%else %if &switch=VIYARESTAPI %then %do;
|
||||
%mf_trimstr(%sysfunc(getoption(servicesbaseurl)),/)
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_getplatform;
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
for:
|
||||
> "these","words","are","double","quoted"
|
||||
|
||||
@param in_str the unquoted, spaced delimited string to transform
|
||||
@param dlm= the delimeter to be applied to the output (default comma)
|
||||
@param indlm= the delimeter used for the input (default is space)
|
||||
@param quote= the quote mark to apply (S=Single, D=Double). If any other value
|
||||
than uppercase S or D is supplied, then that value will be used as the
|
||||
quoting character.
|
||||
@param [in] in_str The unquoted, spaced delimited string to transform
|
||||
@param [in] dlm= (,) The delimeter to be applied to the output (default comma)
|
||||
@param [in] indlm= ( ) The delimeter used for the input (default is space)
|
||||
@param [in] quote= (S) The quote mark to apply (S=Single, D=Double, N=None).
|
||||
If any other value than uppercase S or D is supplied, then that value will
|
||||
be used as the quoting character.
|
||||
@return output returns a string with the newly quoted / delimited output.
|
||||
|
||||
@version 9.2
|
||||
@@ -28,11 +28,15 @@
|
||||
**/
|
||||
|
||||
|
||||
%macro mf_getquotedstr(IN_STR,DLM=%str(,),QUOTE=S,indlm=%str( )
|
||||
%macro mf_getquotedstr(IN_STR
|
||||
,DLM=%str(,)
|
||||
,QUOTE=S
|
||||
,indlm=%str( )
|
||||
)/*/STORE SOURCE*/;
|
||||
%if "e=S %then %let quote=%str(%');
|
||||
%else %if "e=D %then %let quote=%str(%");
|
||||
%else %let quote=%str();
|
||||
/* credit Rowland Hale - byte34 is double quote, 39 is single quote */
|
||||
%if "e=S %then %let quote=%qsysfunc(byte(39));
|
||||
%else %if "e=D %then %let quote=%qsysfunc(byte(34));
|
||||
%else %if "e=N %then %let quote=;
|
||||
%local i item buffer;
|
||||
%let i=1;
|
||||
%do %while (%qscan(&IN_STR,&i,%str(&indlm)) ne %str() ) ;
|
||||
@@ -50,4 +54,4 @@
|
||||
|
||||
&buffer
|
||||
|
||||
%mend;
|
||||
%mend mf_getquotedstr;
|
||||
@@ -8,7 +8,7 @@
|
||||
returns:
|
||||
> dbo
|
||||
|
||||
@param libref Library reference (also accepts a 2 level libds ref).
|
||||
@param [in] libref Library reference (also accepts a 2 level libds ref).
|
||||
|
||||
@return output returns the library schema for the FIRST library encountered
|
||||
|
||||
@@ -38,6 +38,6 @@
|
||||
|
||||
&schema
|
||||
|
||||
%mend;
|
||||
%mend mf_getschema;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
@@ -1,37 +1,60 @@
|
||||
/**
|
||||
@file
|
||||
@brief Assigns and returns an unused fileref
|
||||
@details
|
||||
@details Using the native approach for assigning filerefs fails as some
|
||||
procedures (such as proc http) do not recognise the temporary names (starting
|
||||
with a hash), returning a message such as:
|
||||
|
||||
> ERROR 22-322: Expecting a name.
|
||||
|
||||
This macro works by attempting a random fileref (with a prefix), seeing if it
|
||||
is already assigned, and if not - returning the fileref.
|
||||
|
||||
If your process can accept filerefs with the hash (#) prefix, then set
|
||||
`prefix=0` to revert to the native approach - which is significantly faster
|
||||
when there are a lot of filerefs in a session.
|
||||
|
||||
Use as follows:
|
||||
|
||||
%let fileref1=%mf_getuniquefileref();
|
||||
%let fileref2=%mf_getuniquefileref();
|
||||
%let fileref2=%mf_getuniquefileref(prefix=0);
|
||||
%put &fileref1 &fileref2;
|
||||
|
||||
which returns:
|
||||
which returns filerefs similar to:
|
||||
|
||||
> mcref0 mcref1
|
||||
> _7432233 #LN00070
|
||||
|
||||
@param prefix= first part of fileref. Remember that filerefs can only be 8
|
||||
characters, so a 7 letter prefix would mean that `maxtries` should be 10.
|
||||
@param maxtries= the last part of the libref. Provide an integer value.
|
||||
@param [in] prefix= (_) first part of fileref. Remember that filerefs can only
|
||||
be 8 characters, so a 7 letter prefix would mean `maxtries` should be 10.
|
||||
if using zero (0) as the prefix, a native assignment is used.
|
||||
@param [in] maxtries= (1000) the last part of the libref. Must be an integer.
|
||||
@param [in] lrecl= (32767) Provide a default lrecl with which to initialise
|
||||
the generated fileref.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getuniquefileref(prefix=mcref,maxtries=1000);
|
||||
%local x fname;
|
||||
%let x=0;
|
||||
%do x=0 %to &maxtries;
|
||||
%if %sysfunc(fileref(&prefix&x)) > 0 %then %do;
|
||||
%let fname=&prefix&x;
|
||||
%let rc=%sysfunc(filename(fname,,temp));
|
||||
%macro mf_getuniquefileref(prefix=_,maxtries=1000,lrecl=32767);
|
||||
%local rc fname;
|
||||
%if &prefix=0 %then %do;
|
||||
%let rc=%sysfunc(filename(fname,,temp,lrecl=&lrecl));
|
||||
%if &rc %then %put %sysfunc(sysmsg());
|
||||
&prefix&x
|
||||
%*put &sysmacroname: Fileref &prefix&x was assigned and returned;
|
||||
%return;
|
||||
&fname
|
||||
%end;
|
||||
%else %do;
|
||||
%local x len;
|
||||
%let len=%eval(8-%length(&prefix));
|
||||
%let x=0;
|
||||
%do x=0 %to &maxtries;
|
||||
%let fname=&prefix%substr(%sysfunc(ranuni(0)),3,&len);
|
||||
%if %sysfunc(fileref(&fname)) > 0 %then %do;
|
||||
%let rc=%sysfunc(filename(fname,,temp,lrecl=&lrecl));
|
||||
%if &rc %then %put %sysfunc(sysmsg());
|
||||
&fname
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
%put unable to find available fileref after &maxtries attempts;
|
||||
%end;
|
||||
%put unable to find available fileref in range &prefix.0-&maxtries;
|
||||
%mend;
|
||||
%mend mf_getuniquefileref;
|
||||
@@ -3,38 +3,55 @@
|
||||
@brief Returns an unused libref
|
||||
@details Use as follows:
|
||||
|
||||
libname mclib0 (work);
|
||||
libname mclib1 (work);
|
||||
libname mclib2 (work);
|
||||
libname mclib0 (work);
|
||||
libname mclib1 (work);
|
||||
libname mclib2 (work);
|
||||
|
||||
%let libref=%mf_getuniquelibref();
|
||||
%put &=libref;
|
||||
%let libref=%mf_getuniquelibref();
|
||||
%put &=libref;
|
||||
|
||||
which returns:
|
||||
|
||||
> mclib3
|
||||
|
||||
@param prefix= first part of libref. Remember that librefs can only be 8 characters,
|
||||
so a 7 letter prefix would mean that maxtries should be 10.
|
||||
@param maxtries= the last part of the libref. Provide an integer value.
|
||||
A blank value is returned if no usable libname is determined.
|
||||
|
||||
@param [in] prefix= (mclib) first part of the returned libref. As librefs can
|
||||
be as long as 8 characters, a maximum length of 7 characters is premitted
|
||||
for this prefix.
|
||||
@param [in] maxtries= (1000) Deprecated parameter. Remains here to ensure a
|
||||
non-breaking change. Will be removed in v5.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getuniquelibref(prefix=mc,maxtries=1000);
|
||||
%local x;
|
||||
|
||||
%macro mf_getuniquelibref(prefix=mclib,maxtries=1000);
|
||||
%local x libref;
|
||||
%let x=0;
|
||||
%do x=0 %to &maxtries;
|
||||
%if %sysfunc(libref(&prefix&x)) ne 0 %then %do;
|
||||
%let libref=&prefix&x;
|
||||
%let rc=%sysfunc(libname(&libref,%sysfunc(pathname(work))));
|
||||
%if &rc %then %put %sysfunc(sysmsg());
|
||||
&prefix&x
|
||||
%*put &sysmacroname: Libref &libref assigned as WORK and returned;
|
||||
%if ( %length(&prefix) gt 7 ) %then %do;
|
||||
%put %str(ERR)OR: The prefix parameter cannot exceed 7 characters.;
|
||||
0
|
||||
%return;
|
||||
%end;
|
||||
%else %if (%sysfunc(NVALID(&prefix,v7))=0) %then %do;
|
||||
%put %str(ERR)OR: Invalid prefix (&prefix);
|
||||
0
|
||||
%return;
|
||||
%end;
|
||||
%put unable to find available libref in range &prefix.0-&maxtries;
|
||||
%mend;
|
||||
|
||||
/* Set maxtries equal to '10 to the power of [# unused characters] - 1' */
|
||||
%let maxtries=%eval(10**(8-%length(&prefix))-1);
|
||||
|
||||
%do x = 0 %to &maxtries;
|
||||
%if %sysfunc(libref(&prefix&x)) ne 0 %then %do;
|
||||
&prefix&x
|
||||
%return;
|
||||
%end;
|
||||
%let x = %eval(&x + 1);
|
||||
%end;
|
||||
|
||||
%put %str(ERR)OR: No usable libref in range &prefix.0-&maxtries;
|
||||
%put %str(ERR)OR- Try reducing the prefix or deleting some libraries!;
|
||||
0
|
||||
%mend mf_getuniquelibref;
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
> MCc59c750610321d4c8bf75faadbcd22
|
||||
|
||||
@param prefix= set a prefix for the new name
|
||||
@param [in] prefix= (MC) Sets a prefix for the new name
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
@@ -18,5 +18,5 @@
|
||||
|
||||
|
||||
%macro mf_getuniquename(prefix=MC);
|
||||
&prefix.%substr(%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32-%length(&prefix))
|
||||
%mend;
|
||||
&prefix.%substr(%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32-%length(&prefix))
|
||||
%mend mf_getuniquename;
|
||||
@@ -2,7 +2,7 @@
|
||||
@file
|
||||
@brief Returns a userid according to session context
|
||||
@details In a workspace session, a user is generally represented by <code>
|
||||
&sysuserid</code> or <code>SYS_COMPUTE_SESSION_OWNER</code> if it exists.
|
||||
&sysuserid</code> or <code>SYS_COMPUTE_SESSION_OWNER</code> if it exists.
|
||||
In a Stored Process session, <code>&sysuserid</code>
|
||||
resolves to a system account (default=sassrv) and instead there are several
|
||||
metadata username variables to choose from (_metauser, _metaperson
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
%let user= %mf_getUser();
|
||||
%put &user;
|
||||
|
||||
@param type - do not use, may be deprecated in a future release
|
||||
|
||||
@return SYSUSERID (if workspace server)
|
||||
@return _METAPERSON (if stored process server)
|
||||
@@ -23,20 +21,22 @@
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getuser(type=META
|
||||
%macro mf_getuser(
|
||||
)/*/STORE SOURCE*/;
|
||||
%local user metavar;
|
||||
%if &type=OS %then %let metavar=_secureusername;
|
||||
%else %let metavar=_metaperson;
|
||||
%local user;
|
||||
|
||||
%if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER;
|
||||
%else %if %symexist(&metavar) %then %do;
|
||||
%if %length(&&&metavar)=0 %then %let user=&sysuserid;
|
||||
%if %symexist(_sasjs_username) %then %let user=&_sasjs_username;
|
||||
%else %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %do;
|
||||
%let user=&SYS_COMPUTE_SESSION_OWNER;
|
||||
%end;
|
||||
%else %if %symexist(_metaperson) %then %do;
|
||||
%if %length(&_metaperson)=0 %then %let user=&sysuserid;
|
||||
/* sometimes SAS will add @domain extension - remove for consistency */
|
||||
%else %let user=%scan(&&&metavar,1,@);
|
||||
/* but be sure to quote in case of usernames with commas */
|
||||
%else %let user=%unquote(%scan(%quote(&_metaperson),1,@));
|
||||
%end;
|
||||
%else %let user=&sysuserid;
|
||||
|
||||
%quote(&user)
|
||||
|
||||
%mend;
|
||||
%mend mf_getuser;
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getattrn.sas
|
||||
|
||||
@param libds dataset to query
|
||||
@param variable the variable which contains the value to return.
|
||||
@param filter contents of where clause
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_setkeyvalue.sas
|
||||
|
||||
@param [in] libds dataset to query
|
||||
@param [in] variable the variable which contains the value to return.
|
||||
@param [in] filter= (1) contents of where clause
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -20,7 +23,7 @@
|
||||
|
||||
%macro mf_getvalue(libds,variable,filter=1
|
||||
)/*/STORE SOURCE*/;
|
||||
%if %mf_getattrn(&libds,NLOBS)>0 %then %do;
|
||||
%if %mf_getattrn(&libds,NLOBS)>0 %then %do;
|
||||
%local dsid rc &variable;
|
||||
%let dsid=%sysfunc(open(&libds(where=(&filter))));
|
||||
%syscall set(dsid);
|
||||
@@ -30,4 +33,4 @@
|
||||
%trim(&&&variable)
|
||||
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_getvalue;
|
||||
@@ -2,31 +2,48 @@
|
||||
@file
|
||||
@brief Returns number of variables in a dataset
|
||||
@details Useful to identify those renagade datasets that have no columns!
|
||||
Can also be used to count for numeric, or character columns
|
||||
|
||||
%put Number of Variables=%mf_getvarcount(sashelp.class);
|
||||
%put Number of Variables=%mf_getvarcount(sashelp.class);
|
||||
%put Character Variables=%mf_getvarcount(sashelp.class,typefilter=C);
|
||||
%put Numeric Variables = %mf_getvarcount(sashelp.class,typefilter=N);
|
||||
|
||||
returns:
|
||||
> Number of Variables=4
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] typefilter= (A) Filter for certain types of column. Valid values:
|
||||
@li A Count All columns
|
||||
@li C Count Character columns only
|
||||
@li N Count Numeric columns only
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_getvarcount(libds
|
||||
%macro mf_getvarcount(libds,typefilter=A
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dsid nvars rc ;
|
||||
%local dsid nvars rc outcnt x;
|
||||
%let dsid=%sysfunc(open(&libds));
|
||||
%let nvars=.;
|
||||
%let outcnt=0;
|
||||
%let typefilter=%upcase(&typefilter);
|
||||
%if &dsid %then %do;
|
||||
%let nvars=%sysfunc(attrn(&dsid,NVARS));
|
||||
%if &typefilter=A %then %let outcnt=&nvars;
|
||||
%else %if &nvars>0 %then %do x=1 %to &nvars;
|
||||
/* increment based on variable type */
|
||||
%if %sysfunc(vartype(&dsid,&x))=&typefilter %then %do;
|
||||
%let outcnt=%eval(&outcnt+1);
|
||||
%end;
|
||||
%end;
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %do;
|
||||
%put unable to open &libds (rc=&dsid);
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
&nvars
|
||||
%mend;
|
||||
&outcnt
|
||||
%mend mf_getvarcount;
|
||||
@@ -5,9 +5,9 @@
|
||||
Usage:
|
||||
|
||||
data test;
|
||||
format str1 $1. num1 datetime19.;
|
||||
str2='hello mum!'; num2=666;
|
||||
stop;
|
||||
format str1 $1. num1 datetime19.;
|
||||
str2='hello mum!'; num2=666;
|
||||
stop;
|
||||
run;
|
||||
%put %mf_getVarFormat(test,str1);
|
||||
%put %mf_getVarFormat(work.test,num1);
|
||||
@@ -23,9 +23,10 @@
|
||||
8.
|
||||
NOTE: Variable renegade does not exist in test
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param var Variable name for which a format should be returned
|
||||
@param force Set to 1 to supply a default if the variable has no format
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] var Variable name for which a format should be returned
|
||||
@param [in] force= (0) Set to 1 to supply a default if the variable has no
|
||||
format
|
||||
@returns outputs format
|
||||
|
||||
@author Allan Bowe
|
||||
@@ -45,13 +46,14 @@
|
||||
/* Get variable format */
|
||||
%if(&vnum > 0) %then %let vformat=%sysfunc(varfmt(&dsid, &vnum));
|
||||
%else %do;
|
||||
%put NOTE: Variable &var does not exist in &libds;
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
%return;
|
||||
%put NOTE: Variable &var does not exist in &libds;
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
@@ -60,7 +62,7 @@
|
||||
%let vlen = %sysfunc(varlen(&dsid, &vnum));
|
||||
%let vtype = %sysfunc(vartype(&dsid, &vnum.));
|
||||
%if &vtype=C %then %let vformat=$&vlen..;
|
||||
%else %let vformat=8.;
|
||||
%else %let vformat=best.;
|
||||
%end;
|
||||
|
||||
|
||||
@@ -68,4 +70,4 @@
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
/* Return variable format */
|
||||
&vformat
|
||||
%mend;
|
||||
%mend mf_getVarFormat;
|
||||
@@ -5,8 +5,8 @@
|
||||
Usage:
|
||||
|
||||
data test;
|
||||
format str $1. num datetime19.;
|
||||
stop;
|
||||
format str $1. num datetime19.;
|
||||
stop;
|
||||
run;
|
||||
%put %mf_getVarLen(test,str);
|
||||
%put %mf_getVarLen(work.test,num);
|
||||
@@ -18,8 +18,8 @@
|
||||
8
|
||||
NOTE: Variable renegade does not exist in test
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param var Variable name for which a length should be returned
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] var Variable name for which a length should be returned
|
||||
@returns outputs length
|
||||
|
||||
@author Allan Bowe
|
||||
@@ -39,14 +39,18 @@
|
||||
/* Get variable format */
|
||||
%if(&vnum > 0) %then %let vlen = %sysfunc(varlen(&dsid, &vnum));
|
||||
%else %do;
|
||||
%put NOTE: Variable &var does not exist in &libds;
|
||||
%let vlen = %str( );
|
||||
%put NOTE: Variable &var does not exist in &libds;
|
||||
%let vlen = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &libds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
/* Return variable format */
|
||||
&vlen
|
||||
%mend;
|
||||
%mend mf_getVarLen;
|
||||
@@ -10,14 +10,21 @@
|
||||
returns:
|
||||
> List of Variables=Name Sex Age Height Weight
|
||||
|
||||
For a seperated list of column values:
|
||||
|
||||
%put %mf_getvarlist(sashelp.class,dlm=%str(,),quote=double);
|
||||
|
||||
returns:
|
||||
> "Name","Sex","Age","Height","Weight"
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param dlm= provide a delimiter (eg comma or space) to separate the vars
|
||||
@param quote= use either DOUBLE or SINGLE to quote the results
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] dlm= ( ) Provide a delimiter (eg comma or space) to separate the
|
||||
variables
|
||||
@param [in] quote= (none) use either DOUBLE or SINGLE to quote the results
|
||||
@param [in] typefilter= (A) Filter for certain types of column. Valid values:
|
||||
@li A Return All columns
|
||||
@li C Return Character columns
|
||||
@li N Return Numeric columns
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -27,9 +34,10 @@
|
||||
%macro mf_getvarlist(libds
|
||||
,dlm=%str( )
|
||||
,quote=no
|
||||
,typefilter=A
|
||||
)/*/STORE SOURCE*/;
|
||||
/* declare local vars */
|
||||
%local outvar dsid nvars x rc dlm q var;
|
||||
%local outvar dsid nvars x rc dlm q var vtype;
|
||||
|
||||
/* credit Rowland Hale - byte34 is double quote, 39 is single quote */
|
||||
%if %upcase("e)=DOUBLE %then %let q=%qsysfunc(byte(34));
|
||||
@@ -37,28 +45,30 @@
|
||||
/* open dataset in macro */
|
||||
%let dsid=%sysfunc(open(&libds));
|
||||
|
||||
|
||||
%if &dsid %then %do;
|
||||
%let nvars=%sysfunc(attrn(&dsid,NVARS));
|
||||
%if &nvars>0 %then %do;
|
||||
/* add first dataset variable to global macro variable */
|
||||
%let outvar=&q.%sysfunc(varname(&dsid,1))&q.;
|
||||
/* add remaining variables with supplied delimeter */
|
||||
/* add variables with supplied delimeter */
|
||||
%do x=1 %to &nvars;
|
||||
%let var=&q.%sysfunc(varname(&dsid,&x))&q.;
|
||||
%if &var=&q&q %then %do;
|
||||
%put &sysmacroname: Empty column found in &libds!;
|
||||
%let var=&q. &q.;
|
||||
/* get variable type */
|
||||
%let vtype=%sysfunc(vartype(&dsid,&x));
|
||||
%if &vtype=&typefilter or &typefilter=A %then %do;
|
||||
%let var=&q.%sysfunc(varname(&dsid,&x))&q.;
|
||||
%if &var=&q&q %then %do;
|
||||
%put &sysmacroname: Empty column found in &libds!;
|
||||
%let var=&q. &q.;
|
||||
%end;
|
||||
%if %quote(&outvar)=%quote() %then %let outvar=&var;
|
||||
%else %let outvar=&outvar.&dlm.&var.;
|
||||
%end;
|
||||
%if &x=1 %then %let outvar=&var;
|
||||
%else %let outvar=&outvar.&dlm.&var.;
|
||||
%end;
|
||||
%end;
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %do;
|
||||
%put unable to open &libds (rc=&dsid);
|
||||
%put &sysmacroname: Unable to open &libds (rc=&dsid);
|
||||
%put &sysmacroname: SYSMSG= %sysfunc(sysmsg());
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
&outvar
|
||||
%mend;
|
||||
%do;%unquote(&outvar)%end;
|
||||
%mend mf_getvarlist;
|
||||
@@ -6,8 +6,8 @@
|
||||
Usage:
|
||||
|
||||
data work.test;
|
||||
format str $1. num datetime19.;
|
||||
stop;
|
||||
format str $1. num datetime19.;
|
||||
stop;
|
||||
run;
|
||||
%put %mf_getVarNum(work.test,str);
|
||||
%put %mf_getVarNum(work.test,num);
|
||||
@@ -21,8 +21,8 @@ returns:
|
||||
|
||||
> NOTE: Variable renegade does not exist in test
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param var Variable name for which a position should be returned
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] var Variable name for which a position should be returned
|
||||
|
||||
@author Allan Bowe
|
||||
@version 9.2
|
||||
@@ -39,11 +39,15 @@ returns:
|
||||
/* Get variable number */
|
||||
%let vnum = %sysfunc(varnum(&dsid, &var));
|
||||
%if(&vnum <= 0) %then %do;
|
||||
%put NOTE: Variable &var does not exist in &libds;
|
||||
%let vnum = %str( );
|
||||
%put NOTE: Variable &var does not exist in &libds;
|
||||
%let vnum = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &ds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
@@ -51,4 +55,4 @@ returns:
|
||||
/* Return variable number */
|
||||
&vnum.
|
||||
|
||||
%mend;
|
||||
%mend mf_getVarNum;
|
||||
@@ -5,16 +5,16 @@
|
||||
Usage:
|
||||
|
||||
data test;
|
||||
length str $1. num 8.;
|
||||
stop;
|
||||
length str $1. num 8.;
|
||||
stop;
|
||||
run;
|
||||
%put %mf_getvartype(test,str);
|
||||
%put %mf_getvartype(work.test,num);
|
||||
|
||||
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param var the variable name to be checked
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] var the variable name to be checked
|
||||
@return output returns C or N depending on variable type. If variable
|
||||
does not exist then a blank is returned and a note is written to the log.
|
||||
|
||||
@@ -35,14 +35,18 @@ Usage:
|
||||
/* Get variable type (C/N) */
|
||||
%if(&vnum. > 0) %then %let vtype = %sysfunc(vartype(&dsid, &vnum.));
|
||||
%else %do;
|
||||
%put NOTE: Variable &var does not exist in &libds;
|
||||
%let vtype = %str( );
|
||||
%put NOTE: Variable &var does not exist in &libds;
|
||||
%let vtype = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &libds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
/* Return variable type */
|
||||
&vtype
|
||||
%mend;
|
||||
%mend mf_getvartype;
|
||||
43
base/mf_getxengine.sas
Normal file
43
base/mf_getxengine.sas
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns the engine type of a SAS fileref
|
||||
@details Queries sashelp.vextfl to get the xengine value.
|
||||
Usage:
|
||||
|
||||
filename feng temp;
|
||||
%put %mf_getxengine(feng);
|
||||
|
||||
returns:
|
||||
> TEMP
|
||||
|
||||
@param [in] fref The fileref to check
|
||||
|
||||
@returns The XENGINE value in sashelp.vextfl or 0 if not found.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_getengine.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_getxengine(fref
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dsid engnum rc engine;
|
||||
|
||||
%let dsid=%sysfunc(
|
||||
open(sashelp.vextfl(where=(fileref="%upcase(&fref)")),i)
|
||||
);
|
||||
%if (&dsid ^= 0) %then %do;
|
||||
%let engnum=%sysfunc(varnum(&dsid,XENGINE));
|
||||
%let rc=%sysfunc(fetch(&dsid));
|
||||
%let engine=%sysfunc(getvarc(&dsid,&engnum));
|
||||
%* put &fref. ENGINE is &engine.;
|
||||
%let rc= %sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %let engine=0;
|
||||
|
||||
&engine
|
||||
|
||||
%mend mf_getxengine;
|
||||
29
base/mf_increment.sas
Normal file
29
base/mf_increment.sas
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
@file
|
||||
@brief Increments a macro variable
|
||||
@details Useful outside of do-loops - will increment a macro variable every
|
||||
time it is called.
|
||||
|
||||
Example:
|
||||
|
||||
%let cnt=1;
|
||||
%put We have run %mf_increment(cnt) lines;
|
||||
%put Now we have run %mf_increment(cnt) lines;
|
||||
%put There are %mf_increment(cnt) lines in total;
|
||||
|
||||
@param [in] macro_name The name of the macro variable to increment
|
||||
@param [in] incr= (1) The amount to add or subtract to the macro
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li mf_increment.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_increment(macro_name,incr=1);
|
||||
|
||||
/* iterate the value */
|
||||
%let ¯o_name=%eval(&&¯o_name+&incr);
|
||||
/* return the value */
|
||||
&&¯o_name
|
||||
|
||||
%mend mf_increment;
|
||||
@@ -6,12 +6,13 @@
|
||||
%sysevalf(%superq(param)=,boolean)
|
||||
|
||||
Usage:
|
||||
|
||||
%put mf_isblank(&var);
|
||||
|
||||
inspiration: https://support.sas.com/resources/papers/proceedings09/022-2009.pdf
|
||||
%put %mf_isblank(&var);
|
||||
|
||||
@param param VALUE to be checked
|
||||
inspiration:
|
||||
https://support.sas.com/resources/papers/proceedings09/022-2009.pdf
|
||||
|
||||
@param [in] Param VALUE to be checked
|
||||
|
||||
@return output returns 1 (if blank) else 0
|
||||
|
||||
@@ -23,4 +24,4 @@
|
||||
|
||||
%sysevalf(%superq(param)=,boolean)
|
||||
|
||||
%mend;
|
||||
%mend mf_isblank;
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
%let isdir=%mf_isdir(/tmp);
|
||||
|
||||
With thanks and full credit to Andrea Defronzo - https://www.linkedin.com/in/andrea-defronzo-b1a47460/
|
||||
With thanks and full credit to Andrea Defronzo -
|
||||
https://www.linkedin.com/in/andrea-defronzo-b1a47460/
|
||||
|
||||
@param path full path of the file/directory to be checked
|
||||
@param [in] path Full path of the file/directory to be checked
|
||||
|
||||
@return output returns 1 if path is a directory, 0 if it is not
|
||||
|
||||
@@ -17,17 +18,17 @@
|
||||
|
||||
%macro mf_isdir(path
|
||||
)/*/STORE SOURCE*/;
|
||||
%local rc did is_directory fref_t;
|
||||
%local rc did is_directory fref_t;
|
||||
|
||||
%let is_directory = 0;
|
||||
%let rc = %sysfunc(filename(fref_t, %superq(path)));
|
||||
%let did = %sysfunc(dopen(&fref_t.));
|
||||
%if &did. ^= 0 %then %do;
|
||||
%let is_directory = 1;
|
||||
%let rc = %sysfunc(dclose(&did.));
|
||||
%end;
|
||||
%let rc = %sysfunc(filename(fref_t));
|
||||
%let is_directory = 0;
|
||||
%let rc = %sysfunc(filename(fref_t, %superq(path)));
|
||||
%let did = %sysfunc(dopen(&fref_t.));
|
||||
%if &did. ^= 0 %then %do;
|
||||
%let is_directory = 1;
|
||||
%let rc = %sysfunc(dclose(&did.));
|
||||
%end;
|
||||
%let rc = %sysfunc(filename(fref_t));
|
||||
|
||||
&is_directory
|
||||
&is_directory
|
||||
|
||||
%mend;
|
||||
%mend mf_isdir;
|
||||
36
base/mf_isint.sas
Normal file
36
base/mf_isint.sas
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns 1 if the variable contains only digits 0-9, else 0
|
||||
@details Note that numerics containing any punctuation (including decimals
|
||||
or exponents) will be flagged zero.
|
||||
|
||||
If you'd like support for this, then do raise an issue (or even better, a
|
||||
pull request!)
|
||||
|
||||
Usage:
|
||||
|
||||
%put %mf_isint(1) returns 1;
|
||||
%put %mf_isint(1.1) returns 0;
|
||||
%put %mf_isint(%str(1,1)) returns 0;
|
||||
|
||||
@param [in] arg input value to check
|
||||
|
||||
@version 9.2
|
||||
**/
|
||||
|
||||
%macro mf_isint(arg
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* blank val is not an integer */
|
||||
%if "&arg"="" %then %do;0%return;%end;
|
||||
|
||||
/* remove minus sign if exists */
|
||||
%local val;
|
||||
%if "%substr(%str(&arg),1,1)"="-" %then %let val=%substr(%str(&arg),2);
|
||||
%else %let val=&arg;
|
||||
|
||||
/* check remaining chars */
|
||||
%if %sysfunc(findc(%str(&val),,kd)) %then %do;0%end;
|
||||
%else %do;1%end;
|
||||
|
||||
%mend mf_isint;
|
||||
40
base/mf_islibds.sas
Normal file
40
base/mf_islibds.sas
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
@file
|
||||
@brief Checks whether a string follows correct library.dataset format
|
||||
@details Many macros in the core library accept a library.dataset parameter
|
||||
referred to as 'libds'. This macro validates the structure of that parameter,
|
||||
eg:
|
||||
|
||||
@li 8 character libref?
|
||||
@li 32 character dataset?
|
||||
@li contains a period?
|
||||
|
||||
It does NOT check whether the dataset exists, or if the library is assigned.
|
||||
|
||||
Usage:
|
||||
|
||||
%put %mf_islibds(work.something)=1;
|
||||
%put %mf_islibds(nolib)=0;
|
||||
%put %mf_islibds(badlibref.ds)=0;
|
||||
%put %mf_islibds(w.t.f)=0;
|
||||
|
||||
@param [in] libds The string to be checked
|
||||
|
||||
@return output Returns 1 if libds is valid, 0 if it is not
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_islibds.test.sas
|
||||
@li mp_validatecol.sas
|
||||
|
||||
@version 9.2
|
||||
**/
|
||||
|
||||
%macro mf_islibds(libds
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local regex;
|
||||
%let regex=%sysfunc(prxparse(%str(/^[_a-z]\w{0,7}\.[_a-z]\w{0,31}$/i)));
|
||||
|
||||
%sysfunc(prxmatch(®ex,&libds))
|
||||
|
||||
%mend mf_islibds;
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
%put %mf_loc(POF); %*location of PlatformObjectFramework tools;
|
||||
|
||||
@param [in] loc The item to locate, eg:
|
||||
@li PLAATFORMOBJECTFRAMEWORK (or POF)
|
||||
@li VIYACONFG
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
@@ -15,7 +19,8 @@
|
||||
%local root;
|
||||
|
||||
%if &loc=POF or &loc=PLATFORMOBJECTFRAMEWORK %then %do;
|
||||
%let root=%substr(%sysget(SASROOT),1,%index(%sysget(SASROOT),SASFoundation)-2);
|
||||
%let root=%sysget(SASROOT);
|
||||
%let root=%substr(&root,1,%index(&root,SASFoundation)-2);
|
||||
%let root=&root/SASPlatformObjectFramework/&sysver;
|
||||
%put Batch tools located at: &root;
|
||||
&root
|
||||
@@ -26,4 +31,4 @@
|
||||
&root
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mf_loc;
|
||||
|
||||
436
base/mf_mimetype.sas
Normal file
436
base/mf_mimetype.sas
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns a mime type from a file extension
|
||||
@details Provide a file extension and get a mime type.
|
||||
The mappings were derived from this source:
|
||||
https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
|
||||
|
||||
|
||||
@param [in] ext The file extension (unquoted)
|
||||
@return output The mime type
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_mimetype(
|
||||
ext
|
||||
)/*/STORE SOURCE*/ /minoperator mindelimiter=' ';
|
||||
|
||||
%let ext=%lowcase(&ext);
|
||||
|
||||
%if &ext in (sas txt text conf def list log)
|
||||
%then %do;%str(text/plain)%end;
|
||||
%else %if &ext=xlsx %then %do;
|
||||
%str(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)%end;
|
||||
%else %if &ext in (xls xlm xla xlc xlt xlw)
|
||||
%then %do;%str(application/vnd.ms-excel)%end;
|
||||
%else %if &ext=xlsm
|
||||
%then %do;%str(application/vnd.ms-excel.sheet.macroenabled.12)%end;
|
||||
%else %if &ext=xlsb
|
||||
%then %do;%str(application/vnd.ms-excel.sheet.binary.macroenabled.12)%end;
|
||||
%else %if &ext in (css csv html n3 sgml vcard)
|
||||
%then %do;%str(text/&ext)%end;
|
||||
%else %if &ext in (avif bmp cgm gif ief jxl ktx png sgi tiff webp)
|
||||
%then %do;%str(image/&ext)%end;
|
||||
%else %if &ext in (exi gxf ipfix json mbox mp21 mxf oda oxps pdf rtf sdp wasm
|
||||
xml yang zip)
|
||||
%then %do;%str(application/&ext)%end;
|
||||
%else %if &ext in (jpeg jpg jpe) %then %do;%str(image/jpeg)%end;
|
||||
%else %if &ext in (mp4 mp4v mpg4) %then %do;%str(video/mp4)%end;
|
||||
%else %if &ext in (otf ttf woff woff2) %then %do;%str(font/&ext)%end;
|
||||
%else %if &ext in (mpeg mpg mpe m1v m2v)
|
||||
%then %do;%str(video/mpeg)%end;
|
||||
%else %if &ext in (h261 h263 h264 jpm mj2 webm)
|
||||
%then %do;%str(video/&ext)%end;
|
||||
%else %if &ext in (f4v fli flv m4v mng smv)
|
||||
%then %do;%str(video/x-&ext)%end;
|
||||
%else %if &ext in (3ds cmx pcx rgb tga)
|
||||
%then %do;%str(image/x-&ext)%end;
|
||||
%else %if &ext in (asm nfo opml sfv)
|
||||
%then %do;%str(text/x-&ext)%end;
|
||||
%else %if &ext in (aac caf flac wav)
|
||||
%then %do;%str(audio/x-&ext)%end;
|
||||
%else %if &ext in (ts m2t m2ts mts)
|
||||
%then %do;%str(video/mp2t)%end;
|
||||
%else %if &ext in (pfa pfb pfm afm)
|
||||
%then %do;%str(application/x-font-type1)%end;
|
||||
%else %if &ext in (oga ogg spx opus)
|
||||
%then %do;%str(audio/ogg)%end;
|
||||
%else %if &ext in (mid midi kar rmi)
|
||||
%then %do;%str(audio/midi)%end;
|
||||
%else %if &ext in (onetoc onetoc2 onetmp onepkg)
|
||||
%then %do;%str(application/onenote)%end;
|
||||
%else %if &ext in (mxml xhvml xvml xvm)
|
||||
%then %do;%str(application/xv+xml)%end;
|
||||
%else %if &ext in (f for f77 f90)
|
||||
%then %do;%str(text/x-fortran)%end;
|
||||
%else %if &ext in (wmf wmz emf emz)
|
||||
%then %do;%str(application/x-msmetafile)%end;
|
||||
%else %if &ext in (exe dll com bat msi)
|
||||
%then %do;%str(application/x-msdownload)%end;
|
||||
%else %if &ext in (bin dms lrf mar so dist distz pkg bpk dump elc deploy)
|
||||
%then %do;%str(application/octet-stream)%end;
|
||||
%else %if &ext in (atom atomcat atomsvc ccxml davmount emma gml gpx inkml mads
|
||||
mathml metalink mets mods omdoc pls rdf rsd rss sbml shf smil sru ssdl ssml
|
||||
tei wsdl wspolicy xaml xenc xhtml xop xslt xspf yin)
|
||||
%then %do;%str(application/&ext+xml)%end;
|
||||
%else %if &ext in (dir dcr dxr cst cct cxt w3d fgd swa)
|
||||
%then %do;%str(application/x-director)%end;
|
||||
%else %if &ext in (z1 z2 z3 z4 z5 z6 z7 z8)
|
||||
%then %do;%str(application/x-zmachine)%end;
|
||||
%else %if &ext in (c cc cxx cpp h hh dic)
|
||||
%then %do;%str(text/x-c)%end;
|
||||
%else %if &ext in (mpga mp2 mp2a mp3 m2a m3a)
|
||||
%then %do;%str(audio/mpeg)%end;
|
||||
%else %if &ext in (t tr roff man me ms)
|
||||
%then %do;%str(text/troff)%end;
|
||||
%else %if &ext in (cbr cba cbt cbz cb7)
|
||||
%then %do;%str(application/x-cbr)%end;
|
||||
%else %if &ext in (fh fhc fh4 fh5 fh7)
|
||||
%then %do;%str(image/x-freehand)%end;
|
||||
%else %if &ext in (aab x32 u32 vox)
|
||||
%then %do;%str(application/x-authorware-bin)%end;
|
||||
%else %if &ext in (uvi uvvi uvg uvvg)
|
||||
%then %do;%str(image/vnd.dece.graphic)%end;
|
||||
%else %if &ext in (cdx cif cmdf cml csml xyz)
|
||||
%then %do;%str(chemical/x-&ext)%end;
|
||||
%else %if &ext in (aif aiff aifc) %then %do;%str(audio/x-aiff)%end;
|
||||
%else %if &ext in (ma nb mb) %then %do;%str(application/mathematica)%end;
|
||||
%else %if &ext in (mvb m13 m14) %then %do;%str(application/x-msmediaview)%end;
|
||||
%else %if &ext in (msh mesh silo) %then %do;%str(model/mesh)%end;
|
||||
%else %if &ext in (uri uris urls) %then %do;%str(text/uri-list)%end;
|
||||
%else %if &ext in (mkv mk3d mks) %then %do;%str(video/x-matroska)%end;
|
||||
%else %if &ext=ez %then %do;%str(application/andrew-inset)%end;
|
||||
%else %if &ext=aw %then %do;%str(application/applixware)%end;
|
||||
%else %if &ext=cdmia %then %do;%str(application/cdmi-capability)%end;
|
||||
%else %if &ext=cdmic %then %do;%str(application/cdmi-container)%end;
|
||||
%else %if &ext=cdmid %then %do;%str(application/cdmi-domain)%end;
|
||||
%else %if &ext=cdmio %then %do;%str(application/cdmi-object)%end;
|
||||
%else %if &ext=cdmiq %then %do;%str(application/cdmi-queue)%end;
|
||||
%else %if &ext=cu %then %do;%str(application/cu-seeme)%end;
|
||||
%else %if &ext=dssc %then %do;%str(application/dssc+der)%end;
|
||||
%else %if &ext=xdssc %then %do;%str(application/dssc+xml)%end;
|
||||
%else %if &ext=ecma %then %do;%str(application/ecmascript)%end;
|
||||
%else %if &ext=epub %then %do;%str(application/epub+zip)%end;
|
||||
%else %if &ext=pfr %then %do;%str(application/font-tdpfr)%end;
|
||||
%else %if &ext=stk %then %do;%str(application/hyperstudio)%end;
|
||||
%else %if &ext=ink %then %do;%str(application/inkml+xml)%end;
|
||||
%else %if &ext=jar %then %do;%str(application/java-archive)%end;
|
||||
%else %if &ext=ser %then %do;%str(application/java-serialized-object)%end;
|
||||
%else %if &ext=class %then %do;%str(application/java-vm)%end;
|
||||
%else %if &ext=jsonml %then %do;%str(application/jsonml+json)%end;
|
||||
%else %if &ext=lostxml %then %do;%str(application/lost+xml)%end;
|
||||
%else %if &ext=hqx %then %do;%str(application/mac-binhex40)%end;
|
||||
%else %if &ext=cpt %then %do;%str(application/mac-compactpro)%end;
|
||||
%else %if &ext=mrc %then %do;%str(application/marc)%end;
|
||||
%else %if &ext=mrcx %then %do;%str(application/marcxml+xml)%end;
|
||||
%else %if &ext=mscml %then %do;%str(application/mediaservercontrol+xml)%end;
|
||||
%else %if &ext=meta4 %then %do;%str(application/metalink4+xml)%end;
|
||||
%else %if &ext=m21 %then %do;%str(application/mp21)%end;
|
||||
%else %if &ext=mp4s %then %do;%str(application/mp4)%end;
|
||||
%else %if &ext=doc %then %do;%str(application/msword)%end;
|
||||
%else %if &ext=dot %then %do;%str(application/msword)%end;
|
||||
%else %if &ext=opf %then %do;%str(application/oebps-package+xml)%end;
|
||||
%else %if &ext=ogx %then %do;%str(application/ogg)%end;
|
||||
%else %if &ext=xer %then %do;%str(application/patch-ops-error+xml)%end;
|
||||
%else %if &ext=pgp %then %do;%str(application/pgp-encrypted)%end;
|
||||
%else %if &ext=asc %then %do;%str(application/pgp-signature)%end;
|
||||
%else %if &ext=sig %then %do;%str(application/pgp-signature)%end;
|
||||
%else %if &ext=prf %then %do;%str(application/pics-rules)%end;
|
||||
%else %if &ext=p10 %then %do;%str(application/pkcs10)%end;
|
||||
%else %if &ext=p7m %then %do;%str(application/pkcs7-mime)%end;
|
||||
%else %if &ext=p7c %then %do;%str(application/pkcs7-mime)%end;
|
||||
%else %if &ext=p7s %then %do;%str(application/pkcs7-signature)%end;
|
||||
%else %if &ext=p8 %then %do;%str(application/pkcs8)%end;
|
||||
%else %if &ext=ac %then %do;%str(application/pkix-attr-cert)%end;
|
||||
%else %if &ext=cer %then %do;%str(application/pkix-cert)%end;
|
||||
%else %if &ext=crl %then %do;%str(application/pkix-crl)%end;
|
||||
%else %if &ext=pkipath %then %do;%str(application/pkix-pkipath)%end;
|
||||
%else %if &ext=pki %then %do;%str(application/pkixcmp)%end;
|
||||
%else %if &ext=cww %then %do;%str(application/prs.cww)%end;
|
||||
%else %if &ext=pskcxml %then %do;%str(application/pskc+xml)%end;
|
||||
%else %if &ext=rif %then %do;%str(application/reginfo+xml)%end;
|
||||
%else %if &ext=rnc %then %do;%str(application/relax-ng-compact-syntax)%end;
|
||||
%else %if &ext=rld %then %do;%str(application/resource-lists-diff+xml)%end;
|
||||
%else %if &ext=rl %then %do;%str(application/resource-lists+xml)%end;
|
||||
%else %if &ext=gbr %then %do;%str(application/rpki-ghostbusters)%end;
|
||||
%else %if &ext=mft %then %do;%str(application/rpki-manifest)%end;
|
||||
%else %if &ext=roa %then %do;%str(application/rpki-roa)%end;
|
||||
%else %if &ext=scq %then %do;%str(application/scvp-cv-request)%end;
|
||||
%else %if &ext=scs %then %do;%str(application/scvp-cv-response)%end;
|
||||
%else %if &ext=spq %then %do;%str(application/scvp-vp-request)%end;
|
||||
%else %if &ext=spp %then %do;%str(application/scvp-vp-response)%end;
|
||||
%else %if &ext=setpay %then %do;%str(application/set-payment-initiation)%end;
|
||||
%else %if &ext=setreg %then %do;%str(application/set-registration-initiation)%end;
|
||||
%else %if &ext=smi %then %do;%str(application/smil+xml)%end;
|
||||
%else %if &ext=rq %then %do;%str(application/sparql-query)%end;
|
||||
%else %if &ext=srx %then %do;%str(application/sparql-results+xml)%end;
|
||||
%else %if &ext=gram %then %do;%str(application/srgs)%end;
|
||||
%else %if &ext=grxml %then %do;%str(application/srgs+xml)%end;
|
||||
%else %if &ext=teicorpus %then %do;%str(application/tei+xml)%end;
|
||||
%else %if &ext=tfi %then %do;%str(application/thraud+xml)%end;
|
||||
%else %if &ext=tsd %then %do;%str(application/timestamped-data)%end;
|
||||
%else %if &ext=vxml %then %do;%str(application/voicexml+xml)%end;
|
||||
%else %if &ext=wgt %then %do;%str(application/widget)%end;
|
||||
%else %if &ext=hlp %then %do;%str(application/winhlp)%end;
|
||||
%else %if &ext=7z %then %do;%str(application/x-7z-compressed)%end;
|
||||
%else %if &ext=abw %then %do;%str(application/x-abiword)%end;
|
||||
%else %if &ext=ace %then %do;%str(application/x-ace-compressed)%end;
|
||||
%else %if &ext=dmg %then %do;%str(application/x-apple-diskimage)%end;
|
||||
%else %if &ext=aam %then %do;%str(application/x-authorware-map)%end;
|
||||
%else %if &ext=aas %then %do;%str(application/x-authorware-seg)%end;
|
||||
%else %if &ext=bcpio %then %do;%str(application/x-bcpio)%end;
|
||||
%else %if &ext=torrent %then %do;%str(application/x-bittorrent)%end;
|
||||
%else %if &ext=blb %then %do;%str(application/x-blorb)%end;
|
||||
%else %if &ext=blorb %then %do;%str(application/x-blorb)%end;
|
||||
%else %if &ext=bz %then %do;%str(application/x-bzip)%end;
|
||||
%else %if &ext=bz2 %then %do;%str(application/x-bzip2)%end;
|
||||
%else %if &ext=boz %then %do;%str(application/x-bzip2)%end;
|
||||
%else %if &ext=vcd %then %do;%str(application/x-cdlink)%end;
|
||||
%else %if &ext=cfs %then %do;%str(application/x-cfs-compressed)%end;
|
||||
%else %if &ext=chat %then %do;%str(application/x-chat)%end;
|
||||
%else %if &ext=pgn %then %do;%str(application/x-chess-pgn)%end;
|
||||
%else %if &ext=nsc %then %do;%str(application/x-conference)%end;
|
||||
%else %if &ext=cpio %then %do;%str(application/x-cpio)%end;
|
||||
%else %if &ext=csh %then %do;%str(application/x-csh)%end;
|
||||
%else %if &ext=deb %then %do;%str(application/x-debian-package)%end;
|
||||
%else %if &ext=udeb %then %do;%str(application/x-debian-package)%end;
|
||||
%else %if &ext=dgc %then %do;%str(application/x-dgc-compressed)%end;
|
||||
%else %if &ext=wad %then %do;%str(application/x-doom)%end;
|
||||
%else %if &ext=ncx %then %do;%str(application/x-dtbncx+xml)%end;
|
||||
%else %if &ext=dtb %then %do;%str(application/x-dtbook+xml)%end;
|
||||
%else %if &ext=res %then %do;%str(application/x-dtbresource+xml)%end;
|
||||
%else %if &ext=dvi %then %do;%str(application/x-dvi)%end;
|
||||
%else %if &ext=evy %then %do;%str(application/x-envoy)%end;
|
||||
%else %if &ext=eva %then %do;%str(application/x-eva)%end;
|
||||
%else %if &ext=bdf %then %do;%str(application/x-font-bdf)%end;
|
||||
%else %if &ext=gsf %then %do;%str(application/x-font-ghostscript)%end;
|
||||
%else %if &ext=psf %then %do;%str(application/x-font-linux-psf)%end;
|
||||
%else %if &ext=pcf %then %do;%str(application/x-font-pcf)%end;
|
||||
%else %if &ext=snf %then %do;%str(application/x-font-snf)%end;
|
||||
%else %if &ext=arc %then %do;%str(application/x-freearc)%end;
|
||||
%else %if &ext=spl %then %do;%str(application/x-futuresplash)%end;
|
||||
%else %if &ext=gca %then %do;%str(application/x-gca-compressed)%end;
|
||||
%else %if &ext=ulx %then %do;%str(application/x-glulx)%end;
|
||||
%else %if &ext=gnumeric %then %do;%str(application/x-gnumeric)%end;
|
||||
%else %if &ext=gramps %then %do;%str(application/x-gramps-xml)%end;
|
||||
%else %if &ext=gtar %then %do;%str(application/x-gtar)%end;
|
||||
%else %if &ext=hdf %then %do;%str(application/x-hdf)%end;
|
||||
%else %if &ext=install %then %do;%str(application/x-install-instructions)%end;
|
||||
%else %if &ext=iso %then %do;%str(application/x-iso9660-image)%end;
|
||||
%else %if &ext=jnlp %then %do;%str(application/x-java-jnlp-file)%end;
|
||||
%else %if &ext=latex %then %do;%str(application/x-latex)%end;
|
||||
%else %if &ext=lzh %then %do;%str(application/x-lzh-compressed)%end;
|
||||
%else %if &ext=lha %then %do;%str(application/x-lzh-compressed)%end;
|
||||
%else %if &ext=mie %then %do;%str(application/x-mie)%end;
|
||||
%else %if &ext=prc %then %do;%str(application/x-mobipocket-ebook)%end;
|
||||
%else %if &ext=mobi %then %do;%str(application/x-mobipocket-ebook)%end;
|
||||
%else %if &ext=application %then %do;%str(application/x-ms-application)%end;
|
||||
%else %if &ext=lnk %then %do;%str(application/x-ms-shortcut)%end;
|
||||
%else %if &ext=wmd %then %do;%str(application/x-ms-wmd)%end;
|
||||
%else %if &ext=wmz %then %do;%str(application/x-ms-wmz)%end;
|
||||
%else %if &ext=xbap %then %do;%str(application/x-ms-xbap)%end;
|
||||
%else %if &ext=mdb %then %do;%str(application/x-msaccess)%end;
|
||||
%else %if &ext=obd %then %do;%str(application/x-msbinder)%end;
|
||||
%else %if &ext=crd %then %do;%str(application/x-mscardfile)%end;
|
||||
%else %if &ext=clp %then %do;%str(application/x-msclip)%end;
|
||||
%else %if &ext=mny %then %do;%str(application/x-msmoney)%end;
|
||||
%else %if &ext=pub %then %do;%str(application/x-mspublisher)%end;
|
||||
%else %if &ext=scd %then %do;%str(application/x-msschedule)%end;
|
||||
%else %if &ext=trm %then %do;%str(application/x-msterminal)%end;
|
||||
%else %if &ext=wri %then %do;%str(application/x-mswrite)%end;
|
||||
%else %if &ext=nc %then %do;%str(application/x-netcdf)%end;
|
||||
%else %if &ext=cdf %then %do;%str(application/x-netcdf)%end;
|
||||
%else %if &ext=nzb %then %do;%str(application/x-nzb)%end;
|
||||
%else %if &ext=p12 %then %do;%str(application/x-pkcs12)%end;
|
||||
%else %if &ext=pfx %then %do;%str(application/x-pkcs12)%end;
|
||||
%else %if &ext=p7b %then %do;%str(application/x-pkcs7-certificates)%end;
|
||||
%else %if &ext=spc %then %do;%str(application/x-pkcs7-certificates)%end;
|
||||
%else %if &ext=p7r %then %do;%str(application/x-pkcs7-certreqresp)%end;
|
||||
%else %if &ext=rar %then %do;%str(application/x-rar-compressed)%end;
|
||||
%else %if &ext=ris %then %do;%str(application/x-research-info-systems)%end;
|
||||
%else %if &ext=sh %then %do;%str(application/x-sh)%end;
|
||||
%else %if &ext=shar %then %do;%str(application/x-shar)%end;
|
||||
%else %if &ext=swf %then %do;%str(application/x-shockwave-flash)%end;
|
||||
%else %if &ext=xap %then %do;%str(application/x-silverlight-app)%end;
|
||||
%else %if &ext=sql %then %do;%str(application/x-sql)%end;
|
||||
%else %if &ext=sit %then %do;%str(application/x-stuffit)%end;
|
||||
%else %if &ext=sitx %then %do;%str(application/x-stuffitx)%end;
|
||||
%else %if &ext=srt %then %do;%str(application/x-subrip)%end;
|
||||
%else %if &ext=sv4cpio %then %do;%str(application/x-sv4cpio)%end;
|
||||
%else %if &ext=sv4crc %then %do;%str(application/x-sv4crc)%end;
|
||||
%else %if &ext=t3 %then %do;%str(application/x-t3vm-image)%end;
|
||||
%else %if &ext=gam %then %do;%str(application/x-tads)%end;
|
||||
%else %if &ext=tar %then %do;%str(application/x-tar)%end;
|
||||
%else %if &ext=tcl %then %do;%str(application/x-tcl)%end;
|
||||
%else %if &ext=tex %then %do;%str(application/x-tex)%end;
|
||||
%else %if &ext=tfm %then %do;%str(application/x-tex-tfm)%end;
|
||||
%else %if &ext=texinfo %then %do;%str(application/x-texinfo)%end;
|
||||
%else %if &ext=texi %then %do;%str(application/x-texinfo)%end;
|
||||
%else %if &ext=obj %then %do;%str(application/x-tgif)%end;
|
||||
%else %if &ext=ustar %then %do;%str(application/x-ustar)%end;
|
||||
%else %if &ext=src %then %do;%str(application/x-wais-source)%end;
|
||||
%else %if &ext=der %then %do;%str(application/x-x509-ca-cert)%end;
|
||||
%else %if &ext=crt %then %do;%str(application/x-x509-ca-cert)%end;
|
||||
%else %if &ext=fig %then %do;%str(application/x-xfig)%end;
|
||||
%else %if &ext=xlf %then %do;%str(application/x-xliff+xml)%end;
|
||||
%else %if &ext=xpi %then %do;%str(application/x-xpinstall)%end;
|
||||
%else %if &ext=xz %then %do;%str(application/x-xz)%end;
|
||||
%else %if &ext=xdf %then %do;%str(application/xcap-diff+xml)%end;
|
||||
%else %if &ext=xht %then %do;%str(application/xhtml+xml)%end;
|
||||
%else %if &ext=xsl %then %do;%str(application/xml)%end;
|
||||
%else %if &ext=dtd %then %do;%str(application/xml-dtd)%end;
|
||||
%else %if &ext=xpl %then %do;%str(application/xproc+xml)%end;
|
||||
%else %if &ext=adp %then %do;%str(audio/adpcm)%end;
|
||||
%else %if &ext=au %then %do;%str(audio/basic)%end;
|
||||
%else %if &ext=snd %then %do;%str(audio/basic)%end;
|
||||
%else %if &ext=m4a %then %do;%str(audio/mp4)%end;
|
||||
%else %if &ext=mp4a %then %do;%str(audio/mp4)%end;
|
||||
%else %if &ext=s3m %then %do;%str(audio/s3m)%end;
|
||||
%else %if &ext=sil %then %do;%str(audio/silk)%end;
|
||||
%else %if &ext=uva %then %do;%str(audio/vnd.dece.audio)%end;
|
||||
%else %if &ext=uvva %then %do;%str(audio/vnd.dece.audio)%end;
|
||||
%else %if &ext=eol %then %do;%str(audio/vnd.digital-winds)%end;
|
||||
%else %if &ext=dra %then %do;%str(audio/vnd.dra)%end;
|
||||
%else %if &ext=dts %then %do;%str(audio/vnd.dts)%end;
|
||||
%else %if &ext=dtshd %then %do;%str(audio/vnd.dts.hd)%end;
|
||||
%else %if &ext=lvp %then %do;%str(audio/vnd.lucent.voice)%end;
|
||||
%else %if &ext=pya %then %do;%str(audio/vnd.ms-playready.media.pya)%end;
|
||||
%else %if &ext=ecelp4800 %then %do;%str(audio/vnd.nuera.ecelp4800)%end;
|
||||
%else %if &ext=ecelp7470 %then %do;%str(audio/vnd.nuera.ecelp7470)%end;
|
||||
%else %if &ext=ecelp9600 %then %do;%str(audio/vnd.nuera.ecelp9600)%end;
|
||||
%else %if &ext=rip %then %do;%str(audio/vnd.rip)%end;
|
||||
%else %if &ext=weba %then %do;%str(audio/webm)%end;
|
||||
%else %if &ext=mka %then %do;%str(audio/x-matroska)%end;
|
||||
%else %if &ext=m3u %then %do;%str(audio/x-mpegurl)%end;
|
||||
%else %if &ext=wax %then %do;%str(audio/x-ms-wax)%end;
|
||||
%else %if &ext=wma %then %do;%str(audio/x-ms-wma)%end;
|
||||
%else %if &ext=ra %then %do;%str(audio/x-pn-realaudio)%end;
|
||||
%else %if &ext=ram %then %do;%str(audio/x-pn-realaudio)%end;
|
||||
%else %if &ext=rmp %then %do;%str(audio/x-pn-realaudio-plugin)%end;
|
||||
%else %if &ext=xm %then %do;%str(audio/xm)%end;
|
||||
%else %if &ext=ttc %then %do;%str(font/collection)%end;
|
||||
%else %if &ext=g3 %then %do;%str(image/g3fax)%end;
|
||||
%else %if &ext=btif %then %do;%str(image/prs.btif)%end;
|
||||
%else %if &ext=svg %then %do;%str(image/svg+xml)%end;
|
||||
%else %if &ext=svgz %then %do;%str(image/svg+xml)%end;
|
||||
%else %if &ext=tif %then %do;%str(image/tiff)%end;
|
||||
%else %if &ext=psd %then %do;%str(image/vnd.adobe.photoshop)%end;
|
||||
%else %if &ext=djv %then %do;%str(image/vnd.djvu)%end;
|
||||
%else %if &ext=djvu %then %do;%str(image/vnd.djvu)%end;
|
||||
%else %if &ext=sub %then %do;%str(image/vnd.dvb.subtitle)%end;
|
||||
%else %if &ext=dwg %then %do;%str(image/vnd.dwg)%end;
|
||||
%else %if &ext=dxf %then %do;%str(image/vnd.dxf)%end;
|
||||
%else %if &ext=fbs %then %do;%str(image/vnd.fastbidsheet)%end;
|
||||
%else %if &ext=fpx %then %do;%str(image/vnd.fpx)%end;
|
||||
%else %if &ext=fst %then %do;%str(image/vnd.fst)%end;
|
||||
%else %if &ext=mmr %then %do;%str(image/vnd.fujixerox.edmics-mmr)%end;
|
||||
%else %if &ext=rlc %then %do;%str(image/vnd.fujixerox.edmics-rlc)%end;
|
||||
%else %if &ext=mdi %then %do;%str(image/vnd.ms-modi)%end;
|
||||
%else %if &ext=wdp %then %do;%str(image/vnd.ms-photo)%end;
|
||||
%else %if &ext=npx %then %do;%str(image/vnd.net-fpx)%end;
|
||||
%else %if &ext=wbmp %then %do;%str(image/vnd.wap.wbmp)%end;
|
||||
%else %if &ext=xif %then %do;%str(image/vnd.xiff)%end;
|
||||
%else %if &ext=ras %then %do;%str(image/x-cmu-raster)%end;
|
||||
%else %if &ext=ico %then %do;%str(image/x-icon)%end;
|
||||
%else %if &ext=sid %then %do;%str(image/x-mrsid-image)%end;
|
||||
%else %if &ext=pct %then %do;%str(image/x-pict)%end;
|
||||
%else %if &ext=pic %then %do;%str(image/x-pict)%end;
|
||||
%else %if &ext=pnm %then %do;%str(image/x-portable-anymap)%end;
|
||||
%else %if &ext=pbm %then %do;%str(image/x-portable-bitmap)%end;
|
||||
%else %if &ext=pgm %then %do;%str(image/x-portable-graymap)%end;
|
||||
%else %if &ext=ppm %then %do;%str(image/x-portable-pixmap)%end;
|
||||
%else %if &ext=xbm %then %do;%str(image/x-xbitmap)%end;
|
||||
%else %if &ext=xpm %then %do;%str(image/x-xpixmap)%end;
|
||||
%else %if &ext=xwd %then %do;%str(image/x-xwindowdump)%end;
|
||||
%else %if &ext=eml %then %do;%str(message/rfc822)%end;
|
||||
%else %if &ext=mime %then %do;%str(message/rfc822)%end;
|
||||
%else %if &ext=iges %then %do;%str(model/iges)%end;
|
||||
%else %if &ext=igs %then %do;%str(model/iges)%end;
|
||||
%else %if &ext=dae %then %do;%str(model/vnd.collada+xml)%end;
|
||||
%else %if &ext=dwf %then %do;%str(model/vnd.dwf)%end;
|
||||
%else %if &ext=gdl %then %do;%str(model/vnd.gdl)%end;
|
||||
%else %if &ext=gtw %then %do;%str(model/vnd.gtw)%end;
|
||||
%else %if &ext=vtu %then %do;%str(model/vnd.vtu)%end;
|
||||
%else %if &ext=vrml %then %do;%str(model/vrml)%end;
|
||||
%else %if &ext=wrl %then %do;%str(model/vrml)%end;
|
||||
%else %if &ext=x3db %then %do;%str(model/x3d+binary)%end;
|
||||
%else %if &ext=x3dbz %then %do;%str(model/x3d+binary)%end;
|
||||
%else %if &ext=x3dv %then %do;%str(model/x3d+vrml)%end;
|
||||
%else %if &ext=x3dvz %then %do;%str(model/x3d+vrml)%end;
|
||||
%else %if &ext=x3d %then %do;%str(model/x3d+xml)%end;
|
||||
%else %if &ext=x3dz %then %do;%str(model/x3d+xml)%end;
|
||||
%else %if &ext=appcache %then %do;%str(text/cache-manifest)%end;
|
||||
%else %if &ext=ics %then %do;%str(text/calendar)%end;
|
||||
%else %if &ext=ifb %then %do;%str(text/calendar)%end;
|
||||
%else %if &ext=htm %then %do;%str(text/html)%end;
|
||||
%else %if &ext=js %then %do;%str(text/javascript)%end;
|
||||
%else %if &ext=mjs %then %do;%str(text/javascript)%end;
|
||||
%else %if &ext=dsc %then %do;%str(text/prs.lines.tag)%end;
|
||||
%else %if &ext=rtx %then %do;%str(text/richtext)%end;
|
||||
%else %if &ext=sgm %then %do;%str(text/sgml)%end;
|
||||
%else %if &ext=tsv %then %do;%str(text/tab-separated-values)%end;
|
||||
%else %if &ext=ttl %then %do;%str(text/turtle)%end;
|
||||
%else %if &ext=curl %then %do;%str(text/vnd.curl)%end;
|
||||
%else %if &ext=dcurl %then %do;%str(text/vnd.curl.dcurl)%end;
|
||||
%else %if &ext=mcurl %then %do;%str(text/vnd.curl.mcurl)%end;
|
||||
%else %if &ext=scurl %then %do;%str(text/vnd.curl.scurl)%end;
|
||||
%else %if &ext=sub %then %do;%str(text/vnd.dvb.subtitle)%end;
|
||||
%else %if &ext=fly %then %do;%str(text/vnd.fly)%end;
|
||||
%else %if &ext=flx %then %do;%str(text/vnd.fmi.flexstor)%end;
|
||||
%else %if &ext=gv %then %do;%str(text/vnd.graphviz)%end;
|
||||
%else %if &ext=3dml %then %do;%str(text/vnd.in3d.3dml)%end;
|
||||
%else %if &ext=spot %then %do;%str(text/vnd.in3d.spot)%end;
|
||||
%else %if &ext=jad %then %do;%str(text/vnd.sun.j2me.app-descriptor)%end;
|
||||
%else %if &ext=wml %then %do;%str(text/vnd.wap.wml)%end;
|
||||
%else %if &ext=wmls %then %do;%str(text/vnd.wap.wmlscript)%end;
|
||||
%else %if &ext=s %then %do;%str(text/x-asm)%end;
|
||||
%else %if &ext=java %then %do;%str(text/x-java-source)%end;
|
||||
%else %if &ext=p %then %do;%str(text/x-pascal)%end;
|
||||
%else %if &ext=pas %then %do;%str(text/x-pascal)%end;
|
||||
%else %if &ext=etx %then %do;%str(text/x-setext)%end;
|
||||
%else %if &ext=uu %then %do;%str(text/x-uuencode)%end;
|
||||
%else %if &ext=vcs %then %do;%str(text/x-vcalendar)%end;
|
||||
%else %if &ext=vcf %then %do;%str(text/x-vcard)%end;
|
||||
%else %if &ext=3gp %then %do;%str(video/3gpp)%end;
|
||||
%else %if &ext=3g2 %then %do;%str(video/3gpp2)%end;
|
||||
%else %if &ext=jpgv %then %do;%str(video/jpeg)%end;
|
||||
%else %if &ext=jpgm %then %do;%str(video/jpm)%end;
|
||||
%else %if &ext=mjp2 %then %do;%str(video/mj2)%end;
|
||||
%else %if &ext=ogv %then %do;%str(video/ogg)%end;
|
||||
%else %if &ext=mov %then %do;%str(video/quicktime)%end;
|
||||
%else %if &ext=qt %then %do;%str(video/quicktime)%end;
|
||||
%else %if &ext=uvh %then %do;%str(video/vnd.dece.hd)%end;
|
||||
%else %if &ext=uvvh %then %do;%str(video/vnd.dece.hd)%end;
|
||||
%else %if &ext=uvm %then %do;%str(video/vnd.dece.mobile)%end;
|
||||
%else %if &ext=uvvm %then %do;%str(video/vnd.dece.mobile)%end;
|
||||
%else %if &ext=uvp %then %do;%str(video/vnd.dece.pd)%end;
|
||||
%else %if &ext=uvvp %then %do;%str(video/vnd.dece.pd)%end;
|
||||
%else %if &ext=uvs %then %do;%str(video/vnd.dece.sd)%end;
|
||||
%else %if &ext=uvvs %then %do;%str(video/vnd.dece.sd)%end;
|
||||
%else %if &ext=uvv %then %do;%str(video/vnd.dece.video)%end;
|
||||
%else %if &ext=uvvv %then %do;%str(video/vnd.dece.video)%end;
|
||||
%else %if &ext=dvb %then %do;%str(video/vnd.dvb.file)%end;
|
||||
%else %if &ext=fvt %then %do;%str(video/vnd.fvt)%end;
|
||||
%else %if &ext=m4u %then %do;%str(video/vnd.mpegurl)%end;
|
||||
%else %if &ext=mxu %then %do;%str(video/vnd.mpegurl)%end;
|
||||
%else %if &ext=pyv %then %do;%str(video/vnd.ms-playready.media.pyv)%end;
|
||||
%else %if &ext=uvu %then %do;%str(video/vnd.uvvu.mp4)%end;
|
||||
%else %if &ext=uvvu %then %do;%str(video/vnd.uvvu.mp4)%end;
|
||||
%else %if &ext=viv %then %do;%str(video/vnd.vivo)%end;
|
||||
%else %if &ext=asf %then %do;%str(video/x-ms-asf)%end;
|
||||
%else %if &ext=asx %then %do;%str(video/x-ms-asf)%end;
|
||||
%else %if &ext=vob %then %do;%str(video/x-ms-vob)%end;
|
||||
%else %if &ext=wm %then %do;%str(video/x-ms-wm)%end;
|
||||
%else %if &ext=wmv %then %do;%str(video/x-ms-wmv)%end;
|
||||
%else %if &ext=wmx %then %do;%str(video/x-ms-wmx)%end;
|
||||
%else %if &ext=wvx %then %do;%str(video/x-ms-wvx)%end;
|
||||
%else %if &ext=avi %then %do;%str(video/x-msvideo)%end;
|
||||
%else %if &ext=movie %then %do;%str(video/x-sgi-movie)%end;
|
||||
%else %if &ext=ice %then %do;%str(x-conference/x-cooltalk)%end;
|
||||
%else %if "&ext"="in" %then %do;%str(text/plain)%end;
|
||||
%else %do;%str(application/octet-stream)%end;
|
||||
|
||||
%mend mf_mimetype;
|
||||
@@ -7,7 +7,7 @@ Usage:
|
||||
%mf_mkdir(/some/path/name)
|
||||
|
||||
|
||||
@param dir relative or absolute pathname. Unquoted.
|
||||
@param [in] dir Relative or absolute pathname. Unquoted.
|
||||
@version 9.2
|
||||
|
||||
**/
|
||||
@@ -46,22 +46,22 @@ Usage:
|
||||
*/
|
||||
|
||||
%if (%length(&dir) gt %length(&child)) %then %do;
|
||||
%let parent = %substr(&dir, 1, %length(&dir)-%length(&child));
|
||||
%mf_mkdir(&parent)
|
||||
%let parent = %substr(&dir, 1, %length(&dir)-%length(&child));
|
||||
%mf_mkdir(&parent)
|
||||
%end;
|
||||
|
||||
/*
|
||||
Now create the directory. Complain loudly of any errors.
|
||||
Now create the directory. Complain loudly of any errs.
|
||||
*/
|
||||
|
||||
%let dname = %sysfunc(dcreate(&child, &parent));
|
||||
%if (%bquote(&dname) eq ) %then %do;
|
||||
%put %str(ERR)OR: could not create &parent + &child;
|
||||
%abort cancel;
|
||||
%put %str(ERR)OR: could not create &parent + &child;
|
||||
%abort cancel;
|
||||
%end;
|
||||
%else %do;
|
||||
%put Directory created: &dir;
|
||||
%put Directory created: &dir;
|
||||
%end;
|
||||
%end;
|
||||
/* exit quietly if directory did exist.*/
|
||||
%mend;
|
||||
%mend mf_mkdir;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
/**
|
||||
@file mf_mval.sas
|
||||
@brief Returns a macro variable value if the variable exists
|
||||
@details Use this macro to avoid repetitive use of `%if %symexist(MACVAR) %then`
|
||||
type logic.
|
||||
@details
|
||||
Use this macro to avoid repetitive use of `%if %symexist(MACVAR) %then`
|
||||
type logic.
|
||||
Usage:
|
||||
|
||||
%if %mf_mval(maynotexist)=itdid %then %do;
|
||||
|
||||
@param [in] var The macro variable NAME to return the (possible) value for
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
@@ -15,4 +18,4 @@
|
||||
%if %symexist(&var) %then %do;
|
||||
%superq(&var)
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_mval;
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getattrn.sas
|
||||
|
||||
@param libds library.dataset
|
||||
@param [in] libds library.dataset
|
||||
|
||||
@return output returns result of the attrn value supplied, or log message
|
||||
if error.
|
||||
if err.
|
||||
|
||||
|
||||
@version 9.2
|
||||
@@ -23,4 +23,4 @@
|
||||
%macro mf_nobs(libds
|
||||
)/*/STORE SOURCE*/;
|
||||
%mf_getattrn(&libds,NLOBS)
|
||||
%mend;
|
||||
%mend mf_nobs;
|
||||
63
base/mf_readfile.sas
Normal file
63
base/mf_readfile.sas
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
@file
|
||||
@brief Reads the first line of a file using pure macro
|
||||
@details Reads the first line of a file and returns it. Future versions may
|
||||
read each line into a macro variable array.
|
||||
|
||||
Generally, reading data into macro variables is not great as certain
|
||||
nonprintable characters (such as CR, LF) may be dropped in the conversion.
|
||||
|
||||
Usage:
|
||||
|
||||
%mf_writefile(&sasjswork/myfile.txt,l1=some content,l2=more content)
|
||||
|
||||
%put %mf_readfile(&sasjswork/myfile.txt);
|
||||
|
||||
|
||||
@param [in] fpath Full path to file to be read
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_deletefile.sas
|
||||
@li mf_writefile.sas
|
||||
@li mf_readfile.test.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
%macro mf_readfile(fpath
|
||||
)/*/STORE SOURCE*/;
|
||||
%local fref rc fid fcontent;
|
||||
|
||||
/* check file exists */
|
||||
%if %sysfunc(filename(fref,&fpath)) ne 0 %then %do;
|
||||
%put &=fref &=fpath;
|
||||
%put %str(ERR)OR: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%let fid=%sysfunc(fopen(&fref,I));
|
||||
|
||||
%if &fid=0 %then %do;
|
||||
%put %str(ERR)OR: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if %sysfunc(fread(&fid)) = 0 %then %do;
|
||||
%let rc=%sysfunc(fget(&fid,fcontent,65534));
|
||||
&fcontent
|
||||
%end;
|
||||
|
||||
/*
|
||||
%do %while(%sysfunc(fread(&fid)) = 0);
|
||||
%let rc=%sysfunc(fget(&fid,fcontent,65534));
|
||||
&fcontent
|
||||
%end;
|
||||
*/
|
||||
|
||||
%let rc=%sysfunc(fclose(&fid));
|
||||
%let rc=%sysfunc(filename(&fref));
|
||||
|
||||
%mend mf_readfile;
|
||||
/** @endcond */
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
@file mf_trimstr.sas
|
||||
@brief Removes character(s) from the end, if they exist
|
||||
@details If the designated characters exist at the end of the string, they
|
||||
@details If the designated characters exist at the end of the string, they
|
||||
are removed
|
||||
|
||||
%put %mf_trimstr(/blah/,/); * /blah;
|
||||
@@ -11,8 +11,9 @@
|
||||
<h4> SAS Macros </h4>
|
||||
|
||||
|
||||
@param basestr The string to be modified
|
||||
@param trimstr The string to be removed from the end of `basestr`, if it exists
|
||||
@param [in] basestr The string to be modified
|
||||
@param [in] trimstr The string to be removed from the end of `basestr`, if it
|
||||
exists
|
||||
|
||||
@return output returns result with the value of `trimstr` removed from the end
|
||||
|
||||
@@ -46,4 +47,4 @@
|
||||
&basestr
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mf_trimstr;
|
||||
@@ -1,11 +1,11 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a Unique ID based on system time in a friendly format
|
||||
@brief Creates a unique ID based on system time in friendly format
|
||||
@details format = YYYYMMDD_HHMMSSmmm_<sysjobid>_<3randomDigits>
|
||||
|
||||
%put %mf_uid();
|
||||
|
||||
@version 9.2
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
@@ -14,8 +14,8 @@
|
||||
)/*/STORE SOURCE*/;
|
||||
%local today now;
|
||||
%let today=%sysfunc(today(),yymmddn8.);
|
||||
%let now=%sysfunc(compress(%sysfunc(time(),time12.3),:.));
|
||||
%let now=%sysfunc(compress(%sysfunc(time(),tod12.3),:.));
|
||||
|
||||
&today._&now._&sysjobid._%sysevalf(%sysfunc(ranuni(0))*999,CEIL)
|
||||
|
||||
%mend;
|
||||
%mend mf_uid;
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
@file
|
||||
@brief Checks if a set of macro variables exist / contain values.
|
||||
@brief Checks if a set of macro variables exist AND contain values.
|
||||
@details Writes ERROR to log if abortType is SOFT, else will call %mf_abort.
|
||||
Usage:
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_abort.sas
|
||||
|
||||
@param verifyvars space separated list of macro variable names
|
||||
@param makeupcase= set to YES to convert all variable VALUES to
|
||||
@param [in] verifyvars Space separated list of macro variable names
|
||||
@param [in] makeupcase= (NO) Set to YES to convert all variable VALUES to
|
||||
uppercase.
|
||||
@param mAbort= Abort Type. Default is SOFT (writes err to log).
|
||||
@param [in] mAbort= (SOFT) Abort Type. When SOFT, simply writes an err
|
||||
message to the log.
|
||||
Set to any other value to call mf_abort (which can be configured to abort in
|
||||
various fashions according to context).
|
||||
|
||||
@@ -35,7 +36,7 @@
|
||||
|
||||
|
||||
%macro mf_verifymacvars(
|
||||
verifyVars /* list of macro variable NAMES */
|
||||
verifyVars /* list of macro variable NAMES */
|
||||
,makeUpcase=NO /* set to YES to make all the variable VALUES uppercase */
|
||||
,mAbort=SOFT
|
||||
)/*/STORE SOURCE*/;
|
||||
@@ -58,8 +59,14 @@
|
||||
|
||||
%goto exit_success;
|
||||
%exit_err:
|
||||
%if &mAbort=SOFT %then %put %str(ERR)OR: &abortmsg;
|
||||
%else %mf_abort(mac=mf_verifymacvars,type=&mabort,msg=&abortmsg);
|
||||
%put &abortmsg;
|
||||
%mf_abort(iftrue=(&mabort ne SOFT),
|
||||
mac=mf_verifymacvars,
|
||||
msg=%str(&abortmsg)
|
||||
)
|
||||
0
|
||||
%return;
|
||||
%exit_success:
|
||||
1
|
||||
|
||||
%mend;
|
||||
%mend mf_verifymacvars;
|
||||
|
||||
53
base/mf_wordsinstr1andstr2.sas
Normal file
53
base/mf_wordsinstr1andstr2.sas
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns words that are in both string 1 and string 2
|
||||
@details Compares two space separated strings and returns the words that are
|
||||
in both.
|
||||
Usage:
|
||||
|
||||
%put %mf_wordsInStr1andStr2(
|
||||
Str1=blah sss blaaah brah bram boo
|
||||
,Str2= blah blaaah brah ssss
|
||||
);
|
||||
|
||||
returns:
|
||||
> blah blaaah brah
|
||||
|
||||
@param [in] str1= () string containing words to extract
|
||||
@param [in] str2= () used to compare with the extract string
|
||||
|
||||
@warning CASE SENSITIVE!
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_wordsInStr1andStr2(
|
||||
Str1= /* string containing words to extract */
|
||||
,Str2= /* used to compare with the extract string */
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local count_base count_extr i i2 extr_word base_word match outvar;
|
||||
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
|
||||
%put base string (str1)= &str1;
|
||||
%put compare string (str2) = &str2;
|
||||
%return;
|
||||
%end;
|
||||
%let count_base=%sysfunc(countw(&Str2));
|
||||
%let count_extr=%sysfunc(countw(&Str1));
|
||||
|
||||
%do i=1 %to &count_extr;
|
||||
%let extr_word=%scan(&Str1,&i,%str( ));
|
||||
%let match=0;
|
||||
%do i2=1 %to &count_base;
|
||||
%let base_word=%scan(&Str2,&i2,%str( ));
|
||||
%if &extr_word=&base_word %then %let match=1;
|
||||
%end;
|
||||
%if &match=1 %then %let outvar=&outvar &extr_word;
|
||||
%end;
|
||||
|
||||
&outvar
|
||||
|
||||
%mend mf_wordsInStr1andStr2;
|
||||
|
||||
@@ -3,20 +3,21 @@
|
||||
@brief Returns words that are in string 1 but not in string 2
|
||||
@details Compares two space separated strings and returns the words that are
|
||||
in the first but not in the second.
|
||||
|
||||
Note - case sensitive!
|
||||
|
||||
Usage:
|
||||
|
||||
%let x= %mf_wordsInStr1ButNotStr2(
|
||||
Str1=blah sss blaaah brah bram boo
|
||||
Str1=blah sss blaaah brah bram boo
|
||||
,Str2= blah blaaah brah ssss
|
||||
);
|
||||
|
||||
returns:
|
||||
> sss bram boo
|
||||
|
||||
@param str1= string containing words to extract
|
||||
@param str2= used to compare with the extract string
|
||||
|
||||
@warning CASE SENSITIVE!
|
||||
@param [in] str1= () String containing words to extract
|
||||
@param [in] str2= () Used to compare with the extract string
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -24,13 +25,12 @@
|
||||
**/
|
||||
|
||||
%macro mf_wordsInStr1ButNotStr2(
|
||||
Str1= /* string containing words to extract */
|
||||
,Str2= /* used to compare with the extract string */
|
||||
Str1= /* string containing words to extract */
|
||||
,Str2= /* used to compare with the extract string */
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local count_base count_extr i i2 extr_word base_word match outvar;
|
||||
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
|
||||
%put WARNING: empty string provided!;
|
||||
%put base string (str1)= &str1;
|
||||
%put compare string (str2) = &str2;
|
||||
%return;
|
||||
@@ -50,5 +50,5 @@
|
||||
|
||||
&outvar
|
||||
|
||||
%mend;
|
||||
%mend mf_wordsInStr1ButNotStr2;
|
||||
|
||||
|
||||
67
base/mf_writefile.sas
Normal file
67
base/mf_writefile.sas
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a text file using pure macro
|
||||
@details Creates a text file of up to 10 lines. If further lines are
|
||||
desired, feel free to [create an issue](
|
||||
https://github.com/sasjs/core/issues/new), or make a pull request!
|
||||
|
||||
The use of PARMBUFF was considered for this macro, but it would have made
|
||||
things problematic for writing lines containing commas.
|
||||
|
||||
Usage:
|
||||
|
||||
%mf_writefile(&sasjswork/myfile.txt,l1=some content,l2=more content)
|
||||
data _null_;
|
||||
infile "&sasjswork/myfile.txt";
|
||||
input;
|
||||
list;
|
||||
run;
|
||||
|
||||
@param [in] fpath Full path to file to be created or appended to
|
||||
@param [in] mode= (O) Available options are A or O as follows:
|
||||
@li A APPEND mode, writes new records after the current end of the file.
|
||||
@li O OUTPUT mode, writes new records from the beginning of the file.
|
||||
@param [in] l1= () First line
|
||||
@param [in] l2= () Second line (etc through to l10)
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_writefile.test.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
%macro mf_writefile(fpath,mode=O,l1=,l2=,l3=,l4=,l5=,l6=,l7=,l8=,l9=,l10=
|
||||
)/*/STORE SOURCE*/;
|
||||
%local fref rc fid i total_lines;
|
||||
|
||||
/* find number of lines by reference to first non-blank param */
|
||||
%do i=10 %to 1 %by -1;
|
||||
%if %str(&&l&i) ne %str() %then %goto continue;
|
||||
%end;
|
||||
%continue:
|
||||
%let total_lines=&i;
|
||||
|
||||
%if %sysfunc(filename(fref,&fpath)) ne 0 %then %do;
|
||||
%put &=fref &=fpath;
|
||||
%put %str(ERR)OR: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%let fid=%sysfunc(fopen(&fref,&mode));
|
||||
|
||||
%if &fid=0 %then %do;
|
||||
%put %str(ERR)OR: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%do i=1 %to &total_lines;
|
||||
%let rc=%sysfunc(fput(&fid, &&l&i));
|
||||
%let rc=%sysfunc(fwrite(&fid));
|
||||
%end;
|
||||
%let rc=%sysfunc(fclose(&fid));
|
||||
%let rc=%sysfunc(filename(&fref));
|
||||
|
||||
%mend mf_writefile;
|
||||
/** @endcond */
|
||||
@@ -4,152 +4,335 @@
|
||||
@details Configures an abort mechanism according to site specific policies or
|
||||
the particulars of an environment. For instance, can stream custom
|
||||
results back to the client in an STP Web App context, or completely stop
|
||||
in the case of a batch run.
|
||||
in the case of a batch run. For STP sessions
|
||||
|
||||
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process
|
||||
environments. This macro takes a unique approach - we set the SAS syscc to 0,
|
||||
run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro
|
||||
but don't close it! This provides a graceful abort for SAS web services in all
|
||||
web enabled environments.
|
||||
The method used varies according to the context. Important points:
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param msg= message to be returned
|
||||
@param iftrue= supply a condition under which the macro should be executed.
|
||||
@li should not use endsas or abort cancel in 9.4m3 WIN environments as this
|
||||
can cause hung multibridge sessions and result in a frozen STP server
|
||||
@li The use of endsas in 9.4m6+ windows environments for POST requests to the
|
||||
STP server can result in an empty response body
|
||||
@li should not use endsas in viya 3.5 as this destroys the session and cannot
|
||||
fetch results (although both mv_getjoblog.sas and the @sasjs/adapter will
|
||||
recognise this and fetch the log of the parent session instead)
|
||||
@li STP environments must finish cleanly to avoid the log being sent to
|
||||
_webout. To assist with this, we also run stpsrvset('program error', 0)
|
||||
and set SYSCC=0.
|
||||
Where possible, we take a unique "soft abort" approach - we open a macro
|
||||
but don't close it! This works everywhere EXCEPT inside a \%include inside
|
||||
a macro. For that, we recommend you use mp_include.sas to perform the
|
||||
include, and then call \%mp_abort(mode=INCLUDE) from the source program (ie,
|
||||
OUTSIDE of the top-parent macro).
|
||||
The soft abort has become ineffective in 9.4m6 WINDOWS environments. We are
|
||||
currently investigating approaches to deal with this.
|
||||
|
||||
@version 9.4M3
|
||||
|
||||
@param [in] mac= (mp_abort.sas) To contain the name of the calling macro. Do
|
||||
not use &sysmacroname as this will always resolve to MP_ABORT.
|
||||
@param [out] msg= message to be returned
|
||||
@param [in] iftrue= (1=1) Condition under which the macro should be executed
|
||||
@param [in] errds= (work.mp_abort_errds) There is no clean way to end a
|
||||
process within a %include called within a macro. Furthermore, there is no
|
||||
way to test if a macro is called within a %include. To handle this
|
||||
particular scenario, the %include should be switched for the mp_include.sas
|
||||
macro.
|
||||
This provides an indicator that we are running a macro within a \%include
|
||||
(`_SYSINCLUDEFILEDEVICE`) and allows us to provide a dataset with the abort
|
||||
values (msg, mac).
|
||||
We can then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back to the calling program to run a regular \%mp_abort().
|
||||
The dataset will contain the following fields:
|
||||
@li iftrue (1=1)
|
||||
@li msg (the message)
|
||||
@li mac (the mac param)
|
||||
|
||||
@param [in] mode= (REGULAR) If mode=INCLUDE then the &errds dataset is checked
|
||||
for an abort status.
|
||||
Valid values:
|
||||
@li REGULAR (default)
|
||||
@li INCLUDE
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_include.sas
|
||||
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
, errds=work.mp_abort_errds
|
||||
, mode=REGULAR
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
%global sysprocessmode sysprocessname sasjs_stpsrv_header_loc sasjsprocessmode;
|
||||
%local fref fid i;
|
||||
|
||||
%put NOTE: /// mp_abort macro executing //;
|
||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||
%put NOTE - &msg;
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson)
|
||||
or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" )
|
||||
%put NOTE: /// mp_abort macro executing //;
|
||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||
%put NOTE - &msg;
|
||||
|
||||
%if %symexist(_SYSINCLUDEFILEDEVICE)
|
||||
/* abort cancel FILE does not restart outside the INCLUDE on Viya 3.5 */
|
||||
and %superq(SYSPROCESSNAME) ne %str(Compute Server)
|
||||
%then %do;
|
||||
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
|
||||
data &errds;
|
||||
iftrue='1=1';
|
||||
length mac $100 msg $5000;
|
||||
mac=symget('mac');
|
||||
msg=symget('msg');
|
||||
run;
|
||||
data _null_;
|
||||
abort cancel FILE;
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* Web App Context */
|
||||
%if %symexist(_PROGRAM)
|
||||
or %superq(SYSPROCESSNAME) = %str(Compute Server)
|
||||
or &mode=INCLUDE
|
||||
%then %do;
|
||||
options obs=max replace mprint;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5"
|
||||
%then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
/* extract log errs / warns, if exist */
|
||||
%local logloc logline;
|
||||
%global logmsg; /* capture global messages */
|
||||
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
|
||||
%else %let logloc=%qsysfunc(getoption(LOG));
|
||||
proc printto log=log;run;
|
||||
%if %length(&logloc)>0 %then %do;
|
||||
%let logline=0;
|
||||
options nosyntaxcheck;
|
||||
%end;
|
||||
|
||||
%if &mode=INCLUDE %then %do;
|
||||
%if %sysfunc(exist(&errds))=1 %then %do;
|
||||
data _null_;
|
||||
set &errds;
|
||||
call symputx('iftrue',iftrue,'l');
|
||||
call symputx('mac',mac,'l');
|
||||
call symputx('msg',msg,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if (&iftrue)=0 %then %return;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname: No include errors found;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* extract log errs / warns, if exist */
|
||||
%local logloc logline;
|
||||
%global logmsg; /* capture global messages */
|
||||
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
|
||||
%else %let logloc=%qsysfunc(getoption(LOG));
|
||||
proc printto log=log;run;
|
||||
%let logline=0;
|
||||
%if %length(&logloc)>0 %then %do;
|
||||
data _null_;
|
||||
infile &logloc lrecl=5000;
|
||||
input; putlog _infile_;
|
||||
i=1;
|
||||
retain logonce 0;
|
||||
if (
|
||||
_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR"
|
||||
) and logonce=0 then
|
||||
do;
|
||||
call symputx('logline',_n_);
|
||||
logonce+1;
|
||||
end;
|
||||
run;
|
||||
/* capture log including lines BEFORE the err */
|
||||
%if &logline>0 %then %do;
|
||||
data _null_;
|
||||
infile &logloc lrecl=5000;
|
||||
input; putlog _infile_;
|
||||
input;
|
||||
i=1;
|
||||
retain logonce 0;
|
||||
if (_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR") and logonce=0 then do;
|
||||
call symputx('logline',_n_);
|
||||
logonce+1;
|
||||
end;
|
||||
run;
|
||||
/* capture log including lines BEFORE the err */
|
||||
%if &logline>0 %then %do;
|
||||
data _null_;
|
||||
infile &logloc lrecl=5000;
|
||||
stoploop=0;
|
||||
if _n_ ge &logline-15 and stoploop=0 then do until (i>22);
|
||||
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
|
||||
input;
|
||||
i=1;
|
||||
stoploop=0;
|
||||
if _n_ ge &logline-5 and stoploop=0 then do until (i>12);
|
||||
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
|
||||
input;
|
||||
i+1;
|
||||
stoploop=1;
|
||||
end;
|
||||
if stoploop=1 then stop;
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
||||
/* setup webout */
|
||||
OPTIONS NOBOMFILE;
|
||||
%if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
|
||||
filename _webout temp lrecl=999999 mod;
|
||||
%end;
|
||||
%else %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
name="_webout.json" lrecl=999999 mod;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* send response in SASjs JSON format */
|
||||
data _null_;
|
||||
file _webout mod lrecl=32000;
|
||||
length msg $32767 debug $8;
|
||||
sasdatetime=datetime();
|
||||
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
||||
/* escape the quotes */
|
||||
msg=tranwrd(msg,'"','\"');
|
||||
/* ditch the CRLFs as chrome complains */
|
||||
msg=compress(msg,,'kw');
|
||||
/* quote without quoting the quotes (which are escaped instead) */
|
||||
msg=cats('"',msg,'"');
|
||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||
else debug='""';
|
||||
if debug ge '"131"' then put '>>weboutBEGIN<<';
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
put ',"sasjsAbort" : [{';
|
||||
put ' "MSG":' msg ;
|
||||
put ' ,"MAC": "' "&mac" '"}]';
|
||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||
put ',"_DEBUG":' debug ;
|
||||
if symexist('_metauser') then do;
|
||||
_METAUSER=quote(trim(symget('_METAUSER')));
|
||||
put ",""_METAUSER"": " _METAUSER;
|
||||
_METAPERSON=quote(trim(symget('_METAPERSON')));
|
||||
put ',"_METAPERSON": ' _METAPERSON;
|
||||
end;
|
||||
if symexist('SYS_JES_JOB_URI') then do;
|
||||
SYS_JES_JOB_URI=quote(trim(symget('SYS_JES_JOB_URI')));
|
||||
put ',"SYS_JES_JOB_URI": ' SYS_JES_JOB_URI;
|
||||
end;
|
||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put "}" @;
|
||||
if debug ge '"131"' then put '>>weboutEND<<';
|
||||
run;
|
||||
|
||||
%let syscc=0;
|
||||
%if %symexist(_metaport) %then %do;
|
||||
data _null_;
|
||||
if symexist('sysprocessmode')
|
||||
then if symget("sysprocessmode")="SAS Stored Process Server"
|
||||
then rc=stpsrvset('program error', 0);
|
||||
i+1;
|
||||
stoploop=1;
|
||||
end;
|
||||
if stoploop=1 then stop;
|
||||
run;
|
||||
%end;
|
||||
/**
|
||||
* endsas is reliable but kills some deployments.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
*/
|
||||
%put _all_;
|
||||
filename skip temp;
|
||||
%end;
|
||||
|
||||
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
||||
/* setup webout for Viya */
|
||||
options nobomfile;
|
||||
%if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
|
||||
filename _webout temp lrecl=999999 mod;
|
||||
%end;
|
||||
%else %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
name="_webout.json" lrecl=999999 mod;
|
||||
%end;
|
||||
%end;
|
||||
%else %if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc))=0 %then %do;
|
||||
options nobomfile;
|
||||
/* set up http header for SASjs Server */
|
||||
%let fid=%sysfunc(fopen(&fref,A));
|
||||
%if &fid=0 %then %do;
|
||||
%put %str(ERR)OR: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
%let rc=%sysfunc(fput(&fid,%str(Content-Type: application/json)));
|
||||
%let rc=%sysfunc(fwrite(&fid));
|
||||
%let rc=%sysfunc(fclose(&fid));
|
||||
%let rc=%sysfunc(filename(&fref));
|
||||
%end;
|
||||
|
||||
/* send response in SASjs JSON format */
|
||||
data _null_;
|
||||
file _webout mod lrecl=32000 encoding='utf-8';
|
||||
length msg syswarningtext syserrortext $32767 mode $10 ;
|
||||
sasdatetime=datetime();
|
||||
msg=symget('msg');
|
||||
%if &logline>0 %then %do;
|
||||
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
|
||||
%end;
|
||||
/* escape the escapes */
|
||||
msg=tranwrd(msg,'\','\\');
|
||||
/* escape the quotes */
|
||||
msg=tranwrd(msg,'"','\"');
|
||||
/* ditch the CRLFs as chrome complains */
|
||||
msg=compress(msg,,'kw');
|
||||
/* quote without quoting the quotes (which are escaped instead) */
|
||||
msg=cats('"',msg,'"');
|
||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||
else debug='""';
|
||||
if symget('sasjsprocessmode')='Stored Program' then mode='SASJS';
|
||||
if mode ne 'SASJS' then put '>>weboutBEGIN<<';
|
||||
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
||||
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
||||
put ',"sasjsAbort" : [{';
|
||||
put ' "MSG":' msg ;
|
||||
put ' ,"MAC": "' "&mac" '"}]';
|
||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||
put ',"_DEBUG":' debug ;
|
||||
if symexist('_metauser') then do;
|
||||
_METAUSER=quote(trim(symget('_METAUSER')));
|
||||
put ",""_METAUSER"": " _METAUSER;
|
||||
_METAPERSON=quote(trim(symget('_METAPERSON')));
|
||||
put ',"_METAPERSON": ' _METAPERSON;
|
||||
end;
|
||||
if symexist('SYS_JES_JOB_URI') then do;
|
||||
SYS_JES_JOB_URI=quote(trim(symget('SYS_JES_JOB_URI')));
|
||||
put ',"SYS_JES_JOB_URI": ' SYS_JES_JOB_URI;
|
||||
end;
|
||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
syserrortext=cats(symget('syserrortext'));
|
||||
if findc(syserrortext,'"\'!!'0A0D09000E0F010210111A'x) then do;
|
||||
syserrortext='"'!!trim(
|
||||
prxchange('s/"/\\"/',-1, /* double quote */
|
||||
prxchange('s/\x0A/\n/',-1, /* new line */
|
||||
prxchange('s/\x0D/\r/',-1, /* carriage return */
|
||||
prxchange('s/\x09/\\t/',-1, /* tab */
|
||||
prxchange('s/\x00/\\u0000/',-1, /* NUL */
|
||||
prxchange('s/\x0E/\\u000E/',-1, /* SS */
|
||||
prxchange('s/\x0F/\\u000F/',-1, /* SF */
|
||||
prxchange('s/\x01/\\u0001/',-1, /* SOH */
|
||||
prxchange('s/\x02/\\u0002/',-1, /* STX */
|
||||
prxchange('s/\x10/\\u0010/',-1, /* DLE */
|
||||
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
|
||||
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
|
||||
prxchange('s/\\/\\\\/',-1,syserrortext)
|
||||
)))))))))))))!!'"';
|
||||
end;
|
||||
else syserrortext=cats('"',syserrortext,'"');
|
||||
put ',"SYSERRORTEXT" : ' syserrortext;
|
||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||
put ",""SYSSITE"" : ""&syssite"" ";
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
syswarningtext=cats(symget('syswarningtext'));
|
||||
if findc(syswarningtext,'"\'!!'0A0D09000E0F010210111A'x) then do;
|
||||
syswarningtext='"'!!trim(
|
||||
prxchange('s/"/\\"/',-1, /* double quote */
|
||||
prxchange('s/\x0A/\n/',-1, /* new line */
|
||||
prxchange('s/\x0D/\r/',-1, /* carriage return */
|
||||
prxchange('s/\x09/\\t/',-1, /* tab */
|
||||
prxchange('s/\x00/\\u0000/',-1, /* NUL */
|
||||
prxchange('s/\x0E/\\u000E/',-1, /* SS */
|
||||
prxchange('s/\x0F/\\u000F/',-1, /* SF */
|
||||
prxchange('s/\x01/\\u0001/',-1, /* SOH */
|
||||
prxchange('s/\x02/\\u0002/',-1, /* STX */
|
||||
prxchange('s/\x10/\\u0010/',-1, /* DLE */
|
||||
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
|
||||
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
|
||||
prxchange('s/\\/\\\\/',-1,syswarningtext)
|
||||
)))))))))))))!!'"';
|
||||
end;
|
||||
else syswarningtext=cats('"',syswarningtext,'"');
|
||||
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
put "}" ;
|
||||
if mode ne 'SASJS' then put '>>weboutEND<<';
|
||||
run;
|
||||
|
||||
%put _all_;
|
||||
|
||||
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip(); %macro skippy();';
|
||||
putlog 'stpsrvset program err and syscc';
|
||||
rc=stpsrvset('program error', 0);
|
||||
call symputx("syscc",0,"g");
|
||||
run;
|
||||
%if &sysscp=WIN
|
||||
and 1=0 /* deprecating this logic until we figure out a consistent abort */
|
||||
and "%substr(%str(&sysvlong ),1,8)"="9.04.01M"
|
||||
and "%substr(%str(&sysvlong ),9,1)">"5" %then %do;
|
||||
/* skip approach (below) does not work in windows m6+ envs */
|
||||
endsas;
|
||||
%end;
|
||||
%else %do;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* Use mp_include() to handle this.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%end;
|
||||
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
||||
/* endsas kills the session making it harder to fetch results */
|
||||
data _null_;
|
||||
syswarningtext=symget('syswarningtext');
|
||||
syserrortext=symget('syserrortext');
|
||||
abort_msg=symget('msg');
|
||||
syscc=symget('syscc');
|
||||
sysuserid=symget('sysuserid');
|
||||
iftrue=symget('iftrue');
|
||||
put (_all_)(/=);
|
||||
call symputx('syscc',0);
|
||||
abort cancel nolist;
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %do;
|
||||
%put _all_;
|
||||
%abort cancel;
|
||||
%end;
|
||||
%mend;
|
||||
%end;
|
||||
%else %do;
|
||||
%put _all_;
|
||||
%abort cancel;
|
||||
%end;
|
||||
%mend mp_abort;
|
||||
|
||||
/** @endcond */
|
||||
/** @endcond */
|
||||
|
||||
95
base/mp_aligndecimal.sas
Normal file
95
base/mp_aligndecimal.sas
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
@file
|
||||
@brief Apply leading blanks to align numbers vertically in a char variable
|
||||
@details This is particularly useful when storing numbers (as character) that
|
||||
need to be sorted.
|
||||
|
||||
It works by splitting the number left and right of the decimal place, and
|
||||
aligning it accordingly. A temporary variable is created as part of this
|
||||
process (which is automatically dropped)
|
||||
|
||||
The macro can be used only in data step, eg as follows:
|
||||
|
||||
data _null_;
|
||||
length myvar $50;
|
||||
do i=1 to 1000 by 50;
|
||||
if mod(i,2)=0 then j=ranuni(0)*i*100;
|
||||
else j=i*100;
|
||||
|
||||
%mp_aligndecimal(myvar,width=7)
|
||||
|
||||
leading_spaces=length(myvar)-length(cats(myvar));
|
||||
putlog +leading_spaces myvar;
|
||||
end;
|
||||
run;
|
||||
|
||||
The generated code will look something like this:
|
||||
|
||||
length aligndp4e49996 $7;
|
||||
if index(myvar,'.') then do;
|
||||
aligndp4e49996=cats(scan(myvar,1,'.'));
|
||||
aligndp4e49996=right(aligndp4e49996);
|
||||
myvar=aligndp4e49996!!'.'!!cats(scan(myvar,2,'.'));
|
||||
end;
|
||||
else do;
|
||||
aligndp4e49996=myvar;
|
||||
aligndp4e49996=right(aligndp4e49996);
|
||||
myvar=aligndp4e49996;
|
||||
end;
|
||||
drop aligndp4e49996;
|
||||
|
||||
Results (myvar variable):
|
||||
|
||||
0.7683559324
|
||||
122.8232796
|
||||
99419.50552
|
||||
42938.5143414
|
||||
763.3799189
|
||||
15170.606073
|
||||
15083.285773
|
||||
85443.198707
|
||||
2022999.2251
|
||||
12038.658867
|
||||
1350582.6734
|
||||
52777.258221
|
||||
11723.347628
|
||||
33101.268376
|
||||
6181622.8603
|
||||
7390614.0669
|
||||
73384.537893
|
||||
1788362.1016
|
||||
2774586.2219
|
||||
7998580.8415
|
||||
|
||||
|
||||
@param [in] var The (data step, character) variable to modify
|
||||
@param [in] width= (8) The number of characters BEFORE the decimal point
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquename.sas
|
||||
|
||||
<h4> Related Programs </h4>
|
||||
@li mp_aligndecimal.test.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mp_aligndecimal(var,width=8);
|
||||
|
||||
%local tmpvar;
|
||||
%let tmpvar=%mf_getuniquename(prefix=aligndp);
|
||||
length &tmpvar $&width;
|
||||
if index(&var,'.') then do;
|
||||
&tmpvar=cats(scan(&var,1,'.'));
|
||||
&tmpvar=right(&tmpvar);
|
||||
&var=&tmpvar!!'.'!!cats(scan(&var,2,'.'));
|
||||
end;
|
||||
else do;
|
||||
&tmpvar=cats(&var);
|
||||
&tmpvar=right(&tmpvar);
|
||||
&var=&tmpvar;
|
||||
end;
|
||||
drop &tmpvar;
|
||||
|
||||
%mend mp_aligndecimal;
|
||||
57
base/mp_appendfile.sas
Normal file
57
base/mp_appendfile.sas
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
@file
|
||||
@brief Append (concatenate) two or more files.
|
||||
@details Will append one more more `appendrefs` (filerefs) to a `baseref`.
|
||||
Uses a binary mechanism, so will work with any file type. For that reason -
|
||||
use with care! And supply your own trailing carriage returns in each file..
|
||||
|
||||
Usage:
|
||||
|
||||
filename tmp1 temp;
|
||||
filename tmp2 temp;
|
||||
filename tmp3 temp;
|
||||
data _null_; file tmp1; put 'base file';
|
||||
data _null_; file tmp2; put 'append1';
|
||||
data _null_; file tmp3; put 'append2';
|
||||
run;
|
||||
%mp_appendfile(baseref=tmp1, appendrefs=tmp2 tmp3)
|
||||
|
||||
|
||||
@param [in] baseref= (0) Fileref of the base file (should exist)
|
||||
@param [in] appendrefs= (0) One or more filerefs to be appended to the base
|
||||
fileref. Space separated.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mp_binarycopy.sas
|
||||
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_appendfile(
|
||||
baseref=0,
|
||||
appendrefs=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%mp_abort(iftrue= (&baseref=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Baseref NOT specified!)
|
||||
)
|
||||
%mp_abort(iftrue= (&appendrefs=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Appendrefs NOT specified!)
|
||||
)
|
||||
|
||||
%local i;
|
||||
%do i=1 %to %sysfunc(countw(&appendrefs));
|
||||
%mp_abort(iftrue= (&syscc>0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(syscc=&syscc)
|
||||
)
|
||||
%mp_binarycopy(inref=%scan(&appendrefs,&i), outref=&baseref, mode=APPEND)
|
||||
%end;
|
||||
|
||||
%mend mp_appendfile;
|
||||
181
base/mp_applyformats.sas
Normal file
181
base/mp_applyformats.sas
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
@file
|
||||
@brief Apply a set of formats to a table
|
||||
@details Applies a set of formats to the metadata of one or more SAS datasets.
|
||||
Can be used to migrate formats from one table to another. The input table
|
||||
must contain the following columns:
|
||||
|
||||
@li lib - the libref of the table to be updated
|
||||
@li ds - the dataset to be updated
|
||||
@li var - the variable to be updated
|
||||
@li fmt - the format to apply. Missing or default ($CHAR, 8.) formats are
|
||||
ignored.
|
||||
|
||||
The macro will abort in the following scenarios:
|
||||
|
||||
@li Libref not assigned
|
||||
@li Dataset does not exist
|
||||
@li Input table contains null or invalid values
|
||||
|
||||
Example usage:
|
||||
|
||||
data work.example;
|
||||
set sashelp.prdsale;
|
||||
format _all_ clear;
|
||||
run;
|
||||
|
||||
%mp_getcols(sashelp.prdsale,outds=work.cols)
|
||||
|
||||
data work.cols2;
|
||||
set work.cols;
|
||||
lib='WORK';
|
||||
ds='EXAMPLE';
|
||||
var=name;
|
||||
fmt=format;
|
||||
keep lib ds var fmt;
|
||||
run;
|
||||
|
||||
%mp_applyformats(work.cols2)
|
||||
|
||||
@param [in] inds The input dataset containing the formats to apply (and where
|
||||
to apply them). Example structure:
|
||||
|LIB:$8.|DS:$32.|VAR:$32.|FMT:$49.|
|
||||
|---|---|---|---|
|
||||
|`WORK `|`EXAMPLE `|`ACTUAL `|`DOLLAR12.2 `|
|
||||
|`WORK `|`EXAMPLE `|`COUNTRY `|`$CHAR10. `|
|
||||
|`WORK `|`EXAMPLE `|`DIVISION `|`$CHAR10. `|
|
||||
|`WORK `|`EXAMPLE `|`MONTH `|`MONNAME3. `|
|
||||
|`WORK `|`EXAMPLE `|`PREDICT `|`DOLLAR12.2 `|
|
||||
|`WORK `|`EXAMPLE `|`PRODTYPE `|`$CHAR10. `|
|
||||
|`WORK `|`EXAMPLE `|`PRODUCT `|`$CHAR10. `|
|
||||
|`WORK `|`EXAMPLE `|`QUARTER `|`8. `|
|
||||
|`WORK `|`EXAMPLE `|`REGION `|`$CHAR10. `|
|
||||
|`WORK `|`EXAMPLE `|`YEAR `|`8. `|
|
||||
|
||||
@param [out] errds= (0) Provide a libds reference here to export the
|
||||
error messages to a table. In this case, they will not be printed to the
|
||||
log.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getengine.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_nobs.sas
|
||||
@li mp_validatecol.sas
|
||||
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_getformats.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_applyformats(inds,errds=0
|
||||
)/*/STORE SOURCE*/;
|
||||
%local outds liblist i engine lib msg ;
|
||||
|
||||
/**
|
||||
* Validations
|
||||
*/
|
||||
proc sort data=&inds;
|
||||
by lib ds var fmt;
|
||||
run;
|
||||
|
||||
%if &errds=0 %then %let outds=%mf_getuniquename(prefix=mp_applyformats);
|
||||
%else %let outds=&errds;
|
||||
|
||||
data &outds;
|
||||
set &inds;
|
||||
where fmt not in ('','.', '$', '$CHAR.','8.');
|
||||
length msg $128;
|
||||
by lib ds var fmt;
|
||||
if libref(lib) ne 0 then do;
|
||||
msg=catx(' ','libref',lib,'is not assigned!');
|
||||
%if &errds=0 %then %do;
|
||||
putlog 'ERR' +(-1) "OR: " msg;
|
||||
%end;
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
if exist(cats(lib,'.',ds)) ne 1 then do;
|
||||
msg=catx(' ','libds',lib,'.',ds,'does not exist!');
|
||||
%if &errds=0 %then %do;
|
||||
putlog 'ERR' +(-1) "OR: " msg;
|
||||
%end;
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
%mp_validatecol(fmt,FORMAT,is_fmt)
|
||||
if is_fmt=0 then do;
|
||||
msg=catx(' ','format',fmt,'on libds',lib,'.',ds,'.',var,'is not valid!');
|
||||
%if &errds=0 %then %do;
|
||||
putlog 'ERR' +(-1) "OR: " msg;
|
||||
%end;
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
|
||||
if first.ds then do;
|
||||
retain dsid;
|
||||
dsid=open(cats(lib,'.',ds));
|
||||
if dsid=0 then do;
|
||||
msg=catx(' ','libds',lib,'.',ds,' could not be opened!');
|
||||
%if &errds=0 %then %do;
|
||||
putlog 'ERR' +(-1) "OR: " msg;
|
||||
%end;
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
if varnum(dsid,var)<1 then do;
|
||||
msg=catx(' ','Variable',lib,'.',ds,'.',var,' was not found!');
|
||||
%if &errds=0 %then %do;
|
||||
putlog 'ERR' +(-1) "OR: " msg;
|
||||
%end;
|
||||
output;
|
||||
end;
|
||||
end;
|
||||
if last.ds then rc=close(dsid);
|
||||
run;
|
||||
|
||||
proc sql noprint;
|
||||
select distinct lib into: liblist separated by ' ' from &inds;
|
||||
%put &=liblist;
|
||||
%if %length(&liblist)>0 %then %do i=1 %to %sysfunc(countw(&liblist));
|
||||
%let lib=%scan(&liblist,1);
|
||||
%let engine=%mf_getengine(&lib);
|
||||
%if &engine ne V9 and &engine ne BASE %then %do;
|
||||
%let msg=&lib has &engine engine - formats cannot be applied;
|
||||
insert into &outds set lib="&lib",ds="_all_",var="_all", msg="&msg" ;
|
||||
%if &errds=0 %then %put %str(ERR)OR: &msg;
|
||||
%end;
|
||||
%end;
|
||||
quit;
|
||||
|
||||
%if %mf_nobs(&outds)>0 %then %return;
|
||||
|
||||
/**
|
||||
* Validations complete - now apply the actual formats!
|
||||
*/
|
||||
%let fref=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
set &inds;
|
||||
by lib ds var fmt;
|
||||
where fmt not in ('','.', '$', '$CHAR.','8.');
|
||||
file &fref;
|
||||
if first.lib then put 'proc datasets nolist lib=' lib ';';
|
||||
if first.ds then put ' modify ' ds ';';
|
||||
put ' format ' var fmt ';';
|
||||
if last.ds then put ' run;';
|
||||
if last.lib then put 'quit;';
|
||||
run;
|
||||
|
||||
%inc &fref/source2;
|
||||
|
||||
%if &errds=0 %then %do;
|
||||
proc sql;
|
||||
drop table &outds;
|
||||
%end;
|
||||
|
||||
%mend mp_applyformats;
|
||||
56
base/mp_assert.sas
Normal file
56
base/mp_assert.sas
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
@file
|
||||
@brief Generic assertion
|
||||
@details Useful in the context of writing sasjs tests. The results of the
|
||||
test are _appended_ to the &outds. table.
|
||||
|
||||
Example usage:
|
||||
|
||||
%mp_assert(iftrue=(1=1),
|
||||
desc=Obviously true
|
||||
)
|
||||
|
||||
%mp_assert(iftrue=(1=0),
|
||||
desc=Will fail
|
||||
)
|
||||
|
||||
@param [in] iftrue= (1=1) A condition where, if true, the test is a PASS.
|
||||
Else, the test is a fail.
|
||||
|
||||
@param [in] desc= (Testing observations) The user provided test description
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
results. If it does not exist, it will be created, with the following format:
|
||||
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||
|---|---|---|
|
||||
|User Provided description|PASS|Dataset &inds contained ALL columns|
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_assert(iftrue=(1=1),
|
||||
desc=0,
|
||||
outds=work.test_results
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data ;
|
||||
length test_description $256 test_result $4 test_comments $256;
|
||||
test_description=symget('desc');
|
||||
test_comments="&sysmacroname: Test result of "!!symget('iftrue');
|
||||
%if %eval(%unquote(&iftrue)) %then %do;
|
||||
test_result='PASS';
|
||||
%end;
|
||||
%else %do;
|
||||
test_result='FAIL';
|
||||
%end;
|
||||
run;
|
||||
|
||||
%local ds ;
|
||||
%let ds=&syslast;
|
||||
proc append base=&outds data=&ds;
|
||||
run;
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
|
||||
%mend mp_assert;
|
||||
145
base/mp_assertcols.sas
Normal file
145
base/mp_assertcols.sas
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
@file
|
||||
@brief Asserts the existence (or not) of columns
|
||||
@details Useful in the context of writing sasjs tests. The results of the
|
||||
test are _appended_ to the &outds. table.
|
||||
|
||||
Example usage:
|
||||
|
||||
%mp_assertcols(sashelp.class,
|
||||
cols=name age sex,
|
||||
test=ALL,
|
||||
desc=check all columns exist
|
||||
)
|
||||
|
||||
%mp_assertcols(sashelp.class,
|
||||
cols=a b c,
|
||||
test=NONE
|
||||
)
|
||||
|
||||
%mp_assertcols(sashelp.class,
|
||||
cols=age depth,
|
||||
test=ANY
|
||||
)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existds.sas
|
||||
@li mf_existvarlist.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_wordsinstr1butnotstr2.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
|
||||
@param [in] inds The input library.dataset to test for values
|
||||
@param [in] cols= (0) The list of columns to check for
|
||||
@param [in] desc= (0) The user provided test description
|
||||
@param [in] test= (ALL) The test to apply. Valid values are:
|
||||
@li ALL - Test is a PASS if ALL columns exist in &inds
|
||||
@li ANY - Test is a PASS if ANY of the columns exist in &inds
|
||||
@li NONE - Test is a PASS if NONE of the columns exist in &inds
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
results. If it does not exist, it will be created, with the following format:
|
||||
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||
|---|---|---|
|
||||
|User Provided description|PASS|Dataset &inds contained ALL columns|
|
||||
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assertcolvals.sas
|
||||
@li mp_assertdsobs.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_assertcols(inds,
|
||||
cols=0,
|
||||
test=ALL,
|
||||
desc=0,
|
||||
outds=work.test_results
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(syscc=&syscc - on macro entry)
|
||||
)
|
||||
|
||||
%local lib ds ;
|
||||
%let lib=%scan(&inds,1,%str(.));
|
||||
%let ds=%scan(&inds,2,%str(.));
|
||||
%let cols=%upcase(&cols);
|
||||
|
||||
%mp_abort(iftrue= (%mf_existds(&lib..&ds)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&lib..&ds not found!)
|
||||
)
|
||||
|
||||
%mp_abort(iftrue= (&cols=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(No cols provided)
|
||||
)
|
||||
|
||||
|
||||
%let test=%upcase(&test);
|
||||
|
||||
%if &test ne ANY and &test ne ALL and &test ne NONE %then %do;
|
||||
%mp_abort(
|
||||
mac=&sysmacroname,
|
||||
msg=%str(Invalid test - &test)
|
||||
)
|
||||
%end;
|
||||
|
||||
/**
|
||||
* now do the actual test!
|
||||
*/
|
||||
%local result;
|
||||
%if %mf_existVarList(&inds,&cols)=1 %then %let result=ALL;
|
||||
%else %do;
|
||||
%local targetcols compare;
|
||||
%let targetcols=%upcase(%mf_getvarlist(&inds));
|
||||
%let compare=%mf_wordsinstr1butnotstr2(
|
||||
Str1=&cols,
|
||||
Str2=&targetcols
|
||||
);
|
||||
%if %cmpres(&compare)=%cmpres(&cols) %then %let result=NONE;
|
||||
%else %let result=SOME;
|
||||
%end;
|
||||
|
||||
data;
|
||||
length test_description $256 test_result $4 test_comments $256;
|
||||
test_description=symget('desc');
|
||||
if test_description='0'
|
||||
then test_description="Testing &inds for existence of &test of: &cols";
|
||||
|
||||
test_result='FAIL';
|
||||
test_comments="&sysmacroname: &inds has &result columns ";
|
||||
%if &test=ALL %then %do;
|
||||
%if &result=ALL %then %do;
|
||||
test_result='PASS';
|
||||
%end;
|
||||
%end;
|
||||
%else %if &test=ANY %then %do;
|
||||
%if &result=SOME %then %do;
|
||||
test_result='PASS';
|
||||
%end;
|
||||
%end;
|
||||
%else %if &test=NONE %then %do;
|
||||
%if &result=NONE %then %do;
|
||||
test_result='PASS';
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
||||
%end;
|
||||
run;
|
||||
|
||||
%local ds;
|
||||
%let ds=&syslast;
|
||||
proc append base=&outds data=&ds;
|
||||
run;
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
|
||||
%mend mp_assertcols;
|
||||
172
base/mp_assertcolvals.sas
Normal file
172
base/mp_assertcolvals.sas
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
@file
|
||||
@brief Asserts the values in a column
|
||||
@details Useful in the context of writing sasjs tests. The results of the
|
||||
test are _appended_ to the &outds. table.
|
||||
|
||||
Example usage:
|
||||
|
||||
data work.checkds;
|
||||
do checkval='Jane','James','Jill';
|
||||
output;
|
||||
end;
|
||||
run;
|
||||
%mp_assertcolvals(sashelp.class.name,
|
||||
checkvals=work.checkds.checkval,
|
||||
desc=At least one value has a match,
|
||||
test=ANYVAL
|
||||
)
|
||||
|
||||
data work.check;
|
||||
do val='M','F';
|
||||
output;
|
||||
end;
|
||||
run;
|
||||
%mp_assertcolvals(sashelp.class.sex,
|
||||
checkvals=work.check.val,
|
||||
desc=All values have a match,
|
||||
test=ALLVALS
|
||||
)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existds.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_nobs.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
|
||||
@param [in] indscol The input library.dataset.column to test for values
|
||||
@param [in] checkvals= (0) A library.dataset.column value containing a UNIQUE
|
||||
list of values to be compared against the source (indscol).
|
||||
@param [in] desc= (Testing observations) The user provided test description
|
||||
@param [in] test= (ALLVALS) The test to apply. Valid values are:
|
||||
@li ALLVALS - Test is a PASS if ALL values have a match in checkvals
|
||||
@li ANYVAL - Test is a PASS if at least 1 value has a match in checkvals
|
||||
@li NOVAL - Test is a PASS if there are NO matches in checkvals
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
results. If it does not exist, it will be created, with the following format:
|
||||
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||
|---|---|---|
|
||||
|User Provided description|PASS|Column &indscol contained ALL target vals|
|
||||
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_assertdsobs.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_assertcolvals(indscol,
|
||||
checkvals=0,
|
||||
test=ALLVALS,
|
||||
desc=mp_assertcolvals - no desc provided,
|
||||
outds=work.test_results
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(syscc=&syscc - on macro entry)
|
||||
)
|
||||
|
||||
%local lib ds col clib cds ccol nobs;
|
||||
%let lib=%scan(&indscol,1,%str(.));
|
||||
%let ds=%scan(&indscol,2,%str(.));
|
||||
%let col=%scan(&indscol,3,%str(.));
|
||||
%mp_abort(iftrue= (%mf_existds(&lib..&ds)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&lib..&ds not found!)
|
||||
)
|
||||
|
||||
%mp_abort(iftrue= (&checkvals=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Set CHECKVALS to a library.dataset.column containing check vals)
|
||||
)
|
||||
%let clib=%scan(&checkvals,1,%str(.));
|
||||
%let cds=%scan(&checkvals,2,%str(.));
|
||||
%let ccol=%scan(&checkvals,3,%str(.));
|
||||
%mp_abort(iftrue= (%mf_existds(&clib..&cds)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&clib..&cds not found!)
|
||||
)
|
||||
%let nobs=%mf_nobs(&clib..&cds);
|
||||
%mp_abort(iftrue= (&nobs=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&clib..&cds is empty!)
|
||||
)
|
||||
|
||||
%let test=%upcase(&test);
|
||||
|
||||
%if &test ne ALLVALS and &test ne ANYVAL and &test ne NOVAL %then %do;
|
||||
%mp_abort(
|
||||
mac=&sysmacroname,
|
||||
msg=%str(Invalid test - &test)
|
||||
)
|
||||
%end;
|
||||
|
||||
%local result orig;
|
||||
%let result=-1;
|
||||
%let orig=-1;
|
||||
proc sql noprint;
|
||||
select count(*) into: result trimmed
|
||||
from &lib..&ds
|
||||
where &col not in (
|
||||
select &ccol from &clib..&cds
|
||||
);
|
||||
select count(*) into: orig trimmed from &lib..&ds;
|
||||
quit;
|
||||
|
||||
%local notfound tmp1 tmp2;
|
||||
%let tmp1=%mf_getuniquename();
|
||||
%let tmp2=%mf_getuniquename();
|
||||
|
||||
/* this is a bit convoluted - but using sql outobs=10 throws warnings */
|
||||
proc sql noprint;
|
||||
create view &tmp1 as
|
||||
select distinct &col
|
||||
from &lib..&ds
|
||||
where &col not in (
|
||||
select &ccol from &clib..&cds
|
||||
);
|
||||
data &tmp2;
|
||||
set &tmp1;
|
||||
if _n_>10 then stop;
|
||||
run;
|
||||
proc sql;
|
||||
select distinct &col into: notfound separated by ' ' from &tmp2;
|
||||
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(syscc=&syscc after macro query)
|
||||
)
|
||||
|
||||
data;
|
||||
length test_description $256 test_result $4 test_comments $256;
|
||||
test_description=symget('desc');
|
||||
test_result='FAIL';
|
||||
test_comments="&sysmacroname: &lib..&ds..&col has &result/&orig values "
|
||||
!!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound');
|
||||
%if &test=ANYVAL %then %do;
|
||||
if &result < &orig then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=ALLVALS %then %do;
|
||||
if &result=0 then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=NOVAL %then %do;
|
||||
if &result=&orig then test_result='PASS';
|
||||
%end;
|
||||
%else %do;
|
||||
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
||||
%end;
|
||||
run;
|
||||
|
||||
%local ds;
|
||||
%let ds=&syslast;
|
||||
proc append base=&outds data=&ds;
|
||||
run;
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
|
||||
%mend mp_assertcolvals;
|
||||
121
base/mp_assertdsobs.sas
Normal file
121
base/mp_assertdsobs.sas
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
@file
|
||||
@brief Asserts the number of observations in a dataset
|
||||
@details Useful in the context of writing sasjs tests. The results of the
|
||||
test are _appended_ to the &outds. table.
|
||||
|
||||
Example usage:
|
||||
|
||||
%mp_assertdsobs(sashelp.class) %* tests if any observations are present;
|
||||
|
||||
%mp_assertdsobs(sashelp.class,test=ATLEAST 10) %* pass if >9 obs present;
|
||||
|
||||
%mp_assertdsobs(sashelp.class,test=ATMOST 20) %* pass if <21 obs present;
|
||||
|
||||
|
||||
@param [in] inds input dataset to test for presence of observations
|
||||
@param [in] desc= (Testing observations) The user provided test description
|
||||
@param [in] test= (HASOBS) The test to apply. Valid values are:
|
||||
@li HASOBS - Test is a PASS if the input dataset has any observations
|
||||
@li EMPTY - Test is a PASS if input dataset is empty
|
||||
@li EQUALS [integer] - Test passes if row count matches the provided integer
|
||||
@li ATLEAST [integer] - Test passes if row count is more than or equal to
|
||||
the provided integer
|
||||
@li ATMOST [integer] - Test passes if row count is less than or equal to
|
||||
the provided integer
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
results. If it does not exist, it will be created, with the following format:
|
||||
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||
|---|---|---|
|
||||
|User Provided description|PASS|Dataset &inds has XX obs|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_nobs.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_assertcolvals.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertcols.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_assertdsobs(inds,
|
||||
test=HASOBS,
|
||||
desc=Testing observations,
|
||||
outds=work.test_results
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local nobs ds;
|
||||
%let nobs=%mf_nobs(&inds);
|
||||
%let test=%upcase(&test);
|
||||
%let ds=%mf_getuniquename(prefix=mp_assertdsobs);
|
||||
|
||||
%if %substr(&test.xxxxx,1,6)=EQUALS %then %do;
|
||||
%let val=%scan(&test,2,%str( ));
|
||||
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid test - &test, expected EQUALS [integer])
|
||||
)
|
||||
%let test=EQUALS;
|
||||
%end;
|
||||
%else %if %substr(&test.xxxxxxx,1,7)=ATLEAST %then %do;
|
||||
%let val=%scan(&test,2,%str( ));
|
||||
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid test - &test, expected ATLEAST [integer])
|
||||
)
|
||||
%let test=ATLEAST;
|
||||
%end;
|
||||
%else %if %substr(&test.xxxxxxx,1,7)=ATMOST %then %do;
|
||||
%let val=%scan(&test,2,%str( ));
|
||||
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid test - &test, expected ATMOST [integer])
|
||||
)
|
||||
%let test=ATMOST;
|
||||
%end;
|
||||
%else %if &test ne HASOBS and &test ne EMPTY %then %do;
|
||||
%mp_abort(
|
||||
mac=&sysmacroname,
|
||||
msg=%str(Invalid test - &test)
|
||||
)
|
||||
%end;
|
||||
|
||||
data &ds;
|
||||
length test_description $256 test_result $4 test_comments $256;
|
||||
test_description=symget('desc');
|
||||
test_result='FAIL';
|
||||
test_comments="&sysmacroname: Dataset &inds has &nobs observations.";
|
||||
test_comments=test_comments!!" Test was "!!symget('test');
|
||||
%if &test=HASOBS %then %do;
|
||||
if &nobs>0 then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=EMPTY %then %do;
|
||||
if &nobs=0 then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=EQUALS %then %do;
|
||||
if &nobs=&val then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=ATLEAST %then %do;
|
||||
if &nobs ge &val then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=ATMOST %then %do;
|
||||
if &nobs le &val then test_result='PASS';
|
||||
%end;
|
||||
%else %do;
|
||||
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
||||
%end;
|
||||
run;
|
||||
|
||||
proc append base=&outds data=&ds;
|
||||
run;
|
||||
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
|
||||
%mend mp_assertdsobs;
|
||||
147
base/mp_assertscope.sas
Normal file
147
base/mp_assertscope.sas
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
@file
|
||||
@brief Used to capture scope leakage of macro variables
|
||||
@details
|
||||
|
||||
A common 'difficult to detect' bug in macros is where a nested macro
|
||||
over-writes variables in a higher level macro.
|
||||
|
||||
This assertion takes a snapshot of the macro variables before and after
|
||||
a macro invocation. Differences are captured in the `&outds` table. This
|
||||
makes it easy to detect whether any macro variables were modified or
|
||||
changed.
|
||||
|
||||
The following variables are NOT tested (as they are known, global variables
|
||||
used in SASjs):
|
||||
|
||||
@li &sasjs_prefix._FUNCTIONS
|
||||
|
||||
Global variables are initialised in mp_init.sas - which will also trigger
|
||||
"strict mode" in your SAS session. Whilst this is a default in SASjs
|
||||
produced apps, if you prefer not to use this mode, simply instantiate the
|
||||
following variable to prevent the macro from running: `SASJS_PREFIX`
|
||||
|
||||
Example usage:
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
|
||||
%let oops=I did it again;
|
||||
|
||||
%mp_assertscope(COMPARE,
|
||||
desc=Checking macro variables against previous snapshot
|
||||
)
|
||||
|
||||
This macro is designed to work alongside `sasjs test` - for more information
|
||||
about this facility, visit [cli.sasjs.io/test](https://cli.sasjs.io/test).
|
||||
|
||||
@param [in] action (SNAPSHOT) The action to take. Valid values:
|
||||
@li SNAPSHOT - take a copy of the current macro variables
|
||||
@li COMPARE - compare the current macro variables against previous values
|
||||
@param [in] scope= (GLOBAL) The scope of the variables to be checked. This
|
||||
corresponds to the values in the SCOPE column in `sashelp.vmacro`.
|
||||
@param [in] desc= (Testing scope leakage) The user provided test description
|
||||
@param [in] ignorelist= Provide a list of macro variable names to ignore from
|
||||
the comparison
|
||||
@param [in,out] scopeds= (work.mp_assertscope) The dataset to contain the
|
||||
scope snapshot
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
results. If it does not exist, it will be created, with the following format:
|
||||
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||
|---|---|---|
|
||||
|User Provided description|PASS|No out of scope variables created or modified|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getquotedstr.sas
|
||||
@li mp_init.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_assert.sas
|
||||
@li mp_assertcols.sas
|
||||
@li mp_assertcolvals.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assertscope.test.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_assertscope(action,
|
||||
desc=Testing Scope Leakage,
|
||||
scope=GLOBAL,
|
||||
scopeds=work.mp_assertscope,
|
||||
ignorelist=,
|
||||
outds=work.test_results
|
||||
)/*/STORE SOURCE*/;
|
||||
/**
|
||||
* this sets up the global vars, it will also enter STRICT mode. If this
|
||||
* behaviour is not desired, simply initiate the following global macro
|
||||
* variable to prevent the macro from running: SASJS_PREFIX
|
||||
*/
|
||||
%mp_init()
|
||||
|
||||
%local ds test_result test_comments del add mod ilist;
|
||||
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE
|
||||
SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist);
|
||||
|
||||
/* get current variables */
|
||||
%if &action=SNAPSHOT %then %do;
|
||||
proc sql;
|
||||
create table &scopeds as
|
||||
select name,offset,value
|
||||
from dictionary.macros
|
||||
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
|
||||
order by name,offset;
|
||||
%end;
|
||||
%else %if &action=COMPARE %then %do;
|
||||
|
||||
proc sql;
|
||||
create table _data_ as
|
||||
select name,offset,value
|
||||
from dictionary.macros
|
||||
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
|
||||
order by name,offset;
|
||||
|
||||
%let ds=&syslast;
|
||||
|
||||
proc compare
|
||||
base=&scopeds(where=(upcase(name) not in (%mf_getquotedstr(&ilist))))
|
||||
compare=&ds noprint;
|
||||
run;
|
||||
|
||||
%if &sysinfo=0 %then %do;
|
||||
%let test_result=PASS;
|
||||
%let test_comments=&scope Variables Unmodified;
|
||||
%end;
|
||||
%else %do;
|
||||
proc sql noprint undo_policy=none;
|
||||
select distinct name into: del separated by ' ' from &scopeds
|
||||
where name not in (select name from &ds);
|
||||
select distinct name into: add separated by ' ' from &ds
|
||||
where name not in (select name from &scopeds);
|
||||
select distinct a.name into: mod separated by ' '
|
||||
from &scopeds a
|
||||
inner join &ds b
|
||||
on a.name=b.name
|
||||
and a.offset=b.offset
|
||||
where a.value ne b.value;
|
||||
%let test_result=FAIL;
|
||||
%let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del));
|
||||
%end;
|
||||
|
||||
|
||||
data ;
|
||||
length test_description $256 test_result $4 test_comments $256;
|
||||
test_description=symget('desc');
|
||||
test_comments=symget('test_comments');
|
||||
test_result=symget('test_result');
|
||||
run;
|
||||
|
||||
%let ds=&syslast;
|
||||
proc append base=&outds data=&ds;
|
||||
run;
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
%end;
|
||||
|
||||
%mend mp_assertscope;
|
||||
117
base/mp_base64copy.sas
Normal file
117
base/mp_base64copy.sas
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
@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 [in] inref= (0) Fileref of the input file (should exist)
|
||||
@param [out] outref= (0) Output fileref. If it does not exist, it is created.
|
||||
@param [in] action= (ENCODE) The action to take. Valid values:
|
||||
@li ENCODE - Convert the file to base64 format
|
||||
@li DECODE - Decode the file from base64 format
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
|
||||
**/
|
||||
|
||||
%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=(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 recfm=N;
|
||||
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 + (-1) @;
|
||||
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);
|
||||
length raw $4;
|
||||
do i=1 to 4;
|
||||
rc=fget(filein,char,1);
|
||||
substr(raw,i,1)=char;
|
||||
end;
|
||||
rc = fput(fileout,input(raw,$base64X4.));
|
||||
rc = fwrite(fileout);
|
||||
end;
|
||||
rc = fclose(filein);
|
||||
rc = fclose(fileout);
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend mp_base64copy;
|
||||
@@ -2,16 +2,38 @@
|
||||
@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
|
||||
os-independent method to copy files. In case of naming collision, the
|
||||
default filerefs can be modified.
|
||||
Based on http://stackoverflow.com/questions/13046116/using-sas-to-copy-a-text-file
|
||||
os-independent method to copy files. In case of naming collision, the
|
||||
default filerefs can be modified.
|
||||
Note that if you have a new enough version of SAS, and you don't need features
|
||||
such as APPEND, you may be better of using the fcopy() function instead.
|
||||
|
||||
%mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
|
||||
|
||||
@param inloc full, quoted "path/and/filename.ext" of the object to be copied
|
||||
@param outloc full, quoted "path/and/filename.ext" of object to be created
|
||||
@param inref can override default input fileref to avoid naming clash
|
||||
@param outref an override default output fileref to avoid naming clash
|
||||
To append to a file, use the mode option, eg:
|
||||
|
||||
filename tmp1 temp;
|
||||
filename tmp2 temp;
|
||||
data _null_;
|
||||
file tmp1;
|
||||
put 'stacking';
|
||||
run;
|
||||
|
||||
%mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
|
||||
%mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
|
||||
|
||||
|
||||
@param [in] inloc= () quoted "path/and/filename.ext" of the file to be copied
|
||||
@param [out] outloc= () quoted "path/and/filename.ext" of the file to create
|
||||
@param [in] inref= (____in) If provided, this fileref takes precedence over
|
||||
the `inloc` parameter
|
||||
@param [out] outref= (____in) If provided, this fileref takes precedence
|
||||
over the `outloc` parameter. It must already exist!
|
||||
@param [in] mode= (CREATE) Valid values:
|
||||
@li CREATE - Create the file (even if it already exists)
|
||||
@li APPEND - Append to the file (don't overwrite)
|
||||
@param [in] iftrue= (1=1)
|
||||
Supply a condition for which the macro should be executed
|
||||
|
||||
@returns nothing
|
||||
|
||||
@version 9.2
|
||||
@@ -19,37 +41,40 @@
|
||||
**/
|
||||
|
||||
%macro mp_binarycopy(
|
||||
inloc= /* full path and filename of the object to be copied */
|
||||
inloc= /* full path and filename of the object to be copied */
|
||||
,outloc= /* full path and filename of object to be created */
|
||||
,inref=____in /* override default to use own filerefs */
|
||||
,outref=____out /* override default to use own filerefs */
|
||||
,mode=CREATE
|
||||
,iftrue=%str(1=1)
|
||||
)/*/STORE SOURCE*/;
|
||||
/* these IN and OUT filerefs can point to anything */
|
||||
%local mod;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
%if &mode=APPEND %then %let mod=mod;
|
||||
|
||||
/* these IN and OUT filerefs can point to anything */
|
||||
%if &inref = ____in %then %do;
|
||||
filename &inref &inloc lrecl=1048576 ;
|
||||
%end;
|
||||
%if &outref=____out %then %do;
|
||||
filename &outref &outloc lrecl=1048576 ;
|
||||
filename &outref &outloc lrecl=1048576 &mod;
|
||||
%end;
|
||||
|
||||
/* copy the file byte-for-byte */
|
||||
data _null_;
|
||||
length filein 8 fileid 8;
|
||||
filein = fopen("&inref",'I',1,'B');
|
||||
fileid = fopen("&outref",'O',1,'B');
|
||||
rec = '20'x;
|
||||
do while(fread(filein)=0);
|
||||
rc = fget(filein,rec,1);
|
||||
rc = fput(fileid, rec);
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
rc = fclose(filein);
|
||||
rc = fclose(fileid);
|
||||
run;
|
||||
/* copy the file byte-for-byte */
|
||||
data _null_;
|
||||
infile &inref lrecl=1 recfm=n;
|
||||
file &outref &mod recfm=n;
|
||||
input sourcechar $char1. @@;
|
||||
format sourcechar hex2.;
|
||||
put sourcechar char1. @@;
|
||||
run;
|
||||
|
||||
%if &inref = ____in %then %do;
|
||||
filename &inref clear;
|
||||
%end;
|
||||
%if &outref=____out %then %do;
|
||||
filename &outref clear;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mp_binarycopy;
|
||||
|
||||
195
base/mp_chop.sas
Normal file
195
base/mp_chop.sas
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
@file
|
||||
@brief Splits a file of ANY SIZE by reference to a search string.
|
||||
@details Provide a fileref and a search string to chop off part of a file.
|
||||
|
||||
Works by reading in the file byte by byte, then marking the beginning and end
|
||||
of each matched string, before finally doing the chop.
|
||||
|
||||
Choose whether to keep the FIRST or the LAST section of the file. Optionally,
|
||||
use an OFFSET to fix the precise chop point.
|
||||
|
||||
Usage:
|
||||
|
||||
%let src="%sysfunc(pathname(work))/file.txt";
|
||||
%let str=Chop here!;
|
||||
%let out1="%sysfunc(pathname(work))/file1.txt";
|
||||
%let out2="%sysfunc(pathname(work))/file2.txt";
|
||||
%let out3="%sysfunc(pathname(work))/file3.txt";
|
||||
%let out4="%sysfunc(pathname(work))/file4.txt";
|
||||
|
||||
data _null_;
|
||||
file &src;
|
||||
put "startsection&str.endsection";
|
||||
run;
|
||||
|
||||
%mp_chop(&src, matchvar=str, keep=FIRST, outfile=&out1)
|
||||
%mp_chop(&src, matchvar=str, keep=LAST, outfile=&out2)
|
||||
%mp_chop(&src, matchvar=str, keep=FIRST, matchpoint=END, outfile=&out3)
|
||||
%mp_chop(&src, matchvar=str, keep=LAST, matchpoint=END, outfile=&out4)
|
||||
|
||||
filename results (&out1 &out2 &out3 &out4);
|
||||
data _null_;
|
||||
infile results;
|
||||
input;
|
||||
list;
|
||||
run;
|
||||
|
||||
Results:
|
||||
@li `startsection`
|
||||
@li `Chop here!endsection`
|
||||
@li `startsectionChop here!`
|
||||
@li `endsection`
|
||||
|
||||
For more examples, see mp_chop.test.sas
|
||||
|
||||
@param [in] infile The QUOTED path to the file on which to perform the chop
|
||||
@param [in] matchvar= () Macro variable NAME containing the string to split by
|
||||
@param [in] matchpoint= (START) Valid values:
|
||||
@li START - chop at the beginning of the string in `matchvar`.
|
||||
@li END - chop at the end of the string in `matchvar`.
|
||||
@param [in] offset= (0) An adjustment to the precise chop location, by
|
||||
by reference to the `matchpoint`. Should be a positive or negative integer.
|
||||
@param [in] keep= (FIRST) Valid values:
|
||||
@li FIRST - keep the section of the file before the chop
|
||||
@li LAST - keep the section of the file after the chop
|
||||
@param [in] mdebug= (0) Set to 1 to provide macro debugging
|
||||
@param [out] outfile= (0)
|
||||
Optional QUOTED path to the adjusted output file (avoids
|
||||
overwriting the first file).
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mp_gsubfile.sas
|
||||
@li mp_replace.sas
|
||||
@li mp_chop.test.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_chop(infile,
|
||||
matchvar=,
|
||||
matchpoint=START,
|
||||
keep=FIRST,
|
||||
offset=0,
|
||||
mdebug=0,
|
||||
outfile=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local fref0 dttm ds1 outref;
|
||||
%let fref0=%mf_getuniquefileref();
|
||||
%let ds1=%mf_getuniquename(prefix=allchars);
|
||||
%let ds2=%mf_getuniquename(prefix=startmark);
|
||||
|
||||
%if &outfile=0 %then %let outfile=&infile;
|
||||
|
||||
%mp_abort(iftrue= (%length(%superq(&matchvar))=0)
|
||||
,mac=mp_chop.sas
|
||||
,msg=%str(&matchvar is an empty variable)
|
||||
)
|
||||
|
||||
/* START */
|
||||
%let dttm=%sysfunc(datetime());
|
||||
|
||||
filename &fref0 &infile lrecl=1 recfm=n;
|
||||
|
||||
/* create dataset with one char per row */
|
||||
data &ds1;
|
||||
infile &fref0;
|
||||
input sourcechar $char1. @@;
|
||||
format sourcechar hex2.;
|
||||
run;
|
||||
|
||||
/* get start & stop position of first matchvar string (one row, two vars) */
|
||||
data &ds2;
|
||||
/* set find string to length in bytes to cover trailing spaces */
|
||||
length string $ %length(%superq(&matchvar));
|
||||
string =symget("&matchvar");
|
||||
drop string;
|
||||
|
||||
firstchar=char(string,1);
|
||||
findlen=lengthm(string); /* <- for trailing bytes */
|
||||
|
||||
do _N_=1 to nobs;
|
||||
set &ds1 nobs=nobs point=_N_;
|
||||
if sourcechar=firstchar then do;
|
||||
pos=1;
|
||||
s=0;
|
||||
do point=_N_ to min(_N_ + findlen -1,nobs);
|
||||
set &ds1 point=point;
|
||||
if sourcechar=char(string, pos) then s + 1;
|
||||
else goto _leave_;
|
||||
pos+1;
|
||||
end;
|
||||
_leave_:
|
||||
if s=findlen then do;
|
||||
START =_N_;
|
||||
_N_ =_N_+ s - 1;
|
||||
STOP =_N_;
|
||||
output;
|
||||
/* matched! */
|
||||
stop;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
stop;
|
||||
keep START STOP;
|
||||
run;
|
||||
|
||||
%local split;
|
||||
%let split=0;
|
||||
data _null_;
|
||||
set &ds2;
|
||||
if "&matchpoint"='START' then do;
|
||||
if "&keep"='FIRST' then mp=start;
|
||||
else if "&keep"='LAST' then mp=start-1;
|
||||
end;
|
||||
else if "&matchpoint"='END' then do;
|
||||
if "&keep"='FIRST' then mp=stop+1;
|
||||
else if "&keep"='LAST' then mp=stop;
|
||||
end;
|
||||
split=mp+&offset;
|
||||
call symputx('split',split,'l');
|
||||
%if &mdebug=1 %then %do;
|
||||
put (_all_)(=);
|
||||
%put &=offset;
|
||||
%end;
|
||||
run;
|
||||
%if &split=0 %then %do;
|
||||
%put &sysmacroname: No match found in &infile for string %superq(&matchvar);
|
||||
%return;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
file &outfile recfm=n;
|
||||
set &ds1;
|
||||
%if &keep=FIRST %then %do;
|
||||
if _n_ ge &split then stop;
|
||||
%end;
|
||||
%else %do;
|
||||
if _n_ gt &split;
|
||||
%end;
|
||||
put sourcechar char1.;
|
||||
run;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &fref0 clear;
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
infile &outfile lrecl=32767;
|
||||
input;
|
||||
list;
|
||||
if _n_>200 then stop;
|
||||
run;
|
||||
%end;
|
||||
/* END */
|
||||
%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
|
||||
|
||||
%mend mp_chop;
|
||||
@@ -1,20 +1,23 @@
|
||||
/**
|
||||
@file mp_cleancsv.sas
|
||||
@file
|
||||
@brief Fixes embedded cr / lf / crlf in CSV
|
||||
@details CSVs will sometimes contain lf or crlf within quotes (eg when
|
||||
saved by excel). When the termstr is ALSO lf or crlf that can be tricky
|
||||
to process using SAS defaults.
|
||||
This macro converts any csv to follow the convention of a windows excel file,
|
||||
applying CRLF line endings and converting embedded cr and crlf to lf.
|
||||
saved by excel). When the termstr is ALSO lf or crlf that can be tricky
|
||||
to process using SAS defaults.
|
||||
This macro converts any csv to follow the convention of a windows excel file,
|
||||
applying CRLF line endings and converting embedded cr and crlf to lf.
|
||||
|
||||
usage:
|
||||
Usage:
|
||||
|
||||
fileref mycsv "/path/your/csv";
|
||||
%mp_cleancsv(in=mycsv,out=/path/new.csv)
|
||||
|
||||
@param in= provide path or fileref to input csv
|
||||
@param out= output path or fileref to output csv
|
||||
@param qchar= quote char - hex code 22 is the double quote.
|
||||
@param [in] in= (NOTPROVIDED)
|
||||
Provide path or fileref to input csv. If a period is
|
||||
found, it is assumed to be a file.
|
||||
@param [in] out= (NOTPROVIDED) Output path or fileref to output csv.
|
||||
If a period is found, it is assumed to be a file.
|
||||
@param [in] qchar= ('22'x) Quote char - hex code 22 is the double quote.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -23,7 +26,7 @@
|
||||
|
||||
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
|
||||
%if "&in"="NOTPROVIDED" or "&out"="NOTPROVIDED" %then %do;
|
||||
%put %str(ERR)OR: Please provide valid input (&in) and output (&out) locations;
|
||||
%put %str(ERR)OR: Please provide valid input (&in) & output (&out) locations;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
@@ -32,9 +35,9 @@
|
||||
%if %index(&out,.) %then %let out="&out";
|
||||
|
||||
/**
|
||||
* convert all cr and crlf within quotes to lf
|
||||
* convert all other cr or lf to crlf
|
||||
*/
|
||||
* convert all cr and crlf within quotes to lf
|
||||
* convert all other cr or lf to crlf
|
||||
*/
|
||||
data _null_;
|
||||
infile &in recfm=n ;
|
||||
file &out recfm=n;
|
||||
@@ -56,9 +59,14 @@
|
||||
else do;
|
||||
/* outside a quote, change cr and lf to crlf */
|
||||
if inchar='0D'x then do;
|
||||
crblank:
|
||||
put '0D0A'x;
|
||||
input inchar $char1.;
|
||||
if inchar ne '0A'x then do;
|
||||
if inchar='0D'x then do;
|
||||
/* multiple CR indicates CR formatted file with blank lines */
|
||||
goto crblank;
|
||||
end;
|
||||
else if inchar ne '0A'x then do;
|
||||
put inchar $char1.;
|
||||
if inchar=qchar then isq = mod(isq+1,2);
|
||||
end;
|
||||
@@ -67,5 +75,5 @@
|
||||
else put inchar $char1.;
|
||||
end;
|
||||
run;
|
||||
%mend;
|
||||
%mend mp_cleancsv;
|
||||
/** @endcond */
|
||||
94
base/mp_cntlout.sas
Normal file
94
base/mp_cntlout.sas
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
@file mp_cntlout.sas
|
||||
@brief Creates a cntlout dataset in a consistent format
|
||||
@details The dataset produced by proc format in the cntlout option will vary
|
||||
according to its contents.
|
||||
|
||||
When dealing with formats from an ETL perspective (eg in [Data Controller for
|
||||
SAS](https://datacontroller.io)), it is important that the output dataset
|
||||
has a consistent model (and compariable values).
|
||||
|
||||
This macro makes use of mddl_sas_cntlout.sas to provide the consistent model,
|
||||
and will left-align the start and end values when dealing with numeric ranges
|
||||
to enable consistency when checking for differences.
|
||||
|
||||
usage:
|
||||
|
||||
%mp_cntlout(libcat=yourlib.cat,cntlout=work.formatexport)
|
||||
|
||||
@param [in] libcat The library.catalog reference
|
||||
@param [in] fmtlist= (0) provide a space separated list of specific formats to
|
||||
extract
|
||||
@param [in] iftrue= (1=1) A condition under which the macro should be executed
|
||||
@param [out] cntlout= (work.fmtextract) Libds reference for the output dataset
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_sas_cntlout.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_aligndecimal.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_getvarformat.sas
|
||||
@li mp_getformats.sas
|
||||
@li mp_loadformat.sas
|
||||
@li mp_ds2fmtds.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_cntlout(
|
||||
iftrue=(1=1)
|
||||
,libcat=
|
||||
,cntlout=work.fmtextract
|
||||
,fmtlist=0
|
||||
)/*/STORE SOURCE*/;
|
||||
%local ddlds cntlds i;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
%let ddlds=%mf_getuniquename();
|
||||
%let cntlds=%mf_getuniquename();
|
||||
|
||||
%mddl_sas_cntlout(libds=&ddlds)
|
||||
|
||||
%if %index(&libcat,-)>0 and %scan(&libcat,2,-)=FC %then %do;
|
||||
%let libcat=%scan(&libcat,1,-);
|
||||
%end;
|
||||
|
||||
proc format lib=&libcat cntlout=&cntlds;
|
||||
%if "&fmtlist" ne "0" and "&fmtlist" ne "" %then %do;
|
||||
select
|
||||
%do i=1 %to %sysfunc(countw(&fmtlist,%str( )));
|
||||
%scan(&fmtlist,&i,%str( ))
|
||||
%end;
|
||||
;
|
||||
%end;
|
||||
run;
|
||||
|
||||
data &cntlout/nonote2err;
|
||||
if 0 then set &ddlds;
|
||||
set &cntlds;
|
||||
by type fmtname notsorted;
|
||||
|
||||
/* align the numeric values to avoid overlapping ranges */
|
||||
if type in ("I","N") then do;
|
||||
%mp_aligndecimal(start,width=16)
|
||||
%mp_aligndecimal(end,width=16)
|
||||
end;
|
||||
|
||||
/* create row marker. Data cannot be sorted without it! */
|
||||
if first.fmtname then fmtrow=1;
|
||||
else fmtrow+1;
|
||||
|
||||
run;
|
||||
proc sort;
|
||||
by type fmtname fmtrow;
|
||||
run;
|
||||
|
||||
proc sql;
|
||||
drop table &ddlds,&cntlds;
|
||||
|
||||
%mend mp_cntlout;
|
||||
/** @endcond */
|
||||
84
base/mp_copyfolder.sas
Normal file
84
base/mp_copyfolder.sas
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
@file
|
||||
@brief A macro to recursively copy a directory
|
||||
@details Performs a recursive directory listing then works from top to bottom
|
||||
copying files and creating subdirectories.
|
||||
|
||||
Usage:
|
||||
|
||||
%let rootdir=%sysfunc(pathname(work))/demo;
|
||||
%let copydir=%sysfunc(pathname(work))/demo_copy;
|
||||
%mf_mkdir(&rootdir)
|
||||
%mf_mkdir(&rootdir/subdir)
|
||||
%mf_mkdir(&rootdir/subdir/subsubdir)
|
||||
data "&rootdir/subdir/example.sas7bdat";
|
||||
run;
|
||||
|
||||
%mp_copyfolder(&rootdir,©dir)
|
||||
|
||||
@param [in] source Unquoted path to the folder to copy from.
|
||||
@param [out] target Unquoted path to the folder to copy to.
|
||||
@param [in] copymax= (MAX) Set to a positive integer to indicate the level of
|
||||
subdirectory copy recursion - eg 3, to go `./3/levels/deep`. For unlimited
|
||||
recursion, set to MAX.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_isdir.sas
|
||||
@li mf_mkdir.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_dirlist.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_copyfolder.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_copyfolder(source,target,copymax=MAX);
|
||||
|
||||
%mp_abort(iftrue=(%mf_isdir(&source)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Source dir does not exist (&source))
|
||||
)
|
||||
|
||||
%mf_mkdir(&target)
|
||||
|
||||
%mp_abort(iftrue=(%mf_isdir(&target)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Target dir could not be created (&target))
|
||||
)
|
||||
|
||||
/* prep temp table */
|
||||
%local tempds;
|
||||
%let tempds=%mf_getuniquename();
|
||||
|
||||
/* recursive directory listing */
|
||||
%mp_dirlist(path=&source,outds=work.&tempds,maxdepth=©max)
|
||||
|
||||
/* create folders and copy content */
|
||||
data _null_;
|
||||
length msg $200;
|
||||
call missing(msg);
|
||||
set work.&tempds;
|
||||
if _n_ = 1 then dpos+sum(length(directory),2);
|
||||
filepath2="&target/"!!substr(filepath,dpos);
|
||||
if file_or_folder='folder' then call execute('%mf_mkdir('!!filepath2!!')');
|
||||
else do;
|
||||
length fref1 fref2 $8;
|
||||
rc1=filename(fref1,filepath,'disk','recfm=n');
|
||||
rc2=filename(fref2,filepath2,'disk','recfm=n');
|
||||
if fcopy(fref1,fref2) ne 0 then do;
|
||||
msg=sysmsg();
|
||||
putlog 'ERR' +(-1) "OR: Unable to copy " filepath " to " filepath2;
|
||||
putlog msg=;
|
||||
end;
|
||||
end;
|
||||
rc=filename(fref1);
|
||||
rc=filename(fref2);
|
||||
run;
|
||||
|
||||
/* tidy up */
|
||||
proc sql;
|
||||
drop table work.&tempds;
|
||||
|
||||
%mend mp_copyfolder;
|
||||
73
base/mp_coretable.sas
Normal file
73
base/mp_coretable.sas
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
@file
|
||||
@brief Create the permanent Core tables
|
||||
@details Several macros in the [core](https://github.com/sasjs/core) library
|
||||
make use of permanent tables. To avoid duplication in definitions, this
|
||||
macro provides a central location for managing the corresponding DDL.
|
||||
|
||||
Note - this macro is likely to be deprecated in future in favour of a
|
||||
dedicated "datamodel" folder (prefix mddl)
|
||||
|
||||
Any corresponding data would go in a seperate repo, to avoid this one
|
||||
ballooning in size!
|
||||
|
||||
Example usage:
|
||||
|
||||
%mp_coretable(LOCKTABLE,libds=work.locktable)
|
||||
|
||||
@param [in] table_ref The type of table to create. Example values:
|
||||
@li DIFFTABLE
|
||||
@li FILTER_DETAIL
|
||||
@li FILTER_SUMMARY
|
||||
@li LOCKANYTABLE
|
||||
@li MAXKEYTABLE
|
||||
@param [in] libds= (0) The library.dataset reference used to create the table.
|
||||
If not provided, then the DDL is simply printed to the log.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_difftable.sas
|
||||
@li mddl_dc_filterdetail.sas
|
||||
@li mddl_dc_filtersummary.sas
|
||||
@li mddl_dc_locktable.sas
|
||||
@li mddl_dc_maxkeytable.sas
|
||||
@li mf_getuniquename.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_filterstore.sas
|
||||
@li mp_lockanytable.sas
|
||||
@li mp_retainedkey.sas
|
||||
@li mp_storediffs.sas
|
||||
@li mp_stackdiffs.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_coretable(table_ref,libds=0
|
||||
)/*/STORE SOURCE*/;
|
||||
%local outds ;
|
||||
%let outds=%sysfunc(ifc(&libds=0,%mf_getuniquename(),&libds));
|
||||
proc sql;
|
||||
%if &table_ref=DIFFTABLE %then %do;
|
||||
%mddl_dc_difftable(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=LOCKTABLE %then %do;
|
||||
%mddl_dc_locktable(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=FILTER_SUMMARY %then %do;
|
||||
%mddl_dc_filtersummary(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=FILTER_DETAIL %then %do;
|
||||
%mddl_dc_filterdetail(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=MAXKEYTABLE %then %do;
|
||||
%mddl_dc_maxkeytable(libds=&outds)
|
||||
%end;
|
||||
|
||||
%if &libds=0 %then %do;
|
||||
proc sql;
|
||||
describe table &syslast;
|
||||
drop table &syslast;
|
||||
%end;
|
||||
%mend mp_coretable;
|
||||
@@ -13,16 +13,19 @@
|
||||
constraint unq unique(tx_from, dd_type),
|
||||
constraint nnn not null(DD_SHORTDESC)
|
||||
);
|
||||
|
||||
|
||||
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
||||
%mp_deleteconstraints(inds=work.constraints,outds=dropped,execute=YES)
|
||||
%mp_createconstraints(inds=work.constraints,outds=created,execute=YES)
|
||||
|
||||
@param inds= The input table containing the constraint info
|
||||
@param outds= a table containing the create statements (create_statement column)
|
||||
@param execute= `YES|NO` - default is NO. To actually create, use YES.
|
||||
@param [in] inds= (work.mp_getconstraints) The input table containing the
|
||||
constraint info
|
||||
@param [out] outds= (work.mp_createconstraints) A table containing the create
|
||||
statements (create_statement column)
|
||||
@param [in] execute= (NO) To actually create, use YES.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
<h4> Related Files </h4>
|
||||
@li mp_getconstraints.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -30,7 +33,7 @@
|
||||
**/
|
||||
|
||||
%macro mp_createconstraints(inds=mp_getconstraints
|
||||
,outds=mp_createconstraints
|
||||
,outds=work.mp_createconstraints
|
||||
,execute=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
@@ -48,7 +51,7 @@ data &outds;
|
||||
else type=constraint_type;
|
||||
create_statement=catx(" ","alter table",libref,".",table_name
|
||||
,"add constraint",constraint_name,type,"(");
|
||||
if last.constraint_name then
|
||||
if last.constraint_name then
|
||||
create_statement=cats(create_statement,column_name,");");
|
||||
else create_statement=cats(create_statement,column_name,",");
|
||||
if "&execute"="YES" then call execute(create_statement);
|
||||
@@ -64,4 +67,4 @@ data &outds;
|
||||
output;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_createconstraints;
|
||||
|
||||
@@ -1,47 +1,13 @@
|
||||
/**
|
||||
@file mp_createwebservice.sas
|
||||
@brief Create a web service in SAS 9 or Viya
|
||||
@details Creates a SASJS ready Stored Process in SAS 9 or Job Execution
|
||||
Service in SAS Viya
|
||||
@brief Create a web service in SAS 9, Viya or SASjs Server (legacy)
|
||||
@details This is actually a wrapper for mx_createwebservice.sas, remaining
|
||||
for legacy purposes. For new apps, use mx_createwebservice.sas.
|
||||
|
||||
Usage:
|
||||
|
||||
%* compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* write some code;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%* fetch any data from frontend ;
|
||||
%webout(FETCH)
|
||||
data example1 example2;
|
||||
set sashelp.class;
|
||||
run;
|
||||
%* send data back;
|
||||
%webout(OPEN)
|
||||
%webout(ARR,example1) * Array format, fast, suitable for large tables ;
|
||||
%webout(OBJ,example2) * Object format, easier to work with ;
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%mp_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mm_createwebservice.sas
|
||||
@li mv_createwebservice.sas
|
||||
@li mx_createwebservice.sas
|
||||
|
||||
@param path= The full folder path where the service will be created
|
||||
@param name= Service name. Avoid spaces.
|
||||
@param desc= The description of the service (optional)
|
||||
@param precode= Space separated list of filerefs, pointing to the code that
|
||||
needs to be attached to the beginning of the service (optional)
|
||||
@param code= Space seperated fileref(s) of the actual code to be added
|
||||
@param replace= select YES to replace any existing service in that location
|
||||
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
@@ -51,33 +17,16 @@ Usage:
|
||||
,code=ft15f001
|
||||
,desc=This service was created by the mp_createwebservice macro
|
||||
,replace=YES
|
||||
,mdebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if &syscc ge 4 %then %do;
|
||||
%put syscc=&syscc - &sysmacroname will not execute in this state;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%local platform; %let platform=%mf_getplatform();
|
||||
%if &platform=SASVIYA %then %do;
|
||||
%if "&path"="HOME" %then %let path=/Users/&sysuserid/My Folder;
|
||||
%mv_createwebservice(path=&path
|
||||
%mx_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,code=&code
|
||||
,desc=&desc
|
||||
,replace=&replace
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
%if "&path"="HOME" %then %let path=/User Folders/&sysuserid/My Folder;
|
||||
%mm_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,desc=&desc
|
||||
,replace=&replace
|
||||
)
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_createwebservice;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user