From 41ad9741a0933eeb38b4efb5a32aeb4d0afebdce Mon Sep 17 00:00:00 2001 From: Michael Wiebusch Date: Fri, 4 Jul 2014 11:22:19 +0200 Subject: [PATCH] added xml-db2tex.pl - a tool to create latex tables from xml-db register definitions --- xml-db/xml-db2tex.pl | 384 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100755 xml-db/xml-db2tex.pl diff --git a/xml-db/xml-db2tex.pl b/xml-db/xml-db2tex.pl new file mode 100755 index 0000000..1e95b3b --- /dev/null +++ b/xml-db/xml-db2tex.pl @@ -0,0 +1,384 @@ +#!/usr/bin/perl -w + + +package this; + +use strict; +use warnings; +use POSIX; +use Data::Dumper; +use FileHandle; +use Getopt::Long; +use File::Basename; +use File::Copy; +# use Cwd; + +my $opt; + +Getopt::Long::Configure(qw(gnu_getopt)); +GetOptions( + 'help|h' => \$opt->{help}, + 'caption|c=s' => \$opt->{caption}, + 'label|l=s' => \$opt->{label}, + 'group|g=s' => \$opt->{group}, + 'entity|e=s' => \$opt->{entity}, + 'output|o=s' => \$opt->{output}, + 'pdf' => \$opt->{pdf}, + 'standalone' => \$opt->{standalone} + ); + +printHelpMessage() if $opt->{help}; +printHelpMessage() unless $opt->{entity} && $opt->{group}; + + +my $me = this->new(); + +$me->setEntity($opt->{entity}); +# $me->{entityFile} = "/home/micha/mnt/55local1/htdocs/daqtools/xml-db/cache/CbController.entity"; +$me->{group} = $opt->{group}; +$me->{table}->{label} = $opt->{label}||"tab:".$opt->{group}; +$me->{table}->{caption} = $opt->{caption}||"Registers in group ".$opt->{group}; +$me->produceTable(); + + +$me->writeTexFile($opt->{output}, $opt->{standalone} ); + +if ($opt->{pdf}){ + if ($opt->{output}){ + $me->pdflatex($opt->{output}); + } else { + die "\n\ncannot make pdf!\nno output file specified, use the -o argument!\n"; + } +} + + + +########### simple subs ######## + +sub printHelpMessage{ +print < -g [-o ] [OPTIONS] + +Generates a latex table of an xml-db group. + +Options: + -h, --help brief help/usage message + -e, --entity enter entity name or /path/to/entityName.entity + -g, --group the xml-db group to be transformed into a table + -o, --output write tex output to this file, if left out, + will write tex code to STDOUT + + -c, --caption caption of the table + -l, --label latex label of the table + + --standalone generate standalone compilable latex file + --pdf compile directly to pdf + + +EOF +exit; +} + + +########### object methods ######## + +sub new { + my $class = shift; + my %options = @_; + + my $self = {}; + + $self->{table} = textabular->new(); # create new latex table object + + # default formatting of the table + $self->{table}->{dataKeys} = [ 'name', 'addr', 'bits', 'description' ]; + $self->{table}->{header} = [ 'register', 'addr', 'bits', 'description' ]; + $self->{table}->{format} = '@{} l l l p{8cm} @{}'; + + $self = { + %$self, + %options + }; + bless($self, $class); + return $self; +} + +sub setEntity { + my $self=shift; + my $entity=shift; + + if(-e $entity) { # treat as /path/to/File + $self->{entityFile} = $entity; + } elsif (-e dirname($0)."/cache/".$entity.".entity"){ + $self->{entityFile} = dirname($0)."/cache/".$entity.".entity"; + } else { + die "Entity $entity not found (not even in xml-db/cache)\n"; + } +} + +sub produceTable { + my $self= shift; + my $xmldb = xmlDbMethods->new( entityFile => $self->{entityFile} ); + my $list = $xmldb->unfoldTree($self->{group}); + my $data = []; + for my $name (@$list) { # processing the list + my $node = $xmldb->{entity}->{$name}; + my $type = $node->{type}; + my $repeat = $node->{repeat} || 1; + my $stepsize = $node->{stepsize}||0; + my $bits = ""; + if ($type ne 'register'){ + my $start = $node->{start}; + my $stop = $node->{start}+$node->{bits}-1; + if ($start == $stop){ + $bits = $start; + } else { + $bits = "$start--$stop"; + } + } + #indent register fields + if ($type eq 'field'){ + $name= '\quad '.$name; + } + for (my $i=0;$i<$repeat;$i++){ + my $name_ = $name; + if ($repeat > 1) { + $name_ = $name.".$i"; + } + my $addr_ = $node->{address}+$i*$stepsize; + my $hexaddr = sprintf("0x%04x",$addr_ ); + + if ($type eq 'register' || $type eq 'registerfield'){ + #write register names bold + $name_ = '\textbf{'.$name_.'}'; + } + if ($type eq 'field'){ + $hexaddr = ''; # don't print addr it's already in the register + } + + push(@{$data},{%$node, name => $name_, addr => $hexaddr, bits => $bits, addr_uint => $addr_}); + } + } + + @$data = sort { $a->{bits} cmp $b->{bits} } @$data; # bit fields in ascending order + @$data = sort { $a->{addr_uint} cmp $b->{addr_uint} } @$data; # addresses in ascending order + + + my $last_addr; + for my $item (@$data){ + # make hline at each new register + my $cur_addr = $item->{addr_uint}; + if($last_addr){ + if($last_addr != $cur_addr){ + $self->{table}->addData(plain_code => '\hline'); + } + } + $self->{table}->addData(%$item); # fill it with the sorted data + $last_addr = $cur_addr; + } + +} + +sub writeTexFile { + my $self = shift; + my $output = shift; + my $standalone = shift; + + + # unless specified by --output/-o, the print OUTPUT commands go to STDOUT + if ($output) { + open(OUTPUT, '>', $output) or die "could not open $output for writing!"; + } else { + *OUTPUT = *STDOUT; + } + + if ($standalone){ + print OUTPUT q% + \documentclass[a4paper,11pt]{article} + \usepackage[T1]{fontenc} + \usepackage{lmodern} + \usepackage{booktabs} + \usepackage{longtable} + \usepackage{geometry} + \geometry{verbose,tmargin=3cm,bmargin=3cm,lmargin=3cm,rmargin=3cm} + \begin{document} + %; + } + print OUTPUT $self->{table}->generateString(); + + print OUTPUT '\end{document}'."\n" if $standalone; +# OUTPUT->close(); + close(OUTPUT); +} + +sub pdflatex{ + my $self = shift; + my $output = shift||$me->{group}; + my $here = qx("pwd"); + $here =~ s/\n//g; + my $directory = "/dev/shm/xml-db2tex".rand(); + unless(-e $directory or mkdir $directory) { + die "Unable to create $directory\n"; + } + my $texfile = $me->{group}.".tex"; + my $pdffile = $me->{group}.".pdf"; + + $output =~ s/\.(tex|pdf)//; + $output.= ".pdf"; + + chdir $directory; + $me->writeTexFile($texfile, "standalone"); + system("pdflatex $texfile"); + system("pdflatex $texfile"); + copy("$directory/$pdffile","$here/$output"); + chdir $here; + system("rm -rf $directory"); + +} + + + +package xmlDbMethods; + +use Storable qw(lock_store lock_retrieve); +use Data::Dumper; + +sub new { + my $class = shift; + my %options = @_; + my $self = { + entityFile => '/dev/null', + %options + }; + bless($self, $class); + $self->{entity} = lock_retrieve($self->{entityFile}); + die "cannot open xml-db entity file ".$self->{entityFile}."\n" unless defined $self->{entity}; + return $self; +} + + + +sub dumpItem { # for debug + my $self = shift; + my $item = shift; + unless (defined($item)){ + print Dumper $self->{entity}; + } else { + print Dumper $self->{entity}->{$item}; + } +} + + +sub unfoldTree { + my $self = shift; + my $name = shift; + my $depth = shift||0; + my $list = shift || []; + + my $node = $self->{entity}->{$name}; + unless($node->{type} eq 'group'){ + push(@{$list},$name); + } + + if ($node->{type} eq 'group' || $node->{type} eq 'register' ){ + for my $child (@{$node->{'children'}}){ +# print $child."\n"; + $self->unfoldTree($child,$depth+1,$list); + } + } + + return $list; +} + +1; + +package textabular; + + +sub new { + my $class = shift; + my %options = @_; + my $self = { + dataKeys => [], + header => [], + data => [], + caption => '', + label => '', + %options + }; + bless($self, $class); + return $self; +} + +sub addData { + my $self = shift; + my %data = @_; + push(@{$self->{data}}, \%data); + return $self; +} + +sub generateString { + my $self = shift; + my $str = '% remember to include the following latex packages: + % booktabs + % longtable'."\n"; + + my $header; + + if ( @{$self->{header}} ){ # if no header list ... + $header= " ".join(" & ",map { '\textbf{'.$_.'}' } @{$self->{header}}).' \\\\'."\n"; + } else { # print the keys instead + $header= " ".join(" & ",map { '\textbf{'.$_.'}' } @{$self->{dataKeys}}).' \\\\'."\n"; + } + + $str .= '\begin{longtable}'."\n"; + $str .="{".($self->{format}||"")."}\n"; + + + # define first header + $str.='\toprule'."\n"; + $str.= $header; + $str.='\midrule'."\n"; + $str.='\midrule'."\n"; + $str.='\endfirsthead'; + + # define (continued) header + $str.='\multicolumn{'.@{$self->{dataKeys}}.'}{c}'; + $str.='{\tablename\ \thetable\ -- \textit{Continued from previous page}} \\\\'; + $str.='\toprule'."\n"; + $str.= $header; + $str.='\midrule'."\n"; + $str.='\midrule'."\n"; + $str.='\endhead'; + + $str.='\multicolumn{'.scalar(@{$self->{dataKeys}}).'}{r}{\textit{Continued on next page}} \\\\ + \endfoot + \endlastfoot'; + + for my $data (@{$self->{data}}){ + + if ( $data->{plain_code} ) { #if there are strings in the data, just print them + $str.=$data->{plain_code}."\n"; + } else { + + my @line; + for my $dataKey (@{$self->{dataKeys}}){ + push(@line,$data->{$dataKey}); + } + my $line = " ".join(" & ", @line) . ' \\\\'."\n"; + $line =~ s/_/\\_/g; # remove all stupid underscores + $str.=$line; + + } + } + $str.='\bottomrule'."\n"; +# $str.='\hline'."\n"; + + $str.='\caption{'.$self->{caption}.'}' if $self->{caption}."\n"; + $str.='\label{'.$self->{label}.'}' if $self->{label}."\n"; + + $str.='\end{longtable}'."\n"; + +# $str.='\end{table}'."\n"; + return $str; +} \ No newline at end of file -- 2.43.0