Put them back the way you found them: restoring SAS titles

11

Has this ever happened to you? You have a SAS program with statements that you wrote, then you make use of a macro function or %include file supplied by a helpful colleague, and when your SAS code resumes, you find that all of your SAS titles have been changed out from under you!

To be a "good citizen" within a macro function, you should return the SAS environment to the way you found it. You'll occasionally see constructs like this to store and restore SAS option values:

%let _currValidName = %sysfunc(getoption(validvarname)); 
%put Overriding current VALIDVARNAME setting of &_currValidName; 
options validvarname=v7;
 
/* some statements that rely on VALIDVARNAME */
 
%put Restoring previous VALIDVARNAME setting to &_currValidName; 
options validvarname=&_currValidName.;

(Even more is possible with PROC OPTSAVE; see Denise Poll's paper on this topic.)

But what about the titles and footnotes? Is there a way to squirrel those values away before usurping them, and then put them back the way that you found them?

Yes, there is. Even though TITLE and FOOTNOTE are global SAS statements and not system options per se, you can programmatically query the current settings from one of the SAS dictionary tables: SASHELP.VTITLE. (Despite the "VTITLE" name, this table also contains FOOTNOTE settings.)

You can use these macros (or the code within them) to save and restore the current settings for TITLE and FOOTNOTE statements:

/* Define macro to save titles */
%macro saveTitles;
  data _savedTitles;
    set sashelp.vtitle;
  run;
%mend;
 
/* Define macro to restore previously saved titles */
%macro restoreTitles;
  proc sql noprint;
    /* Using a SAS 9.3 feature that allows open-ended macro range */
    select text into :SavedTitles1- from _savedTitles where type="T";
    %let SavedTitlesCount = &sqlobs.;
 
    /* and footnotes */
    select text into :SavedFootnotes1- from _savedTitles where type="F";
    %let SavedFootnotesCount = &sqlobs.;
 
    /* remove data set that stored our titles*/
    drop table _savedTitles;
  quit;
 
  /* emit statements to reinstate the titles */
  TITLE; /* clear interloping titles */
  %do i = 1 %to &SavedTitlesCount.;
    TITLE&i. "&&SavedTitles&i.";
  %end;
 
  FOOTNOTE; /* clear interloping footnotes */
  %do i = 1 %to &SavedFootnotesCount.;
    FOOTNOTE&i. "&&SavedFootnotes&i.";
  %end;
%mend;

Sample use:

title "This is my tremendous title";
title2 "and this is a subtitle";
footnote "Created by ME";
proc means data=sashelp.class;
  var weight;
run;
%saveTitles;
 
%someMacroThatChangesTitles();
 
/* restore the old titles */
%restoreTitles;
proc means data=sashelp.class;
  var height;
run;

There is at least one idiosyncrasy of this approach: if your title or footnote includes a macro variable or expression, that macro will be resolved when the title is stored. So when you restore to the original value with %restoreTitles(), the value will be "stuck" as it was when you used %saveTitles(). If you dislike that limitation, then perhaps some hotshot macro programmer will add a solution here in the comments.

Learn more about SASHELP.VTITLE and DICTIONARY.TITLE from Rick Wicklin's post: How to get the current TITLE in SAS.

Share

About Author

Chris Hemedinger

Director, SAS User Engagement

+Chris Hemedinger is the Director of SAS User Engagement, which includes our SAS Communities and SAS User Groups. Since 1993, Chris has worked for SAS as an author, a software developer, an R&D manager and a consultant. Inexplicably, Chris is still coasting on the limited fame he earned as an author of SAS For Dummies

11 Comments

  1. This is very clever, and just what I need for a project that I am working on. An extra wrinkle for that "hotshot macro programmer" is that although SASHELP.VTITLE always exists, it might have zero observations. This occurs when the user submits a null TITLE statement:
    title; proc contents data=sashelp.vtitle ; run;

    • Chris Hemedinger
      Chris Hemedinger on

      Good point Rick! I updated the example so that it clears TITLE and FOOTNOTE before attempting to restore a value that might not have ever been there.

      Our buddy Vince DelGobbo also pointed out that sequences like:

      TITLE1 "First title";
      TITLE3 "and a smaller title 3";
      

      Would not be restored properly using the current code. Easy enough to correct since it's all in SASHELP.VTITLE, but I leave it as a reader exercise for now.

  2. Chris,

    This is very handy, thank you, and it set me to thinking, and I realised that it may be worth my trying a similar approach for log files.

    In some programs I set file destinations for log and listing output, using proc printto, and then close these destinations at the end of the program.

    So far, so good, ..., but this becomes messy when I %include files that use the same idea and proc printto statements become nested.

    Using an approach similar to this one, I can check sashelp.vextfl for log and listing files (this would require a naming convention for these files) before redirecting output. If there was an active destinations I could restore log and listing output to these instead of using the terminating proc printto; run;.

    Further, I could add a helpful note to tell the reader of the parent log where to find the missing middle chunk of logging.

    Finally, by using a push/pop stack, one should be able to accommodate multiple levels of nesting.

    I think I'm going to be busy tomorrow, thanks!

    Rob

  3. Peter Lancashire on

    A structured way to achieve what is usually required would be push and pop title and footnote statements. This is a feature request. It would be a great idea for options too.

  4. I have a problem with this macro.
    The file « _savestitles » has 3 observations (good!) but when invoking macro « restoretitles », with select type="T", only 1 observation was select (normaly, 2 observations would by selected)...see log below. Executing macros give me a title2 with blanks...

    Thank's for your help !

    • Chris Hemedinger
      Chris Hemedinger on

      Simon, to save space I trimmed out your program log. The issue you're having is that when running SAS 9.1.3, you can't rely on the open-ended range for SELECT INTO -- that's a 9.3 feature (as commented in the code). You'll need to add a SELECT COUNT step to calculate the upper range of the number of titles. That technique is covered in this blog post.

  5. This approach appears not work not to work if one has multiple justifications in the title or footnote:

    title1
    j = l 'LEFT'
    j = c 'CENTER'
    j = r 'RIGHT'
    ;

    ods pdf file = "O:\krviel\SAS\test1.pdf" ;

    data _null_ ;
    file print ;
    put "Test1" ;
    run ;

    ods pdf close ;

    data temp ;
    set sashelp.vtitle ;
    run ;

    title ;
    footnote ;

    data _null_ ;

    set temp ;
    call execute( cat( "title"
    , strip( put( number , 8. -L ))
    , " "
    , quote( strip( text ))
    , " ;"
    )
    ) ;
    call symputx( "title1", text ) ;
    run ;

    ods pdf file = "O:\krviel\SAS\test1.pdf" ;

    proc print
    data = temp ;
    run ;

    title1 "&title1." ;

    proc print
    data = temp ;
    run ;

    ods pdf close ;

    Thank you,

    Kevin

  6. Pingback: How to download and convert CSV files for use in SAS - The SAS Dummy

Back to Top