commit 2728fe0303aaadfd42385c7e4bca7b2be0ff1afb Author: Danielle Hutzley Date: Fri Mar 17 19:35:37 2023 -0500 Initial Commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..09421b6 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: test install +test: + ./t/script.pl + +install: + install -T cap2site.pod /bin/cap2site + diff --git a/cap2site.pod b/cap2site.pod new file mode 100755 index 0000000..7126019 --- /dev/null +++ b/cap2site.pod @@ -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 "\n"; + } + + if ($state->preformatted) { + if (index($line, ' ') == 0) { + $state->preformatted() = 0; + print "\n"; + } else { print escape($line); } + + continue; + } + + if (index($line, '###') == 0) { + print "

", escape(substr $line, 3), "

\n"; + } + + elsif (index($line, '##') == 0) { + print "

", escape(trimLeft(substr $line, 2)), "

\n"; + } + + elsif (index($line, '#') == 0) { + print "

", escape(trimLeft(substr $line, 2)), "

\n"; + } + + elsif (index($line, ' ') == 0) { + $state->preformatted = 1; + my $alt = substr $line, 3; + + length($alt) == 0 + ? print "
\n"
+        : print '
', "\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 '';
+        print '', escape($content), '';
+        print '', "\n";
+      }
+
+      elsif ($config->isUrl($uri) and $config->isA($uri, 'video')) {
+        print '', "\n";
+      }
+
+      elsif ($config->isUrl($uri) and $config->isA($uri, 'audio')) {
+        print '', "\n";
+      }
+
+      elsif ($config->isUrl($uri)) {
+        print '', escape($content), '', "\n";
+      }
+
+      else {
+        print '

', $line, '

', "\n"; + } + } + + elsif (index($line, '*') == 0) { + print '
  • ', escape(trimLeft(substr $line, 1)), '
  • ', "\n"; + } + + elsif (index($line, '>') == 0) { + print '
    ', escape(trimLeft(substr $line, 1)), '
    ', "\n"; + } + + else { + print '

    '; + print (length($line) == 0 ? '
    ' : escape $line); + print '

    ', "\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 diff --git a/t/hello.gmi b/t/hello.gmi new file mode 100644 index 0000000..2f92213 --- /dev/null +++ b/t/hello.gmi @@ -0,0 +1,2 @@ +# Hello, world! +Lorem ipsum dolor sit amet diff --git a/t/script.pl b/t/script.pl new file mode 100755 index 0000000..cfd206d --- /dev/null +++ b/t/script.pl @@ -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' +

    Hello, world!

    +

    Lorem ipsum dolor sit amet

    +EOF +); +say "DONE!";