/*

/ Program      : xl2sas.sas
/ Version      : 1.0
/ Author       : Roland Rashleigh-Berry
/ Date         : 03-Feb-2008
/ Purpose      : Read an html Excel spreadsheet into a sas dataset using DDE
/ SubMacros    : none
/ Notes        : This is meant to be run interactively. The start and end rows
/                and columns you specify are those required to read the grid of
/                spreadsheet cells you are interested in. For columns, A=1, B=2,
/                etc.. Numeric integer values must be given for these. You must
/                have Excel for this to work. This was especially written for
/                Excel spreadsheets that are really html files such as those
/                written using "ods html file=xxx.xls;" as Excel can correctly
/                open these html files and treat them as normal spreadsheets and 
/                communicate the cell values through DDE.
/
/                Note that because of rules for rendering html, double spaces in
/                cell values in the html spreadsheet file will normally be
/                converted to single spaces unless the value is protected by
/                tags or the spaces are non-breaking spaces ("A0"x).
/
/ Usage        : %xl2sas(xlfile=C:\myfiles\myspred.xls,sheetname=Results,
/                        dsout=sasuser.myspred,compress=no,vpref=_col,vlen=50,
/                        startrow=5,startcol=1,endrow=95,endcol=10)
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ xlfile            (no quotes) Full path name of spreadsheet file (not quoted
/                   unless there are spaces in the file name in which case
/                   enclose those parts or the whole name in double quotes).
/ sheetname         (no quotes) Name of spreadsheet sheet you want to read in.
/                   This will be visible as the bottom tag name when you open
/                   the spreadsheet in Excel.
/ secswait=2        Number of seconds to wait for Excel to come up
/ dsout             Output dataset name
/ compress=no       (no quotes) Set to "yes" to compress for all spaces. This
/                   will include the non-breaking space character "A0"x .
/                   Note this this action, if set to "yes", will effectively
/                   override the actions of compbl= and left= .
/ compbl=yes        (no quotes) By default, compress multiple spaces into single
/                   spaces. This will include the non-breaking space character
/                   "A0"x . Even if you set this to "no" then Excel itself will
/                   compress multiple spaces into single spaces unless the tags
/                   in the html file protect the values or the spaces are non-
/                   breaking spaces ("A0"x).
/ left=yes          (no quotes) By default, left-align fields by removing
/                   leading spaces. Leading spaces include the non-breaking
/                   space character "A0"x . Even if you set this to "no" then
/                   Excel itself will left-align text by dropping leading
/                   spaces unless the tags in the html file protect the values
/                   or the spaces are non-breaking spaces ("A0"x).
/ dropblanklines=yes (no quotes) By default, drop lines where all the values in
/                   the column range you specify are blank.
/ vpref=C           Prefix for the numbered variables created in the sas dataset
/ vlen=80           Length of the sas dataset variables (they are all character)
/ startrow=1        Start row to read cells from
/ startcol=1        Start column to read cells from
/ endrow            End row to read cells from
/ endcol            End column to read cells from
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ 
/===============================================================================
/ 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: xl2sas v1.0;

%macro xl2sas(xlfile=,
           sheetname=,
            secswait=2,
               dsout=,
            compress=no,
              compbl=yes,
                left=yes,
      dropblanklines=yes,
               vpref=C,
                vlen=80,
            startrow=1,
            startcol=1,
              endrow=,
              endcol=
              );

%local error opts maxvarnum;
%let error=0;



      /*----------------------------------*
              Check input parameters
       *----------------------------------*/

%if not %length(&xlfile) %then %do;
  %let error=1;
  %put ERROR: (xl2sas) No Excel spreadsheet file name supplied to xlfile=;
%end;
%else %do;
  %if not %sysfunc(fileexist(&xlfile)) %then %do;
    %let error=1;
    %put ERROR: (xl2sas) xlfile=&xlfile can not be found;
  %end;
%end;


%if not %length(&sheetname) %then %do;
  %let error=1;
  %put ERROR: (xl2sas) No Excel spreadsheet sheet name supplied to sheetname=;
%end;


%if not %length(&secswait) %then %let secswait=2;
%else %do;
  %if %length(%sysfunc(compress(&secswait,1234567890))) %then %do;
    %let error=1;
    %put ERROR: (xl2sas) An integer number of seconds is required. You specified secswait=&secswait;
  %end;
%end;


%if not %length(&dsout) %then %do;
  %let error=1;
  %put ERROR: (xl2sas) No output dataset name supplied to dsout=;
%end;


%if not %length(&compress) %then %let compress=no;
%let compress=%upcase(%substr(&compress,1,1));


%if not %length(&compbl) %then %let compbl=yes;
%let compbl=%upcase(%substr(&compbl,1,1));


%if not %length(&left) %then %let left=yes;
%let left=%upcase(%substr(&left,1,1));


%if not %length(&dropblanklines) %then %let dropblanklines=yes;
%let dropblanklines=%upcase(%substr(&dropblanklines,1,1));


%if not %length(&vpref) %then %let vpref=C;


%if not %length(&vlen) %then %let vlen=80;


%if not %length(&startrow) %then %let startrow=1;
%else %do;
  %if %length(%sysfunc(compress(&startrow,1234567890))) %then %do;
    %let error=1;
    %put ERROR: (xl2sas) An integer is required. You specified startrow=&startrow;
  %end;
%end;


%if not %length(&startcol) %then %let startcol=1;
%else %do;
  %if %length(%sysfunc(compress(&startcol,1234567890))) %then %do;
    %let error=1;
    %put ERROR: (xl2sas) An integer is required. You specified startcol=&startcol;
  %end;
%end;


%if not %length(&endrow) %then %do;
  %let error=1;
  %put ERROR: (xl2sas) No integer specified for endrow=;
%end;
%else %do;
  %if %length(%sysfunc(compress(&endrow,1234567890))) %then %do;
    %let error=1;
    %put ERROR: (xl2sas) An integer is required. You specified endrow=&endrow;
  %end;
%end;


%if not %length(&endcol) %then %do;
  %let error=1;
  %put ERROR: (xl2sas) No integer specified for endcol=;
%end;
%else %do;
  %if %length(%sysfunc(compress(&endcol,1234567890))) %then %do;
    %let error=1;
    %put ERROR: (xl2sas) An integer is required. You specified endcol=&endcol;
  %end;
%end;


%if &error %then %goto error;



      /*---------------------------------*
              Store current options
       *---------------------------------*/

%*- store current xwait and xsync settings -;
%let opts=%sysfunc(getoption(xwait,keyword)) %sysfunc(getoption(xsync,keyword)); 



      /*---------------------------------*
              Read the spreadsheet
       *---------------------------------*/

%*- calculate highest numbered sas variable -;
%let maxvarnum=%eval(&endcol-&startcol+1);


*- set required options for dde to work correctly -;
options noxwait noxsync;


*- start up Excel by opening the spreadsheet -;
X &xlfile;


*- wait for Excel to finish starting up -;
data _null_;
  x=sleep(&secswait);
run;


*- assign filerefs -;
filename _xlin dde "Excel|&sheetname!R&startrow.C&startcol:R&endrow.C&endcol" lrecl=3000;
filename _xlcmd dde 'Excel|system' lrecl=3000;


*- Excel command to remove new-line characters -;
data _null_;
  file _xlcmd;
  put "[error(FALSE)]";
  put "[FORMULA.REPLACE(""%sysfunc(byte(10))"","""",2,1,FALSE,FALSE)]" ;
run;


*- read in the spreadsheet page -;
data &dsout;
  length &vpref.1-&vpref.&maxvarnum $ &vlen;
  infile _xlin dlm='09'x notab dsd pad missover;
  input &vpref.1-&vpref.&maxvarnum;
run;


*- deassign filerefs -;
filename _xlin clear;
filename _xlcmd clear;



      /*---------------------------------*
                 Restore options
       *---------------------------------*/

*- restore previous xwait and xsync settings -;
options &opts;



      /*---------------------------------*
                Edit the dataset
       *---------------------------------*/

*- edit the fields and drop rows depending on options set -;
%if "&compress" EQ "Y" or "&compbl" EQ "Y" or "&left" EQ "Y"
 or "&dropblanklines" EQ "Y" %then %do;
  data &dsout;
    retain accum "  ";
    set &dsout;
    array &vpref.ra {*} &vpref.1-&vpref.&maxvarnum;
    accum=" ";
    do i=1 to dim(&vpref.ra);
      %if "&compress" EQ "Y" or "&compbl" EQ "Y" or "&left" EQ "Y" %then %do;
        *- translate the non-breaking space into an ordinary space -;
        &vpref.ra(i)=translate(&vpref.ra(i)," ","A0"x);
      %end;
      %if "&compress" EQ "Y" %then %do;
        *- compress for all spaces -;
        &vpref.ra(i)=compress(&vpref.ra(i));
      %end;
      %else %do;
        %if "&compbl" EQ "Y" %then %do;
          *- compress for multiple blanks spaces -;
          &vpref.ra(i)=compbl(&vpref.ra(i));
        %end;
        %if "&left" EQ "Y" %then %do;
          *- left-align field -;
          &vpref.ra(i)=left(&vpref.ra(i));
        %end;
      %end;
      accum=trim(left(accum))||trim(left(&vpref.ra(i)));
    end;
    %if "&dropblanklines" EQ "Y" %then %do;
      if accum ne " " then output;
    %end;
    drop i accum;
  run;
%end;



      /*---------------------------------*
                       Exit
       *---------------------------------*/

%goto skip;
%error: %put ERROR: (xl2sas) Leaving macro due to error(s) listed;
%skip:

%mend;