From e6106e4bfd22aa448fd972150e6bcc39e9fbcb45 Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Mon, 13 Aug 2012 14:23:54 -0400 Subject: Split scripts directory into bin and lib. --- (limited to 'lib/MarkdownBook/Book.pm') diff --git a/lib/MarkdownBook/Book.pm b/lib/MarkdownBook/Book.pm new file mode 100644 index 0000000..4f30afb --- /dev/null +++ b/lib/MarkdownBook/Book.pm @@ -0,0 +1,264 @@ +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +use strict; +use warnings; + +use Carp; + +package MarkdownBook::Book; + +sub new +{ + my ($class, $format, $dir) = @_; + my $self; + my $control_fh; + + $class = ref($class) || $class; + $self = {}; + bless($self, $class); + + unless ($format =~ m/^[a-z]+/) { + Carp::croak('Invalid format "' . $format . '"'); + } + eval { + require 'MarkdownBook/Document/' . $format . '.pm'; + 1; + } or Carp::croak('Unsupported format "' . $format . '"'); + $self->{'format'} = $format; + + $self->{'dir'} = $dir; + $self->{'docs'} = []; + $self->{'sections'} = []; + $self->{'sections_by_id'} = {}; + + open($control_fh, '<', $dir . '/control'); + while (<$control_fh>) { + chomp($_); + if (m/^Title: /) { + s/^Title: (.*)$/$1/; + $self->{'title'} = $_; + } + } + close($control_fh); + + $self->create_documents(); + + return $self; +} + +sub dir +{ + my ($self, $dir) = @_; + my $old = $self->{'dir'}; + + $self->{'dir'} = $dir if defined($dir); + + return $old; +} + +sub title +{ + my ($self, $title) = @_; + my $old = $self->{'title'}; + + $self->{'title'} = $title if defined($title); + + return $old; +} + +sub documents +{ + my ($self, $documents) = @_; + my $old = $self->{'docs'}; + + $self->{'docs'} = $documents if defined($documents); + + return $old; +} + +sub _get_document_module +{ + my ($self) = @_; + + return 'MarkdownBook::Document::' . $self->{'format'}; +} + +sub create_documents +{ + my ($self) = @_; + my $series_fh; + my $file; + my $title; + my $i; + my $doc; + my $doc_prev; + my @letters; + + # Create index document. + $doc = $self->_get_document_module()->new($self, 'index', 'index', + undef, $self->{'title'}); + $doc_prev = $doc; + push(@{$self->{'docs'}}, $doc); + + # Create chapter documents. + $i = 0; + open($series_fh, '<', $self->{'dir'} . '/chapters') + or Carp::croak('Cannot open chapters file'); + while (<$series_fh>) { + chomp($_); + ($file, $title) = split(/[ \t]+/, $_, 2); + $doc = $self->_get_document_module()->new($self, 'chapter', $file, + ++$i, $title); + $doc->prev($doc_prev); + $doc_prev->next($doc) if defined $doc_prev; + $doc_prev = $doc; + push(@{$self->{'docs'}}, $doc); + } + close($series_fh); + + # Create appendix documents. + $i = -1; + @letters = ('A' .. 'Z'); + if (-e $self->{'dir'} . '/appendices') { + open($series_fh, '<', $self->{'dir'} . '/apendices') + or Carp::croak('Cannot open appendices file'); + while (<$series_fh>) { + chomp($_); + ($file, $title) = split(/[ \t]+/, $_, 2); + $doc = $self->_get_document_module()->new($self, 'appendix', $file, + $letters[++$i], $title); + $doc->prev($doc_prev); + $doc_prev->next($doc) if defined $doc_prev; + $doc_prev = $doc; + push(@{$self->{'docs'}}, $doc); + } + close($series_fh); + } +} + +sub add_section +{ + my ($self, $section) = @_; + + push(@{$self->{'sections'}}, $section); + + # Index sections (not documents) by ID. + if (ref($section) eq 'MarkdownBook::Section') { + $self->{'sections_by_id'}->{$section->id()} = $section; + } +} + +sub subst_macros +{ + my ($self, $text) = @_; + + # Substitute macros with arguments. + $text =~ s/ + \$ # Dollar sign + \[ # Left square bracket + ([^\]]+) # Macro + \] # Right square bracket + ( + \[ # Left square bracket + ([^\]]+) # Macro arguments + \] # Right square bracket + ) + /$self->_do_subst_macro($1, split(m@[ \t]+@, $3))/exg; + + # Substitute macros without arguments. + $text =~ s/ + \$ # Dollar sign + \[ # Left square bracket + ([^\]]+) # Macro + \] # Right square bracket + /$self->_do_subst_macro($1)/exg; + + return $text; +} + +sub _do_subst_macro +{ + my ($self, $macro, @args) = @_; + my $sec; + + if ($macro eq 'toc') { + return $self->_do_gen_toc(); + } elsif ($macro eq 'sectlink') { + if (@args != 1) { + Carp::carp('Invalid arguments to "sectlink" macro'); + } else { + $sec = $self->{'sections_by_id'}->{$args[0]}; + return '[ยง ' . $sec->number() . '][' . $sec->id() . ']'; + } + } else { + Carp::carp("Unrecognized macro \"$macro\""); + } +} + +sub _do_gen_toc +{ + my ($self) = @_; + my $section; + my $toc = ''; + + foreach $section (@{$self->{'sections'}}) { + $toc .= "\n" if $toc ne ''; + if (ref($section) =~ m/^MarkdownBook::Document/) { + next if $section->type() eq 'index'; + $toc .= ' * ['; + $toc .= $section->id(); + $toc .= ' '; + $toc .= $section->title(); + $toc .= ']['; + $toc .= $section->file(); + $toc .= ']'; + } elsif (ref($section) eq 'MarkdownBook::Section') { + $toc .= ' ' x $section->level(); + $toc .= ($section->level() == 1 ? ' - [' : ' * ['); + $toc .= $section->number(); + $toc .= ' '; + $toc .= $section->title(); + $toc .= ']['; + $toc .= $section->id(); + $toc .= ']'; + } + } + + return $toc; +} + +sub parse +{ + my ($self) = @_; + my $doc; + + foreach $doc (@{$self->{'docs'}}) { + $self->add_section($doc); + $doc->parse(); + } +} + +sub output +{ + my ($self) = @_; + my $doc; + + foreach $doc (@{$self->{'docs'}}) { + $doc->output(); + } +} + +1; -- cgit v0.9.1