[LWN Logo]

From: Tom Christiansen <tchrist@mox.perl.com>
Subject: SRC: here docs
Date: 22 May 1998 13:22:16 GMT

Ever want to use a here document with interpolation?  Here's an example
that does that:

    die "Couldn't send mail" unless send_mail(<<"EOTEXT", $target);
    To: $fatboy
    From: Your Manager ($0)
    Cc: @{ get_manager_list($fatboy) }
    Date: @{[ my $now = `date`; chomp $now; $now ]} (today)

    Dear $fatboy,

    Today, you exceeded your 5 kilobyte disk quota for the ${\( 500 +
    int rand(100)) }th and last time.  Your account is now closed.

    Sincerely,
    the management
    EOTEXT

Ever want to indent the body of your here document?  Use a s/// to strip
out leading white space.

    # all in one
    ($VAR = <<HERE_TARGET) =~ s/^\s+//gm;
	your text
	goes here
    HERE_TARGET

    # or with two steps
    $VAR = <<HERE_TARGET;
	your text
	goes here
    HERE_TARGET
    $VAR =~ s/^\s+//gm;

Larry shows us in s2p how a simple fixerupper function can make this 
even easier.

    sub fix {
	my $string = shift;
	$string =~ s/^\s+//gm;
	return $string;
    }

    print fix(<<"END");
	My stuff goes here
    END

    # With function predeclaration, you can omit the parens:
    print fix <<"END";
	My stuff goes here
    END

Ever want to indent your end token as well?   Quote it as well!

    ($quote = <<'    FINIS') =~ s/^\s+//gm;
	    ...we will have peace, when you and all your works have
	    perished--and the works of your dark master to whom you would
	    deliver us. You are a liar, Saruman, and a corrupter of men's
	    hearts.  --Theoden in /usr/src/perl/taint.c
        FINIS
    $quote =~ s/\s*--/\n--/;

Another trick that Larry shows us in s2p is putting a leading 
string in front of the code that's not alive yet.  Let's imagine 
we have a dequote fixerupper function predeclared.  We can do this:

    if ($REMEMBER_THE_MAIN) {
	$perl_main_C = dequote<<'    MAIN_INTERPRETER_LOOP';
	    @@@ int
	    @@@ runops() {
	    @@@     SAVEI32(runlevel);
	    @@@     runlevel++;
	    @@@     while ( op = (*op->op_ppaddr)() ) ;
	    @@@     TAINT_NOT;
	    @@@     return 0;
	    @@@ }
	MAIN_INTERPRETER_LOOP
	$got_it++;
    }

Destroying indentation also tends to get you in trouble with poets.
So you want this to work as well:

    $poem = dequote<<EVER_ON_AND_ON;
	   Now far ahead the Road has gone, 
	      And I must follow, if I can, 
	   Pursuing it with eager feet, 
	      Until it joins some larger way 
	   Where many paths and errands meet. 
	      And whither then? I cannot say. 
		    --Bilbo in /usr/src/perl/pp_ctl.c
    EVER_ON_AND_ON
    print "Here's your poem:\n\n$poem\n"; 

Here's a dequote() function that handles all of these cases.  It looks
to see whether each line begins with a common string, and if so, strips
that off.  Otherwise, it takes the leading white space from the first
line and removes only that much off each line.

    sub dequote {
	local $_ = shift;
	my ($white, $leader);  # common white space and common leading string
	if (/^\s*(?:([^\w\s]+)(\s*).*\n)(?:\s*\1\2?.*\n)+$/) {
	    ($white, $leader) = ($2, quotemeta($1));
	} else {
	    ($white, $leader) = (/^(\s+)/, '');
	} 
	s/^\s*?$leader(?:$white)?//gm;
	return $_;
    } 

--tom
-- 
"A well-written program is its own heaven;
a poorly-written program is its own hell."