811 lines
18 KiB
Perl
811 lines
18 KiB
Perl
|
#----------------------------------------------------------------------
|
|||
|
package UVtemplate;
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
=head1 NAME
|
|||
|
|
|||
|
UVtemplate - Templateverarbeitung und String-Formatierungen
|
|||
|
|
|||
|
=head1 SYNOPSIS
|
|||
|
|
|||
|
use UVtemplate;
|
|||
|
|
|||
|
$plate = UVtemplate->new([%keys]);
|
|||
|
|
|||
|
$plate->setKey(%keys);
|
|||
|
$item = $plate->addListItem($name, %keys);
|
|||
|
|
|||
|
$string = $plate->processTemplate($file);
|
|||
|
|
|||
|
=head1 DESCRIPTION
|
|||
|
|
|||
|
Mit Hilfe von UVtemplate, wird die komplette Aufbereitung und
|
|||
|
Formatierung der Programmausgaben nicht nur ausgelagert sondern
|
|||
|
auch so flexibiliert, dass sie jederzeit angepasst werden kann,
|
|||
|
ohne das im eigentlichen Programmcode veraendert werden muss.
|
|||
|
|
|||
|
Auf Programmseite wird eine Datenstruktur mit Schluessel-Wert
|
|||
|
Paaren erzeugt. In den Template-Dateien werden dann spaeter die
|
|||
|
jeweiligen Schluessel, durch ihre im Programm festgelegten
|
|||
|
Werte ersetzt. Zusaetzlich ist es moeglich Schluessel zu Listen
|
|||
|
zusammenzufassen.
|
|||
|
|
|||
|
Da es sich bei den Templates um Ascii-Texte handelt, gibt es
|
|||
|
zusaetzlich die Moeglichkeit die Werte der Schluessel zuformatieren
|
|||
|
um eine einheitliche Ausgabe zu ermoeglichen. D.h. es kann z.B. durch
|
|||
|
das Anhaengen von Leerzeichen dafuer gesorgt werden, das ein Schluessel
|
|||
|
einer Liste immer 60 Zeichen lang ist um ansehnliche Tabellen auszugeben.
|
|||
|
|
|||
|
=head1 FUNCTIONS
|
|||
|
|
|||
|
=over 3
|
|||
|
|
|||
|
=cut
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
use strict;
|
|||
|
use vars qw( $VERSION $functions @dirs);
|
|||
|
use UVconfig;
|
|||
|
|
|||
|
$VERSION = 0.1;
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
=item new
|
|||
|
|
|||
|
Eine neues Objekt vom Typ UVtemplate anlegen.
|
|||
|
|
|||
|
my $plate = UVtemplate->new();
|
|||
|
|
|||
|
Als Parameter koennen gleich beliebig viele Schluessel-Wert-Paare
|
|||
|
uebergeben werden.
|
|||
|
|
|||
|
=cut
|
|||
|
|
|||
|
sub new{
|
|||
|
my $class = shift;
|
|||
|
my $self = {};
|
|||
|
|
|||
|
if (ref($class)){
|
|||
|
$self->{FATHER} = $class;
|
|||
|
bless($self, ref($class));
|
|||
|
|
|||
|
}else{
|
|||
|
bless($self, $class);
|
|||
|
}
|
|||
|
|
|||
|
$self->setKey(@_);
|
|||
|
|
|||
|
return $self;
|
|||
|
}
|
|||
|
|
|||
|
=item setKey
|
|||
|
|
|||
|
Schluessel und zugehoerige Werte im Objekt speichern.
|
|||
|
|
|||
|
$plate->setKey( vote-addr => 'to-vote@dom.ain' );
|
|||
|
$plate->setKey( datenschutz => 1);
|
|||
|
|
|||
|
Ist der zu speichernde Schluessel bereits vorhanden, wird er
|
|||
|
durch den neuen Wert ueberschrieben.
|
|||
|
|
|||
|
=cut
|
|||
|
|
|||
|
sub setKey{
|
|||
|
my $self = shift;
|
|||
|
my %param = @_;
|
|||
|
|
|||
|
foreach my $key (keys(%param)){
|
|||
|
$self->{KEYS}->{$key} = $param{$key};
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
=item addListItem
|
|||
|
|
|||
|
Erzeugt ein neues Objekt vom Typ UVtemplate und fuegt es der
|
|||
|
angebenen Liste hinzu.
|
|||
|
|
|||
|
$plate->addListItem(name => 'Musterman', email => 'em@il');
|
|||
|
|
|||
|
Da sich Listen wie normale Schluessel-Wert Paare verhalten,
|
|||
|
wird die Liste als Array ueber UVtemplate-Objekte unter dem
|
|||
|
definiertem Schluessel abgelegt. Ist dieser Schluessel bereits
|
|||
|
gesetzt und enthaehlt keinen Array, so bricht die Funktion ab.
|
|||
|
|
|||
|
=cut
|
|||
|
|
|||
|
sub addListItem{
|
|||
|
my $self = shift;
|
|||
|
my $list = shift;
|
|||
|
|
|||
|
# pruefen ob key angegeben ist und falls key vorhanden
|
|||
|
# eine liste vorliegt
|
|||
|
return unless ($list && (not($self->{KEYS}->{$list}) ||
|
|||
|
UNIVERSAL::isa($self->{KEYS}->{$list}, 'ARRAY')));
|
|||
|
|
|||
|
# neues Element erzeugen
|
|||
|
my $new = $self->new( @_ );
|
|||
|
|
|||
|
# an listen anhaengen
|
|||
|
push(@{$self->{KEYS}->{$list}}, $new);
|
|||
|
|
|||
|
# referenz zurueckgeben
|
|||
|
return $new;
|
|||
|
}
|
|||
|
|
|||
|
=item getKey($key)
|
|||
|
|
|||
|
Den Wert eines Schluessel ermitteln.
|
|||
|
|
|||
|
my $value = $plate->getKey('email');
|
|||
|
|
|||
|
Ist der Wert im Objekt nicht gesetzt, wird - falls es sich um ein
|
|||
|
Element einer Liste handelt - rekursiv beim Vater weiter gesucht.
|
|||
|
|
|||
|
So stehen allen Kindern auch die Schluessel-Wert Paare ihrer Eltern
|
|||
|
zur Verfuegung.
|
|||
|
|
|||
|
Zum Schluss wird noch geprueft, ob der Schluessel in usevote.cfg
|
|||
|
gesetzt wurde. Dadurch sind alle Konfigurationsoptionen direkt
|
|||
|
in Templates nutzbar.
|
|||
|
|
|||
|
=cut
|
|||
|
|
|||
|
sub getKey{
|
|||
|
my $self = shift;
|
|||
|
my $key = $_[0];
|
|||
|
|
|||
|
my $value;
|
|||
|
|
|||
|
do{
|
|||
|
$value = $self->{KEYS}->{$key};
|
|||
|
$self = $self->{FATHER};
|
|||
|
|
|||
|
}while(!defined($value) && $self);
|
|||
|
|
|||
|
if (!defined($value) && defined($config{$key})) {
|
|||
|
$value = $config{$key};
|
|||
|
}
|
|||
|
|
|||
|
return $value;
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
sub getRules{
|
|||
|
my $self = shift;
|
|||
|
|
|||
|
do{
|
|||
|
return $self->{RULES} if ($self->{RULES});
|
|||
|
$self = $self->{FATHER};
|
|||
|
}while($self);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
=item getConvKey{
|
|||
|
|
|||
|
Einen Format ermitteln.
|
|||
|
|
|||
|
my $value = $plate->getConvKey('email-adresse');
|
|||
|
|
|||
|
Diese Funktion ueberprueft ob eine Formatierung mit den entsprechenden
|
|||
|
Schluessel definiert ist und ruft dementsprechend die dort definierten
|
|||
|
Funktionen ueber der Datenstruktur auf.
|
|||
|
|
|||
|
Ist kein solches Format definiert, wird der Wert des Schluessel mit
|
|||
|
einem solchen Namen zurueckgegeben. (Es wird intern getKey aufgerufen).
|
|||
|
|
|||
|
=cut
|
|||
|
|
|||
|
sub getConvKey{
|
|||
|
my $self = shift;
|
|||
|
my $key = $_[0] || return;
|
|||
|
|
|||
|
my $rules = $self->getRules();
|
|||
|
my $value = $self->getKey($key);
|
|||
|
|
|||
|
$value = '' unless (defined($value));
|
|||
|
|
|||
|
if ($rules && ($rules->{$key})){
|
|||
|
my @funcs = @{$rules->{$key}};
|
|||
|
|
|||
|
foreach my $func (@funcs){
|
|||
|
my ($name, @params) = @$func;
|
|||
|
|
|||
|
if ($functions->{$name}){
|
|||
|
$value = $functions->{$name}->($self, $value, @params);
|
|||
|
|
|||
|
}else{
|
|||
|
print STDERR "format function '$name' not found!\n";
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return $value;
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
=item processTemplate
|
|||
|
|
|||
|
Daten des Objekts in ein Template einfuegen.
|
|||
|
|
|||
|
my $string = $plate->processTemplate('template/info.txt');
|
|||
|
|
|||
|
Die angebene Datei wird eingelesen, zerlegt und danach
|
|||
|
die enstprechenden Platzhalter durch die (formatierten)
|
|||
|
Werte aus der Datenstruktur ersetzt.
|
|||
|
|
|||
|
=cut
|
|||
|
|
|||
|
sub processTemplate{
|
|||
|
my $self = shift;
|
|||
|
my $file = $_[0] || return;
|
|||
|
|
|||
|
my ($rules, $body) = _split_file($file);
|
|||
|
|
|||
|
# konvertierungsregeln parsen
|
|||
|
$self->{RULES} = _parse_rules($rules);
|
|||
|
|
|||
|
# template zerlegen (zuerst fuehrende leerzeilen entfernen!)
|
|||
|
$body =~ s/^\n+//s;
|
|||
|
my $token = UVtemplate::scan->new(string => $body);
|
|||
|
|
|||
|
# daten einsetzen
|
|||
|
return $token->processData($self);
|
|||
|
}
|
|||
|
|
|||
|
sub _split_file{
|
|||
|
my $file = $_[0] || return;
|
|||
|
|
|||
|
my $fname = _complete_filename($file);
|
|||
|
|
|||
|
unless ($fname){
|
|||
|
print STDERR "couldnt find '$file'\n";
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
my (@rules, @body);
|
|||
|
|
|||
|
open(PLATE, $fname);
|
|||
|
my @lines = <PLATE>;
|
|||
|
close(PLATE);
|
|||
|
|
|||
|
my $body = 0;
|
|||
|
|
|||
|
foreach my $line (@lines){
|
|||
|
if ($line =~ m/^== TEMPLATE/){
|
|||
|
$body = 1;
|
|||
|
|
|||
|
}else{
|
|||
|
if ($body){
|
|||
|
push(@body, $line);
|
|||
|
|
|||
|
}else{
|
|||
|
push(@rules, $line);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
# falls kein Separator definiert war, wird der komplette Text
|
|||
|
# als Body interpretiert. Es gibt keine Regeln!
|
|||
|
|
|||
|
unless ($body){
|
|||
|
@body = @rules;
|
|||
|
@rules = ();
|
|||
|
}
|
|||
|
|
|||
|
# und nun wieder zu Strings zusammenpappen
|
|||
|
return (join('', @rules), join('', @body));
|
|||
|
}
|
|||
|
|
|||
|
sub _complete_filename{
|
|||
|
my $file = $_[0] || return;
|
|||
|
|
|||
|
my $dirs = $UVconfig::config{templatedir};
|
|||
|
@dirs = split(/\s*,\s*/, $dirs) if $dirs;
|
|||
|
|
|||
|
my $fname;
|
|||
|
|
|||
|
foreach my $dir (@dirs, '.'){
|
|||
|
$fname = "$dir/$file";
|
|||
|
return $fname if (-r $fname);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
# Konvertierungs-Regeln
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
sub _parse_rules{
|
|||
|
my $string = $_[0] || return;
|
|||
|
|
|||
|
my @stack;
|
|||
|
my $rules = {};
|
|||
|
|
|||
|
my $this = [];
|
|||
|
|
|||
|
while (length($string) > 0){
|
|||
|
_strip_chars(\$string);
|
|||
|
|
|||
|
my $token = _parse_token(\$string);
|
|||
|
|
|||
|
if ($token){
|
|||
|
push(@stack, $token);
|
|||
|
|
|||
|
_strip_chars(\$string);
|
|||
|
|
|||
|
if ($string =~ s/^:=//){
|
|||
|
# neuen Schluessel vom Stack holen
|
|||
|
my $key = pop(@stack);
|
|||
|
|
|||
|
# restlichen Stack auf alten Schluessel packen
|
|||
|
push(@$this, [ @stack ]);
|
|||
|
@stack = ();
|
|||
|
|
|||
|
# neuen Schluessel anlegen
|
|||
|
$rules->{$key} = $this = [];
|
|||
|
|
|||
|
}elsif($string =~ s/^\|//){
|
|||
|
# stack auf schluessel packen
|
|||
|
push(@$this, [ @stack ]);
|
|||
|
@stack = ();
|
|||
|
}
|
|||
|
|
|||
|
}else{
|
|||
|
# fehlermeldung ausgeben (nacharbeiten!)
|
|||
|
print STDERR "Syntaxerror in Definition\n";
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
# den Rest vom Stack abarbeiten
|
|||
|
push(@$this, [ @stack ]) if @stack;
|
|||
|
|
|||
|
return $rules;
|
|||
|
}
|
|||
|
|
|||
|
sub _strip_chars{
|
|||
|
my $line = $_[0] || return;
|
|||
|
|
|||
|
# f<>hrenden whitespace entfernen
|
|||
|
$$line =~ s/^\s+//;
|
|||
|
|
|||
|
# kommentare bis zum n<>chsten Zeilenumbruch entfernen
|
|||
|
$$line =~ s/^#.*$//m;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
sub _parse_token{
|
|||
|
my $string = shift;
|
|||
|
|
|||
|
if ($$string =~ s/^(["'])//){
|
|||
|
return _parse_string($string, $1);
|
|||
|
|
|||
|
}else{
|
|||
|
return _parse_ident($string);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
sub _parse_string{
|
|||
|
my ($string, $limit) = @_;
|
|||
|
|
|||
|
my $value;
|
|||
|
|
|||
|
while ($$string){
|
|||
|
if ($$string =~ s/^$limit//){
|
|||
|
$$string =~ s/^\s*//;
|
|||
|
return $value;
|
|||
|
|
|||
|
}elsif($$string =~ s/^\\(.)//){
|
|||
|
$value .= $1;
|
|||
|
|
|||
|
}else{
|
|||
|
$$string =~ s/^[^$limit\\]*//;
|
|||
|
$value .= $&;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
# end of line
|
|||
|
return $value;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
sub _parse_ident{
|
|||
|
my $string = shift;
|
|||
|
|
|||
|
if ($$string =~ s/^([A-Za-z0-9-]+)\s*//){
|
|||
|
return $1;
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
BEGIN{
|
|||
|
$functions = \%UVconfig::functions;
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
#----------------------------------------------------------------------
|
|||
|
package UVtemplate::scan;
|
|||
|
#----------------------------------------------------------------------
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
sub new{
|
|||
|
my $class = shift;
|
|||
|
my %param = @_;
|
|||
|
|
|||
|
my $self = {};
|
|||
|
bless($self, $class);
|
|||
|
|
|||
|
$self->parseFile($param{file}) if defined($param{file});
|
|||
|
$self->parseString($param{string}) if defined($param{string});
|
|||
|
|
|||
|
return $self;
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
sub processData{
|
|||
|
my $self = shift;
|
|||
|
my $data = $_[0];
|
|||
|
|
|||
|
return _process_data($self->{toks}, $data);
|
|||
|
}
|
|||
|
|
|||
|
sub _process_data{
|
|||
|
my ($toref, $data) = @_;
|
|||
|
|
|||
|
my $string = '';
|
|||
|
my $length = 0;
|
|||
|
my $empty = 0;
|
|||
|
|
|||
|
foreach my $token (@$toref){
|
|||
|
if (ref($token)){
|
|||
|
my $before = length($string);
|
|||
|
$empty = 0;
|
|||
|
|
|||
|
if ($token->[0] eq 'VAR'){
|
|||
|
my $value = $data->getConvKey(_process_data($token->[1], $data));
|
|||
|
|
|||
|
if (defined($value) && length($value)){
|
|||
|
$string .= $value;
|
|||
|
|
|||
|
}else{
|
|||
|
$string .= _process_data($token->[2], $data);
|
|||
|
}
|
|||
|
|
|||
|
}elsif($token->[0] eq 'IF'){
|
|||
|
if ($data->getConvKey(_process_data($token->[1], $data))){
|
|||
|
$string .= _process_data($token->[2], $data);
|
|||
|
|
|||
|
}else{
|
|||
|
$string .= _process_data($token->[3], $data);
|
|||
|
}
|
|||
|
|
|||
|
}elsif($token->[0] eq 'LOOP'){
|
|||
|
my $nodes = $data->getConvKey(_process_data($token->[1], $data));
|
|||
|
my @block;
|
|||
|
|
|||
|
if ($nodes && (UNIVERSAL::isa($nodes, 'ARRAY'))){
|
|||
|
foreach my $node (@$nodes){
|
|||
|
push(@block, _process_data($token->[2], $node));
|
|||
|
}
|
|||
|
|
|||
|
$string .= join(_process_data($token->[3], $data), @block);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
$length = length($string);
|
|||
|
$empty = 1 if ($before == $length);
|
|||
|
|
|||
|
}else{
|
|||
|
if ($empty && ($string =~ m/(\n|^)$/s)){
|
|||
|
$empty = 0; # Falls die letzte Zeile nur aus einem Token
|
|||
|
$token =~ s/^\n//s; # ohne Inhalt bestand, wird die Zeile entfernt
|
|||
|
}
|
|||
|
|
|||
|
$string .= $token;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return $string;
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
# Den String in einen Syntaxbaum abbilden
|
|||
|
|
|||
|
sub _parse_token_string{
|
|||
|
my $self = shift;
|
|||
|
my ($string, $intern) = @_;
|
|||
|
|
|||
|
my (@token, $toref);
|
|||
|
my $data = '';
|
|||
|
|
|||
|
while ($string){
|
|||
|
if ($intern && $string =~ m/^(\]|\|)/){
|
|||
|
last;
|
|||
|
|
|||
|
}elsif ($string =~ s/^\[//){
|
|||
|
my $orig = $string;
|
|||
|
|
|||
|
($toref, $string) = $self->_parse_token($string);
|
|||
|
|
|||
|
if (@$toref){
|
|||
|
push (@token, $data) if $data;
|
|||
|
$data = '';
|
|||
|
|
|||
|
push(@token, $toref)
|
|||
|
}
|
|||
|
|
|||
|
if ($string !~ s/^\]//){
|
|||
|
my $pos = $self->{lines} - _count_lines($orig) + 1;
|
|||
|
|
|||
|
print STDERR "Scanner: [$pos] missing right bracket\n";
|
|||
|
return (\@token, $string);
|
|||
|
}
|
|||
|
|
|||
|
}elsif($string =~ s/^\\n//s){
|
|||
|
$data .= "\n";
|
|||
|
|
|||
|
}elsif($string =~ s/^\\(.)//s){
|
|||
|
$data .= $1;
|
|||
|
|
|||
|
}elsif($intern){
|
|||
|
$string =~ s/^([^\]\[\|\\]+)//s;
|
|||
|
$data .= $1;
|
|||
|
|
|||
|
}else{
|
|||
|
$string =~ s/^([^\[\\]+)//s;
|
|||
|
$data .= $1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
push (@token, $data) if length($data);
|
|||
|
return (\@token, $string)
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
sub _parse_token{
|
|||
|
my $self = shift;
|
|||
|
my $string = $_[0];
|
|||
|
|
|||
|
my @token = ();
|
|||
|
|
|||
|
if ($string =~ s/^\$//s){
|
|||
|
# Variablen - Syntax: [$key[|<else>]]
|
|||
|
push (@token, 'VAR');
|
|||
|
|
|||
|
}elsif ($string =~ s/^\?//s){
|
|||
|
# Bedingung - Syntax: [?if|<then>[|<else>]]
|
|||
|
push (@token, 'IF');
|
|||
|
|
|||
|
}elsif ($string =~ s/^\@//s){
|
|||
|
# Schleifen - Syntax: [@key|<block>[|<sep>]]
|
|||
|
push (@token, 'LOOP');
|
|||
|
|
|||
|
}elsif ($string =~ s/^#//s){
|
|||
|
# Kommentare - Syntax: [# ... ]
|
|||
|
$string = _parse_comment($string);
|
|||
|
|
|||
|
return (\@token, $string);
|
|||
|
|
|||
|
}else{
|
|||
|
print STDERR "unknown token in template\n";
|
|||
|
}
|
|||
|
|
|||
|
my $toref;
|
|||
|
|
|||
|
($toref, $string) = $self->_parse_token_string($string, 1);
|
|||
|
push(@token, $toref);
|
|||
|
|
|||
|
while ($string =~ s/^\|//){
|
|||
|
($toref, $string) = $self->_parse_token_string($string, 1);
|
|||
|
push(@token, $toref);
|
|||
|
}
|
|||
|
|
|||
|
return (\@token, $string);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
sub _parse_comment{
|
|||
|
my $string = $_[0];
|
|||
|
my $count = 1;
|
|||
|
|
|||
|
while($string && $count) {
|
|||
|
$string =~ s/^[^\[\]\\]+//s; # alles au<61>er Klammern und Backslash wegwerfen
|
|||
|
$string =~ s/^\\.//; # alles gesperrte l<>schen
|
|||
|
|
|||
|
$count++ if $string =~ s/^\[//;
|
|||
|
$count-- if $string =~ s/^\]//;
|
|||
|
}
|
|||
|
|
|||
|
$string = ']'.$string if !$count;
|
|||
|
return $string;
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
sub parseString{
|
|||
|
my $self = shift;
|
|||
|
my $text = $_[0];
|
|||
|
|
|||
|
$self->{lines} = _count_lines($text);
|
|||
|
my ($toref, $rest) = $self->_parse_token_string($text);
|
|||
|
|
|||
|
$self->{toks} = $toref;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
sub _count_lines{
|
|||
|
return 0 unless defined($_[0]);
|
|||
|
|
|||
|
my ($string, $count) = ($_[0], 1);
|
|||
|
$count++ while($string =~ m/\n/sg);
|
|||
|
|
|||
|
return $count;
|
|||
|
}
|
|||
|
|
|||
|
#----------------------------------------------------------------------
|
|||
|
#----------------------------------------------------------------------
|
|||
|
#----------------------------------------------------------------------
|
|||
|
|
|||
|
1;
|
|||
|
|
|||
|
=back
|
|||
|
|
|||
|
=head1 SYNTAX
|
|||
|
|
|||
|
Eine Templatedatei besteht aus zwei Teilen. Am Anfang werden die
|
|||
|
Formatierungen bestimmter Schluessel definiert und nach einem
|
|||
|
Trenner folgt der eigentlich Template-Koerper, der dann von Programm
|
|||
|
bearbeitet und ausgegeben wird.
|
|||
|
|
|||
|
format-key := function1 param | function2 param
|
|||
|
|
|||
|
== TEMPLATE ====================================
|
|||
|
|
|||
|
Ich bin nun das eigentliche Template:
|
|||
|
|
|||
|
format-key: [$format-key]
|
|||
|
|
|||
|
Der Trenner beginnt mit den Zeichen '== TEMPLATE' danach koennen
|
|||
|
beliebige Zeichen folgen um die beiden Sektionen optisch voneinander
|
|||
|
abzugrenzen.
|
|||
|
|
|||
|
Wenn es keine Formatierungsanweisungen gibt, kann der Trenner auch
|
|||
|
weggelassen werden. D.h. wenn kein Trenner gefunden wird, wird der
|
|||
|
komplette Text als Template-Koerper betrachtet.
|
|||
|
|
|||
|
=head2 Template-Koerper
|
|||
|
|
|||
|
Im Template-Koerper werden die zu ersetzenden Token durch eckige
|
|||
|
Klammern abgegrenzt. Sollen eckige Klammern im Text ausgegeben werden
|
|||
|
muessen diese durch einen Backslash freigestellt werden.
|
|||
|
|
|||
|
[$termersetzung] [@schleife] nur eine \[ Klammer
|
|||
|
|
|||
|
=over 3
|
|||
|
|
|||
|
=item $ - Termersetzung
|
|||
|
|
|||
|
Ersetzt den Token durch den Wert des angegeben Schluessels.
|
|||
|
|
|||
|
[$formatierung] [$schluessel]
|
|||
|
|
|||
|
Es wird zuerst nach einer Formatierung mit den entsprechenden
|
|||
|
Bezeichner gesucht. Ist dies der Fall werden die entsprechenden
|
|||
|
Funktionen ausgefuehrt.
|
|||
|
|
|||
|
Kann kein Format gefunden, wird direkt in der Datenstruktur
|
|||
|
nach einem Schhluessel mit dem angegeben Bezeichner gesucht
|
|||
|
und sein Wert eingesetzt.
|
|||
|
|
|||
|
Schlussendlich ist es noch moeglich einen default-Wert zu
|
|||
|
definieren, der eingesetzt wird, wenn keiner der obigen Wege
|
|||
|
erfolgreich war.
|
|||
|
|
|||
|
Hallo [$name|Unbekannter]!
|
|||
|
|
|||
|
=item ? - bedingte Verzeigung
|
|||
|
|
|||
|
Ueberprueft ob der Wert des angegebenen Formats/Schluessel
|
|||
|
boolsch WAHR ist. Dementsprechend wird der then oder else
|
|||
|
Block eingefuegt.
|
|||
|
|
|||
|
[?if|then|else] oder auch nur [?if|then]
|
|||
|
|
|||
|
Die then/else Bloecke werden natuerlich auch auf Tokens
|
|||
|
geparst und diese dementsprechend ersetzt.
|
|||
|
|
|||
|
=item @ - Schleifen/Listen
|
|||
|
|
|||
|
Der nachfolgende Textblock wird fuer alle Elemente des durch
|
|||
|
den Schluessel bezeichneten Arrays ausgefuehrt und eingefuegt.
|
|||
|
|
|||
|
[@schluessel|block] oder [@schluessel|block|sep]
|
|||
|
|
|||
|
Als zweiter Parameter kann ein Separtor definiert werden, mit
|
|||
|
dem sich z.B. kommaseparierte Listen erzeugen lassen, da der
|
|||
|
Separator eben nur zwischen den Element eingefuegt wird.
|
|||
|
|
|||
|
Auch fuer Schleifen koennen Formatierungen genutzt werden.
|
|||
|
Allerdings darf kein String zurueckgegeben werden, sondern
|
|||
|
ein Array mit einer Menge von UVtemplate-Objekten.
|
|||
|
|
|||
|
=item # - Kommentare
|
|||
|
|
|||
|
Token die nach der Bearbeitungen entfernt werden.
|
|||
|
|
|||
|
[# mich sieht man nicht]
|
|||
|
|
|||
|
=item Sonstiges
|
|||
|
|
|||
|
Um in Listen einen Zeilenumbruch zu erzwingen, muss
|
|||
|
lediglich ein '\n' eingefuegt werden, falls eine kompakte
|
|||
|
Definition der Liste erfolgen soll.
|
|||
|
|
|||
|
[@names|[name] [email]\n]
|
|||
|
|
|||
|
=back
|
|||
|
|
|||
|
=head2 Formatierungen
|
|||
|
|
|||
|
Eine Formatierung besteht eigentlich nur aus dem entsprechenden
|
|||
|
Namen und einer beliebigen Anzahl von Funktionsaufrufen:
|
|||
|
|
|||
|
format := funktion param1 "param 2" | funktion param
|
|||
|
|
|||
|
Aehnlich der Unix-Shell-Funktionalitaet, wird dabei die Ausgabe
|
|||
|
einer Funktion an die folgende weitergeleitet. So ist es moeglich
|
|||
|
verschiedenste simple Formatierungen zu kombinieren um nicht fuer
|
|||
|
jeden Spezialfall eine neue Funktion schreiben zu muessen.
|
|||
|
|
|||
|
Die jeweilige Formatierungsfunktion erhaelt als Input die Datenstruktur,
|
|||
|
den Output der vorherigen Funktion und die definierten Parameter in der
|
|||
|
entsprechenden Reihenfolge.
|
|||
|
|
|||
|
Zahlen und einfache Bezeichner koennen direkt definiert werden. Sollen
|
|||
|
Sonderzeichen oder Leerzeichen uebergeben werden muessen diese gequotet
|
|||
|
werden. Dazu kann ' also auch " verwendet werden.
|
|||
|
|
|||
|
Die Funktionen geben im Allgemeinen einen String zurueck. Im Rahmen
|
|||
|
von Listen k<EFBFBD>nnen auch Arrays uebergeben werden.
|
|||
|
|
|||
|
Die erste Funktion duerfte ueblicherweise 'value' sein. Sie gibt den
|
|||
|
des angegeben Schluessel zurueck, der dann von den folgenden Funktionen
|
|||
|
definiert wird.
|
|||
|
|
|||
|
name-60 := value name | fill-right 60
|
|||
|
|
|||
|
Das Format "name-60" definiert also den Wert des Schluessel "name" der
|
|||
|
um Leerzeichen aufgefuellt wird, bis eine Laenge von 60 Zeichen
|
|||
|
erreicht wird.
|
|||
|
|
|||
|
name-email := value name | justify-behind mail 72
|
|||
|
|
|||
|
"name-email" resultiert in einem String, der zwischen den Werten
|
|||
|
von "name" und "email" genau so viele Leerzeichen enthaelt, damit
|
|||
|
der gesamte String 72 Zeichen lang ist.
|
|||
|
|
|||
|
Wird dieses Format in einer Liste angewandt, erhaelt man eine Tabelle
|
|||
|
in der die linke Spalte linksbuendig und die rechte Spalte entsprechend
|
|||
|
rechtsbuendig ist.
|
|||
|
|
|||
|
Soweit ein kleiner Ueberblick ueber die Formatierungen.
|
|||
|
Ausfuehrliche Funktionsbeschreibungen und weitere Beispiele finden
|
|||
|
sich in der Dokumentation des Moduls UVformat.
|
|||
|
|
|||
|
=head1 SEE ALSO
|
|||
|
|
|||
|
L<UVformats>
|
|||
|
|
|||
|
=head1 AUTHOR
|
|||
|
|
|||
|
Cornell Binder <cobi@dex.de>
|