diff --git a/README.md b/README.md index f73a0c0..d732d72 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,13 @@ Documentation: https://core.sasjs.io - No X command - Prefixes: _mf_, _mp_ +**fcmp** library (SAS9/Viya) +- Function and macro names are identical, except for special cases +- Prefixes: _mcf_ + +The fcmp macros are used to generate fcmp functions, and can be used with or +without the `proc fcmp` wrapper. + **meta** library (SAS9 only) - OS independent diff --git a/all.sas b/all.sas index ffa7cad..de7d752 100644 --- a/all.sas +++ b/all.sas @@ -18804,3 +18804,82 @@ run; %inc "%sysfunc(pathname(work))/ml_json.lua"; %mend ml_json; +/** + @file + @brief Adds a string to a file + @details Creates an fcmp function for appending a string to an external file. + If the file does not exist, it is created. + + The function itself takes the following paramters: + + | PARAMETER | DESCRIPTION | + |------------|-------------| + | filepath $ | full path to the file| + | string $ | string to add to the file | + | mode $ | mode of the output - either APPEND (default) or CREATE | + + It returns 0 if successful, or -1 if an error occured. + + Usage: + + %mcf_string2file(wrap=YES, insert_cmplib=YES) + + data _null_; + rc=mcf_string2file( + "%sysfunc(pathname(work))/newfile.txt" + , "This is a test" + , "CREATE"); + run; + + data _null_; + infile "%sysfunc(pathname(work))/newfile.txt"; + input; + putlog _infile_; + run; + + @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper. + @param [out] insert_cmplib= (NO) Choose YES to insert the package into the + CMPLIB reference. + @param [out] lib= (work) The output library in which to create the catalog. + @param [out] cat= (sasjs) The output catalog in which to create the package. + @param [out] pkg= (utils) The output package in which to create the function. + Uses a 3 part format: libref.catalog.package + +**/ + +%macro mcf_string2file(wrap=NO + ,insert_cmplib=NO + ,lib=WORK + ,cat=SASJS + ,pkg=UTILS +)/*/STORE SOURCE*/; + +%if &wrap=YES %then %do; + proc fcmp outcat=&lib..&cat..&pkg; +%end; + +function mcf_string2file(filepath $, string $, mode $); + if mode='APPEND' then fmode='a'; + else fmode='o'; + length fref $8; + rc=filename(fref,filepath); + if (rc ne 0) then return( -1 ); + fid = fopen(fref,fmode); + if (fid = 0) then return( -1 ); + rc=fput(fid, string); + rc=fwrite(fid); + rc=fclose(fid); + rc=filename(fref); + return(0); +endsub; + + +%if &wrap=YES %then %do; + quit; +%end; + +%if &insert_cmplib=YES %then %do; + options insert=(CMPLIB=(&lib..&cat)); +%end; + +%mend mcf_string2file; \ No newline at end of file diff --git a/build.py b/build.py index 61e88be..1ad3046 100755 --- a/build.py +++ b/build.py @@ -84,7 +84,7 @@ options noquotelenmax; """ f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb f.write(header) -folders=['base','meta','metax','viya','lua'] +folders=['base','meta','metax','viya','lua','fcmp'] for folder in folders: filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")] filenames.sort() diff --git a/fcmp/mcf_string2file.sas b/fcmp/mcf_string2file.sas new file mode 100644 index 0000000..add97ab --- /dev/null +++ b/fcmp/mcf_string2file.sas @@ -0,0 +1,79 @@ +/** + @file + @brief Adds a string to a file + @details Creates an fcmp function for appending a string to an external file. + If the file does not exist, it is created. + + The function itself takes the following (positional) parameters: + + | PARAMETER | DESCRIPTION | + |------------|-------------| + | filepath $ | full path to the file| + | string $ | string to add to the file | + | mode $ | mode of the output - either APPEND (default) or CREATE | + + It returns 0 if successful, or -1 if an error occured. + + Usage: + + %mcf_string2file(wrap=YES, insert_cmplib=YES) + + data _null_; + rc=mcf_string2file( + "%sysfunc(pathname(work))/newfile.txt" + , "This is a test" + , "CREATE"); + run; + + data _null_; + infile "%sysfunc(pathname(work))/newfile.txt"; + input; + putlog _infile_; + run; + + @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper. + @param [out] insert_cmplib= (NO) Choose YES to insert the package into the + CMPLIB reference. + @param [out] lib= (work) The output library in which to create the catalog. + @param [out] cat= (sasjs) The output catalog in which to create the package. + @param [out] pkg= (utils) The output package in which to create the function. + Uses a 3 part format: libref.catalog.package + +**/ + +%macro mcf_string2file(wrap=NO + ,insert_cmplib=NO + ,lib=WORK + ,cat=SASJS + ,pkg=UTILS +)/*/STORE SOURCE*/; + +%if &wrap=YES %then %do; + proc fcmp outcat=&lib..&cat..&pkg; +%end; + +function mcf_string2file(filepath $, string $, mode $); + if mode='APPEND' then fmode='a'; + else fmode='o'; + length fref $8; + rc=filename(fref,filepath); + if (rc ne 0) then return( -1 ); + fid = fopen(fref,fmode); + if (fid = 0) then return( -1 ); + rc=fput(fid, string); + rc=fwrite(fid); + rc=fclose(fid); + rc=filename(fref); + return(0); +endsub; + + +%if &wrap=YES %then %do; + quit; +%end; + +%if &insert_cmplib=YES %then %do; + options insert=(CMPLIB=(&lib..&cat)); +%end; + +%mend mcf_string2file; \ No newline at end of file diff --git a/main.dox b/main.dox index ad3e9de..33a1b14 100644 --- a/main.dox +++ b/main.dox @@ -20,6 +20,19 @@ */ +/*! \dir fcmp + * \brief Macros for generating FCMP functions + * \details These macros have the following attributes: + + * Macro name matches compiled function / subroutine name + * Prefixes: _mcf_, _mcs_ + + The macro part is just a wrapper for the underlying function / subroutine, + and has switches for including the proc fcmp / quit statements and whether + to insert the package into the CMPLIB option. + + */ + /*! \dir meta * \brief Metadata Aware Macros * \details These macros have the following attributes: diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json index ef34a51..a3d2bff 100644 --- a/sasjs/sasjsconfig.json +++ b/sasjs/sasjsconfig.json @@ -2,11 +2,13 @@ "$schema": "https://cli.sasjs.io/sasjsconfig-schema.json", "macroFolders": [ "base", + "fcmp", "meta", "metax", "viya", "lua", - "tests/base" + "tests/base", + "tests/fcmp" ], "docConfig": { "displayMacroCore": false, diff --git a/tests/fcmp/mcf_string2file.test.sas b/tests/fcmp/mcf_string2file.test.sas new file mode 100644 index 0000000..7b58bac --- /dev/null +++ b/tests/fcmp/mcf_string2file.test.sas @@ -0,0 +1,52 @@ +/** + @file + @brief Testing mm_webout macro + +

SAS Macros

+ @li mcf_string2file.sas + @li mp_assert.sas + +**/ + + +%mcf_string2file(wrap=YES, insert_cmplib=YES) + +data _null_; + rc=mcf_string2file( + "%sysfunc(pathname(work))/newfile.txt" + , "line1" + , "APPEND"); + rc=mcf_string2file( + "%sysfunc(pathname(work))/newfile.txt" + , "line2" + , "APPEND"); +run; + +data _null_; + infile "%sysfunc(pathname(work))/newfile.txt"; + input; + if _n_=2 then call symputx('val',_infile_); +run; + +%mp_assert( + iftrue=(%str(&val)=%str(line2)), + desc=Check if APPEND works +) + +data _null_; + rc=mcf_string2file( + "%sysfunc(pathname(work))/newfile.txt" + , "creating" + , "CREATE"); +run; + +data _null_; + infile "%sysfunc(pathname(work))/newfile.txt"; + input; + if _n_=1 then call symputx('val2',_infile_); +run; + +%mp_assert( + iftrue=(%str(&val2)=%str(creating)), + desc=Check if CREATE works +) \ No newline at end of file