Here's the complete text of the entry you were just reading. To return to the whole blog, click the Blogolalia button at left.

July 18, 2003  

Second Plugin

It's been awhile since I've considered the documentation to be the enemy, but this Perl stuff puts me in a mood.... I've just finished my second Blosxom plugin, and the frustration with Programming Perl mounts. Gotta get me a reference book by someone a bit less ADHD ;-)

This second plugin is every bit as lazy as the first.... I would just as soon write as few html tags inside these entry files as possible. Since the biggest tagging chore is marking each "paragraph" (or blockquote or dfn or whatever the main format is for the entry text) with an open tag and a close tag... why not let a plugin do it?

(Well, one reason not to let a plugin do it is trying to figure out how to make a plugin do it, when all you've got to read are chatty reference works by the Larry crew; about as speedy a guide as the Dictionary of the Khazaars....)

Since this plugin just makes a couple of substitution runs through an entry, its results aren't very sophisticated; we're not talking Tiki territory here. But for a mindless tool, it's not too bad....

I should be good at Perl -- I have the essential laziness. When I encountered Blosxom, I loved every bit of it...except for its bloggedly steadfast desire to deal with files by their mod times. I want my entries to show up in a dated order, but I also want to edit those files post-bloggo and not have their original order messed up. In short, I want to sort entries by name, and I want to control any "timestamping" tht occurs.

My first plugin works with a hack in Blosxom to give me just what I want: by using a blank date.flavour file, blosxom's automatic timestamping is suppressed; by adding another var in a chomp instruction, I can add a "date" line to my text entry files; by sorting on names in a plugin I can get entries in the same order time after time, no matter when I edit/upload them.

All without two or three OPs plugins that I don't understand anyway ;-)

Heck, I can write my own plugins that I don't's one now.

Pattern-matching is a place Perl counts coup, so I wanted to try it out. The idea is to use blank lines (2 or more consecutive newlines) to indicate the start/end of a block of text; appropriate tags are inserted before/after the blocks.

First, we declare some tags to open and close html blocks:

# Could be "<blockquote>" and "</blockquote>", or 
# "<dfn>" and :</dfn>", etc. Default is "<p>" and "</p>"
  $open_p_tag = '<p>';
  $close_p_tag = '</p>';

Then, for reasons I don't understand, we reduce all multiple newlines to 2 newlines:

$$body_ref =~ s/\n{2,}/\n\n/gm;
and strip leading and trailing newlines; set a flag if 1 or more existed:
$have_starting_blanklines = $$body_ref =~ s/^\n+//s;
$have_ending_blanklines = $$body_ref =~ s/\n+$//s;

Now, loop through the entry, adding tags where there aren't any:

# change untagged start-of-blocks (except start of first block)
$$body_ref =~ s/\n\n(?:(?!\<))/\n\n$open_p_tag\n/gm;

# change all untagged end-of-blocks (except end of last block)
$$body_ref =~ s/(?:(?<!\>))\n\n/\n$close_p_tag\n\n/gm;

Yup -- these substitutions don't touch the "ends" of the entry; we have to do them separately. Since we have to "think" about doing them, we might as well think about not-doing them.... So back up in Configuration, let's add a couple of flags:

#  Should p_tagger do anything to the start of the very first block?
#  0 = no; 1 = yes
$initial_open = 0;
#  ... to the end of the very last block?
#  0 = no; 1 = yes
$final_close = 1;

Now we test the flag and (maybe) add missing open/close tags at the very beginning and end of the entry:

# change the first start-of-block if permission and untagged
($initial_open) && ($$body_ref =~ s/^(?:(?!<))/$open_p_tag\n/s);
# change the last end-of-block if permission and untagged
($final_close) && ($$body_ref =~ s/(?:(?<!>))$/\n$close_p_tag\n/s);

Finally, we put back a newline at the top or bottom, if there was one (or more) there before:

($have_starting_blanklines) && ($$body_ref =~ s/^/\n/);
($have_ending_blanklines) && ($$body_ref =~ s/$/\n/);

Here's the stripped-down whole thing:

package blox;

# Configuration Section
 $open_p_tag = '

'; $close_p_tag = '

'; # Should blox do anything to the start of the very first block? # 0 = no; 1 = yes $add_initial_open_tag = 0; # # ... to the end of the very last block? # 0 = no; 1 = yes $add_final_close_tag = 1; sub start { 1; } sub story { my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_; $have_starting_blanklines = $$body_ref =~ s/^\n+//s; $have_ending_blanklines = $$body_ref =~ s/\n+$//s; $$body_ref =~ s/\n{2,}/\n\n/gm; $$body_ref =~ s/\n\n(?:(?!\<))/\n\n$open_p_tag\n/gm; $$body_ref =~ s/(?:(?))\n\n/\n$close_p_tag\n\n/gm; ($add_initial_open_tag) && ($$body_ref =~ s/^(?:(?!<))/$open_p_tag\n/s); ($add_final_close_tag) && ($$body_ref =~ s/(?:(?))$/\n$close_p_tag/s); ($have_starting_blanklines) && ($$body_ref =~ s/^/\n/); ($have_ending_blanklines) && ($$body_ref =~ s/$/\n/); $$body_ref =~ s/($open_p_tag\n) (<)/$1$2/gm; $$body_ref =~ s/(>) (\n$close_p_tag)/$1$2/gm; 1; } 1;