/*

/ Program   : missvars.sas
/ Version   : 2.1
/ Author    : Roland Rashleigh-Berry
/ Date      : 19-Jan-2012
/ Purpose   : To create a list of all-missing variables
/ SubMacros : %nvarsc %nvarsn
/ Notes     : It is not possible to implement this as a function-style macro due
/             to the data step boundary so the results will be written out to a
/             global macro variable. What you do with the list created is
/             up to you. You might report them as errors or warnings or you
/             could drop the variables from a dataset as shown in the usage
/             notes below.
/ Usage     : %missvars(dsname);
/             run;
/             data dsname;
/               set dsname(drop=&_miss_);
/             run;
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ ds                Dataset (pos) (must be pure dataset name and have no keep,
/                   drop, where or rename associated with it).
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ rrb  13Feb07         "macro called" message added
/ rrb  11Nov09         Name of global macro variables _miss_ and _nonmiss_ now
/                      fixed. All-missing variable name list written to _miss_
/                      and other variables names written to _nonmiss_ (v2.0)
/ rrb  04May11         Code tidy
/ rrb  19Jan12         NOTEs disabled and changed so that if a character
/                      variable equates to a single period when left aligned
/                      then it will also be regarded as a missing value (v2.1)
/===============================================================================
/ This is public domain software. No guarantee as to suitability or accuracy is
/ given or implied. User uses this code entirely at their own risk.
/=============================================================================*/

%put MACRO CALLED: missvars v2.1;

%macro missvars(ds);

  %local nvarsn nvarsc savopts;
  %global _miss_ _nonmiss_;

  %let savopts=%sysfunc(getoption(notes));
  options nonotes;

  %let _miss_=;
  %let _nonmiss_=;

  %let nvarsn=%nvarsn(&ds);
  %let nvarsc=%nvarsc(&ds);

  data _null_;
    %if &nvarsn GT 0 %then %do;
      array _nmiss {&nvarsn} $ 1 _temporary_ (&nvarsn*'1');
    %end;
    %if &nvarsc GT 0 %then %do;
      array _cmiss {&nvarsc} $ 1 _temporary_ (&nvarsc*'1');
    %end;
    set &ds end=last;
    %if &nvarsn GT 0 %then %do;
      array _num {*} _numeric_;
    %end;
    %if &nvarsc GT 0 %then %do;
      array _char (*) _character_;
    %end;
    length _miss_ _nmiss_ $ 32767;
    retain _miss_ _nmiss_ " ";
    %if &nvarsn GT 0 %then %do;
      do i=1 to &nvarsn;
        if _num(i) NE . then _nmiss(i)='0';
      end;
    %end;
    %if &nvarsc GT 0 %then %do;
      do i=1 to &nvarsc;
        if left(_char(i)) NOT IN (' ' '.') then _cmiss(i)='0';
      end;
    %end;
    if last then do;
      %if &nvarsn GT 0 %then %do;
        do i=1 to &nvarsn;
          if _nmiss(i) EQ '1' then _miss_=trim(_miss_)||" "||vname(_num(i));
          else _nmiss_=trim(_nmiss_)||" "||vname(_num(i));
        end;
      %end;
      %if &nvarsc GT 0 %then %do;
        do i=1 to &nvarsc;
          if _cmiss(i) EQ '1' then _miss_=trim(_miss_)||" "||vname(_char(i));
          else _nmiss_=trim(_nmiss_)||" "||vname(_char(i));
        end;
      %end;
      call symput('_miss_',left(trim(_miss_)));
      call symput('_nonmiss_',left(trim(_nmiss_)));
    end;
  run;

  options &savopts;

%mend missvars;