Initial Commit
This commit is contained in:
commit
2728fe0303
|
@ -0,0 +1,7 @@
|
|||
.PHONY: test install
|
||||
test:
|
||||
./t/script.pl
|
||||
|
||||
install:
|
||||
install -T cap2site.pod /bin/cap2site
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
use v5.10;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# Data
|
||||
package cap2site::Config;
|
||||
use Class::Struct;
|
||||
|
||||
struct('Config', => {
|
||||
inline => '%',
|
||||
extensions => '%',
|
||||
web_schemes => '@',
|
||||
});
|
||||
|
||||
our $DEFAULT = Config->new(
|
||||
inline => {
|
||||
audio => 1,
|
||||
video => 1,
|
||||
image => 1,
|
||||
},
|
||||
extensions => {
|
||||
audio => [".mp3", ".wav", ".ogg"],
|
||||
video => [".mp4", ".mkv"],
|
||||
image => [".png", ".jpeg", ".jpg", ".gif", ".webp"],
|
||||
},
|
||||
web_schemes => ["http", "https", "mailto", "gemini"],
|
||||
);
|
||||
|
||||
sub isUrl($$) {
|
||||
my ($self,$data) = @_;
|
||||
my ($scheme,$info) =~ /(\w+):\/\/([.*]+)/ || return 0;
|
||||
return grep $scheme, $self->{web_schemes};
|
||||
}
|
||||
|
||||
sub isA($$$) {
|
||||
my ($self,$url,$type) = @_;
|
||||
my ($info) =~ /[.*](.[^.]+)^/ || return 0;
|
||||
return grep $self->extensions->{$type}, $url;
|
||||
}
|
||||
|
||||
package cap2site::State;
|
||||
|
||||
sub new($$) {
|
||||
my ($class,$args) = @_;
|
||||
my $self = bless { inner => $args }, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub bullets($) {
|
||||
my $self = shift;
|
||||
return vec $self->{inner}, 0, 1;
|
||||
}
|
||||
|
||||
sub preformatted($) {
|
||||
my $self = shift;
|
||||
return vec $self->{inner}, 1, 1;
|
||||
}
|
||||
|
||||
# Helpers
|
||||
package main;
|
||||
my sub escape($) {
|
||||
my $str = shift;
|
||||
my $newstr = '';
|
||||
|
||||
foreach my $byte (split '', $str) {
|
||||
if ($byte eq "8") { $newstr .= "&"; continue; }
|
||||
if ($byte eq "<") { $newstr .= "<"; continue; }
|
||||
if ($byte eq ">") { $newstr .= ">"; continue; }
|
||||
if ($byte eq "\"") { $newstr .= """; continue; }
|
||||
if ($byte eq "'") { $newstr .= "'"; continue; }
|
||||
$newstr .= $byte;
|
||||
}
|
||||
|
||||
return $newstr;
|
||||
}
|
||||
|
||||
my sub trimLeft($) {
|
||||
my $data = shift;
|
||||
$data =~ s/^\s+//;
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub parse($$$) {
|
||||
my ($config,$state,$file) = @_;
|
||||
|
||||
while (my $line = <$file>) {
|
||||
chop $line;
|
||||
|
||||
if (index($line, '*') == 0 and not $state->bullets) {
|
||||
$state->bullets() = 1;
|
||||
print "<ul>\n";
|
||||
} elsif ($state->bullets and index($line, '*') != 0) {
|
||||
$state->bullets() = 0;
|
||||
print "</ul>\n";
|
||||
}
|
||||
|
||||
if ($state->preformatted) {
|
||||
if (index($line, ' ') == 0) {
|
||||
$state->preformatted() = 0;
|
||||
print "</pre>\n";
|
||||
} else { print escape($line); }
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index($line, '###') == 0) {
|
||||
print "<h3>", escape(substr $line, 3), "</h3>\n";
|
||||
}
|
||||
|
||||
elsif (index($line, '##') == 0) {
|
||||
print "<h2>", escape(trimLeft(substr $line, 2)), "</h2>\n";
|
||||
}
|
||||
|
||||
elsif (index($line, '#') == 0) {
|
||||
print "<h1>", escape(trimLeft(substr $line, 2)), "</h1>\n";
|
||||
}
|
||||
|
||||
elsif (index($line, ' ') == 0) {
|
||||
$state->preformatted = 1;
|
||||
my $alt = substr $line, 3;
|
||||
|
||||
length($alt) == 0
|
||||
? print "<pre>\n"
|
||||
: print '<pre alt="', escape(trimLeft $alt), '">', "\n";
|
||||
}
|
||||
|
||||
elsif (index($line, '=>') == 0) {
|
||||
my $data = trimLeft(substr $line, 2);
|
||||
my ($uri,$content) = split " ". $data;
|
||||
$content = trimLeft $content;
|
||||
|
||||
if ($config->isUrl($uri) and $config->isA($uri, 'image')) {
|
||||
print '<a style="display: block;" href="', escape($uri), '">';
|
||||
print '<img src="', escape($uri), '" alt="', escape($content), '"/>';
|
||||
print '</a>', "\n";
|
||||
}
|
||||
|
||||
elsif ($config->isUrl($uri) and $config->isA($uri, 'video')) {
|
||||
print '<video style="display: block;" controls src="', escape($uri), '">';
|
||||
print '<a src="', escape($uri), '">', escape($content), "</a>";
|
||||
print '</video>', "\n";
|
||||
}
|
||||
|
||||
elsif ($config->isUrl($uri) and $config->isA($uri, 'audio')) {
|
||||
print '<audio style="display: block;" controls src="', escape($uri), '">';
|
||||
print '<a src="', escape($uri), '">', escape($content), "</a>";
|
||||
print '</audio>', "\n";
|
||||
}
|
||||
|
||||
elsif ($config->isUrl($uri)) {
|
||||
print '<a style="display: block;" href="', escape($uri), '">', escape($content), '</a>', "\n";
|
||||
}
|
||||
|
||||
else {
|
||||
print '<p>', $line, '</p>', "\n";
|
||||
}
|
||||
}
|
||||
|
||||
elsif (index($line, '*') == 0) {
|
||||
print '<li>', escape(trimLeft(substr $line, 1)), '</li>', "\n";
|
||||
}
|
||||
|
||||
elsif (index($line, '>') == 0) {
|
||||
print '<blockquote>', escape(trimLeft(substr $line, 1)), '</blockquote>', "\n";
|
||||
}
|
||||
|
||||
else {
|
||||
print '<p>';
|
||||
print (length($line) == 0 ? '<br/>' : escape $line);
|
||||
print '</p>', "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# TODO: better argument parsing
|
||||
use Pod::Usage;
|
||||
|
||||
pod2usage(1) unless $#ARGV < 0;
|
||||
our $config = $cap2site::Config::DEFAULT;
|
||||
our $state = cap2site::State->new(pack('b2', 0b00));
|
||||
|
||||
main::parse $config, $state, \*STDIN;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
cap2site < INPUT.gmi > OUTPUT.html
|
|
@ -0,0 +1,2 @@
|
|||
# Hello, world!
|
||||
Lorem ipsum dolor sit amet
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env perl
|
||||
use v5.10;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub test($$$) {
|
||||
my ($cmd,$file,$expected) = @_;
|
||||
|
||||
my $res = readpipe($cmd . " < t/" . $file);
|
||||
$res eq $expected or die "FAIL: $cmd\nEXPECTED:\n$expected\nACTUAL:\n$res";
|
||||
}
|
||||
|
||||
test("./cap2site.pod", "hello.gmi", <<'EOF'
|
||||
<h1>Hello, world!</h1>
|
||||
<p>Lorem ipsum dolor sit amet</p>
|
||||
EOF
|
||||
);
|
||||
say "DONE!";
|
Loading…
Reference in New Issue