#!/usr/bin/env perl # # Convert darcs changes into RSS 1.0 # # Version 0.6, 2005/02/07 Pedro Melo # use strict; $^W = 1; # "use warnings" for dysfunctional older Perls use XML::RSS; use XML::Parser; use Getopt::Long; use Config::Simple; # # Configuration hash # my %config; # Defaults for the main options # Your project name and how many changes to use $config{project} = "Netrek Vanilla Server"; $config{n_items} = 25; # Where is markdown? $config{markdown_path} = 'Markdown.pl'; ################################################ Nothing to edit below my $result = 0; my %cmd_line_opts; eval { # If you change any options here, also update them in the usage docs below. $result = GetOptions( 'project|title|p=s' => \$cmd_line_opts{project}, 'homepage|link|h=s' => \$cmd_line_opts{homepage}, 'description|d=s' => \$cmd_line_opts{description}, 'url_cgi|u=s' => \$cmd_line_opts{patch_cgi}, 'n_items|n=i' => \$cmd_line_opts{n_items}, 'darcs|c=s' => \$cmd_line_opts{darcs_cmd}, 'filter|f=s' => \$cmd_line_opts{filter}, 'markdown|m=s' => \$cmd_line_opts{markdown_path}, ) }; my $repo = shift || '.'; my @files = ( '/etc/darcs2rss/config', "$ENV{HOME}/.darcs2rss", "$repo/.darcs2rss", ); foreach my $cfg (@files) { Config::Simple->import_from($cfg, \%config) if -f $cfg && -r _; } while (my ($key, $value) = each %cmd_line_opts) { $config{$key} = $value if defined $value; } $config{darcs_cmd} ||= 'darcs'; $config{filter} ||= 'basic'; my $filter; if ($config{filter}) { if ($config{filter} eq 'markdown') { if (!$config{markdown_path}) { print STDERR "Error: filter markdown selected, but I need to know where Markdown.pl is\n"; print STDERR " use --markdown /path/to/Markdown.pl to fix\n"; exit(1); } eval { $blosxom::version = 1; require $config{markdown_path}; }; if ($@) { print STDERR "Error: failed loading Markdown module from '$config{markdown_path}'\n"; exit(1); } $filter = \&markdown_filter; } elsif ($config{filter} eq 'pre') { $filter = \&pre_filter; } elsif ($config{filter} eq 'basic') { $filter = \&basic_filter; } else { print STDERR "Error: filter type '$config{filter}' is unknown to me. See $0 --help for options \n"; exit(1); } } my $cmdline = "$config{darcs_cmd} changes --xml-output"; $cmdline .= " --last $config{n_items}" if $config{n_items}; my $err_msg; if (!$result) { $err_msg = "\n"; # Getopt::Long already printed something to STDERR } elsif (!chdir($repo)) { $err_msg = "could not chdir into repository '$repo': $!"; } elsif (!-r '_darcs') { $err_msg = "$repo is not a darcs repo. _darcs does not exist or is not readable"; } elsif (`$config{darcs_cmd} --commands 2>&1` !~ m/rollback/) { $err_msg = "'$config{darcs_cmd}' does not appear to be a working darcs binary"; } elsif (!open(CHANGES, "$cmdline |")) { $err_msg = "could not execute '$config{darcs_cmd}': $!\n" } if ($err_msg) { warn "$err_msg\n\n"; die < /path/to/rss_file # From outside a repo darcs2rss /path/to/your/repo > /path/to/rss_file Options: --project (also --title and -p): project name --homepage (also --link and -h): project homepage --description (also -d): project description --url_cgi (also -u): URL of darcs.cgi script --n_items (also -n): number of changes to include, default 25, 0 for unlimited --darcs (also -c): darcs command to use, default search PATH for 'darcs' --filter (also -f): filter comments - see available filters below --markdown (also -m): Markdown.pl location (mandatory if filter is markdown) All the long options are case-insensitive and can be abbreviated to uniqueness. So you can use --description or --desc but not --d because it could mean --darcs The following filters are available: - basic: convert line breaks and encode HTML entities if possible - markdown: filters your comment with Markdown - pre: puts your comment between


If you want to use Markdown, you must specify the Markdown.pl location
with --markdown.

USAGE
}

my $rss = XML::RSS->new( version => '1.0' );

$rss->channel(
  title => $config{project},
  link  => $config{homepage} || '#',
  description => $config{description} || ' ',
);


my $ctx = {};
my $parser = XML::Parser->new(
    Handlers => {
      Start => sub { _handle_start( $ctx, @_ ) },
      End   => sub { _handle_end( $ctx, @_ )   },
      Char  => sub { _handle_char( $ctx, @_ )  },
    },
);
$parser = $parser->parse_start;

while() {
  $parser->parse_more($_);
}
$parser->parse_done;


print $rss->as_string;


#################################################
# XML::Parser handlers

sub _handle_start {
  my $ctx = shift;
  my $expat = shift;
  my $elem = shift;
 
  if ($elem eq 'patch') {
    $ctx->{attrs} = { @_ };
  }
  elsif ($elem eq 'name' || $elem eq 'comment') {
    $ctx->{inside} = $elem;
  }
}


sub _handle_end {
  my $ctx = shift;
  my $expat = shift;
  my $elem = shift;

  if ($elem eq 'patch') {
    my $link = "";
    $link = "$config{patch_cgi}?c=annotate&p=$ctx->{attrs}{hash}" if $config{patch_cgi};

    my $item_date;
    if ($ctx->{attrs}{date} =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) {
      $item_date = "$1-$2-$3T$4:$5:$6+00:00"; # TODO: fix timezone info
    }

    my $comment = $ctx->{comment};
    if ($filter && $comment) {
      $comment = $filter->($comment);
    }
    
    $rss->add_item(
      title => $ctx->{name} || "no title",
      # default to '#' because a link is required in the RSS spec
      link  => $link || '#',
      description => $comment,

      dc => {
        subject => "Patches",
        creator => $ctx->{attrs}{author} || "no author",
        date    => $item_date,
      },
    );

    delete $ctx->{attrs};
    delete $ctx->{name};
    delete $ctx->{comment};
  }
  elsif ($elem eq 'name' || $elem eq 'comment') {
    delete $ctx->{inside};
  }
}


sub _handle_char {
  my $ctx = shift;
  my $expat = shift;
  
  if ($ctx->{inside}) {
    $ctx->{$ctx->{inside}} .= shift;
  }
}


#################################################
# Filters available for comments

sub markdown_filter {
  return Markdown::Markdown(shift);
}


sub pre_filter {
  my $comment = shift;
  return "
$comment
"; } sub basic_filter { my $comment = shift; eval { require HTML::Entities; }; if ($@) { # we could encode more HTML characters if HTML::Entities was installed; } else { import HTML::Entities; $comment = encode_entities($comment) } # To handle all kinds of line endings. See Programming Perl, 3rd Ed, page 623 $comment =~ s/\015?\012/\n/g; # translate two or more line breaks to a paragraph break $comment =~ s#\n\n+#

#g; # translate a single line break to
$comment =~ s#\n#
#g; return "

$comment

"; }