#!/usr/bin/perl -w # File : csvtools.pl # Author : Dr Nicola Talbot # Date : 26 June 2007 # Description : Perl script to accompany csvtools.sty # : Allows you to substitute \CSVtotabular, \CSVtolongtable and \applyCSVfile commands with the appropriate LaTeX code # Version : 0.6b (1 September 2006). # usage : csvtools if ($#ARGV != 1) { die "Syntax : $0 \n"; } ($FILENAME,$OUTPUT) = @ARGV; open FILENAME or die "Can't open '$FILENAME'\n"; $ext = substr($FILENAME, -4); if (($ext eq ".tex") or ($ext eq ".dtx") or ($ext eq ".ltx")) { $LOGFILE = substr($FILENAME,0,length($FILENAME)-4) . ".log"; } else { $LOGFILE = $FILENAME . ".log"; } open LOGFILE or die "Can't open log file '$LOGFILE'. Make sure you run LaTeX before using $0\n"; while () { if (/\\c@([a-zA-Z]+)=\\count/) { $counter{$1} = 0; } } $counter{'csvrownumber'} = 0; close LOGFILE; open OUTPUT, ">$OUTPUT" or die; # global csvsort options $globalsort{'verbose'}=1; $globalsort{'sort'}='alphabetical ascending'; $globalsort{'variable'}='\field{1}'; $globalsort{'sfirstdataline'}=1; $globalsort{'firstdataline'}=2; $keyvaldefaults{'verbose'}='true'; $csvpieouterlabel="\\field{1}"; $csvpieinnerlabel="\\field{2}\\%"; $separator=","; while () { $restofline = $_; local($comment) = ""; if ($restofline=~m/(\\*)%/) { $n = length($1); if ($n%2 == 0) { $restofline = "$`$1"; $comment = "%$'"; $_ = $restofline; } } if ($restofline=~/\\setcsvseparator\s*{([^}]*)}/) { $separator=$1 if defined($1); } if ($restofline=~/\\renewcommand\*?\s*{?\\csvpieinnerlabel}?/) { print OUTPUT $`; $restofline=$'; $lineno=$.; ($csvpieinnerlabel,$restofline,$done)=&getnextgroup($restofline); while (!$done) { if ($nextline = ) { $restofline .= $nextline; ($csvpieinnerlabel,$restofline,$done)=&getnextgroup($restofline); } else { die "Unexpected EOF while parsing \\renewcommand on line $lineno\n"; } } $_ = $restofline; } if ($restofline=~/\\renewcommand\*?\s*{?\\csvpieouterlabel}?/) { print OUTPUT $`; $restofline=$'; $lineno=$.; ($csvpieouterlabel,$restofline,$done)=&getnextgroup($restofline); while (!$done) { if ($nextline = ) { $restofline .= $nextline; ($csvpieouterlabel,$restofline,$done)=&getnextgroup($restofline); } else { die "Unexpected EOF while parsing \\renewcommand on line $lineno\n"; } } $_ = $restofline; } while ($restofline=~/\\stepcounter(.*)/) { $line = $_; ($ctr,$restofline,$done) = &getnextgroup($1); $startline=$.; while (!$done) { if ($nextline = ) { $line = $line . $nextline; $restofline = $restofline . $nextline; ($ctr,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning first argument to \\stepcounter on line $startline\n"; } } $counter{$ctr}++; $_ = $line; } $restofline = $_; while ($restofline=~/\\refstepcounter(.*)/) { $line = $_; ($ctr,$restofline,$done) = &getnextgroup($1); $startline=$.; while (!$done) { if ($nextline = ) { $line = $line . $nextline; $restofline = $restofline . $nextline; ($ctr,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning first argument to \\stepcounter on line $startline\n"; } } $counter{$ctr}++; $_ = $line; } $restofline = $_; while ($restofline=~/\\setcounter(.*)/) { $line = $_; ($ctr,$restofline,$done) = &getnextgroup($1); $startline=$.; while (!$done) { if ($nextline = ) { $line = $line . $nextline; $restofline = $restofline . $nextline; ($ctr,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning first argument to \\stepcounter on line $startline\n"; } } ($num,$restofline,$done) = &getnextgroup($restofline); while (!$done) { if ($nextline = ) { $line = $line . $nextline; $restofline = $restofline . $nextline; ($num,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning second argument to \\stepcounter on line $startline\n"; } } $num=~s/\\value{(.+)}/$counter{$1}/; $counter{$ctr} = $num; $_ = $line; } $restofline = $_; while ($restofline=~/\\addtocounter(.*)/) { $line = $_; ($ctr,$restofline,$done) = &getnextgroup($1); $startline=$.; while (!$done) { if ($nextline = ) { $line = $line . $nextline; $restofline = $restofline . $nextline; ($ctr,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning first argument to \\stepcounter on line $startline\n"; } } ($num,$restofline,$done) = &getnextgroup($restofline); while (!$done) { if ($nextline = ) { $line = $line . $nextline; $restofline = $restofline . $nextline; ($num,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning second argument to \\stepcounter on line $startline\n"; } } $num=~s/\\value{(.+)}/$counter{$1}/; $counter{$ctr} = $counter{$ctr} + $num; $_ = $line; } $restofline = $_; while ($restofline=~/^(.*)\\csvGetEntry(.*)$/) { $start = $1; $restofline = $2; ($ctr,$restofline,$done) = &getnextgroup($restofline); $startline=$.; while (!$done) { if ($nextline = ) { $restofline = $restofline . $nextline; ($ctr,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning first argument to \\csvGetEntry on line $startline\n"; } } ($entry,$restofline,$done) = &getnextgroup($restofline); while (!$done) { if ($nextline = ) { $restofline = $restofline . $nextline; ($entry,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning first argument to \\csvGetEntry on line $startline\n"; } } $_ = $start . "\\csname $entry\\roman{$ctr}\\endcsname" . $restofline; #$_ = $start. $data{$entry}[$counter{$ctr}] . $restofline; } $restofline = $_; while ($restofline=~/\\usepackage(.*)/) { $line = $_; $startline=$.; $restofline = $1; $options = ''; if ($restofline=~/^\s*\[/) { ($options,$restofline,$done) = &getnextoptionalgroup($restofline); while (!$done) { if ($nextline = ) { $line = $line . $nextline; $restofline = $restofline . $nextline; ($options,$restofline,$done) = &getnextoptionalgroup($restofline); } else { die "EOF found whilst scanning optional argument to \\usepackage on line $startline\n"; } } } ($package,$restofline,$done) = &getnextgroup($restofline); while (!$done) { if ($nextline = ) { $line = $line . $nextline; $restofline = $restofline . $nextline; ($package,$restofline,$done) = &getnextgroup($restofline); } else { die "EOF found whilst scanning argument to \\usepackage on line $startline\n"; } } if ($package eq 'csvsort' and $options) { %hash = &getKeyValues($options,%keyvaldefaults); foreach $key (keys(%hash)) { $globalsort{$key}=$hash{$key}; } } $_ = $line; } $restofline = $_; if (/^(.*)\\CSVtotabular(.*)$/) { print OUTPUT "$1$comment"; $comment=""; $line = $2; $lineno = $.; ($csvname,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $line . $nextline; ($csvname,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for CVS filename on line $lineno\n"; } } $csvname=~s/\\csname (.*)\\roman{(.*)}\\endcsname/$data{$1}[$counter{$2}]/; $line = $restofline; ($alignment,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $restofline . $nextline; ($alignment,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for CVS column alignment on line $lineno\n"; } } $line = $restofline; ($FIRST,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $restofline . $nextline; ($FIRST,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\CSVtotabular third argument on line $lineno\n"; } } $line = $restofline; ($MIDDLE,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $restofline . $nextline; ($MIDDLE,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\CSVtotabular fourth argument on line $lineno\n"; } } $line = $restofline; ($LAST,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $restofline . $nextline; ($LAST,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\CSVtotabular fifth argument on line $lineno\n"; } } &csvtotabular($csvname, $alignment, $FIRST, $MIDDLE, $LAST, "tabular"); print OUTPUT "$restofline\n"; } elsif (/^(.*)\\CSVtolongtable(.*)$/) { print OUTPUT "$1$comment"; $comment=""; $line = $2; $lineno = $.; ($csvname,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $line . $nextline; ($csvname,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for CVS filename on line $lineno\n"; } } $csvname=~s/\\csname (.*)\\roman{(.*)}\\endcsname/$data{$1}[$counter{$2}]/; $line = $restofline; ($alignment,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $restofline . $nextline; ($alignment,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for CVS column alignment on line $.\n"; } } $line = $restofline; ($FIRST,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $restofline . $nextline; ($FIRST,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\CSVtolongtable third argument on line $.\n"; } } $line = $restofline; ($MIDDLE,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $restofline . $nextline; ($MIDDLE,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\CSVtolongtable fourth argument on line $.\n"; } } $line = $restofline; ($LAST,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline = ) { $line = $restofline . $nextline; ($LAST,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\CSVtolongtable fifth argument on line $.\n"; } } &csvtotabular($csvname, $alignment, $FIRST, $MIDDLE, $LAST, "longtable"); print OUTPUT "$restofline\n"; } elsif (/^(.*)\\applyCSVfile(\*)?(.*)$/) { print OUTPUT "$1$comment"; $comment=""; $starred = defined($2); $restofline = $3; $lineno = $.; $restofline = &eatinitialspaces($restofline); $restofline = &eatcomments($restofline); $startrow = $starred ? 1 : 2; if (/^\[([0-9]+)\](.*)/) { $startrow = $1; $restofline = $2; } $line=$restofline; ($csvname,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($csvname,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\applyCSVfile*[$startrow] first argument on line $lineno\n"; } } $csvname=~s/\\csname (.*)\\roman{(.*)}\\endcsname/$data{$1}[$counter{$2}]/; $line=$restofline; ($body,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($body,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\applyCSVfile*[$startrow] second argument on line $lineno\n"; } } if ($starred) { &applyCSVfilestar($csvname, $body, $startrow); } else { &applyCSVfile($csvname, $body, $startrow); } print OUTPUT "$restofline\n"; } elsif (/^(.*)\\sortapplyCSVfile(\*)?(.*)$/) { print OUTPUT "$1$comment"; $comment=""; $starred = defined($2); $restofline = $3; $lineno = $.; $restofline = &eatinitialspaces($restofline); $restofline = &eatcomments($restofline); $line=$restofline; $options = ''; if ($restofline=~/^\s*\[/) { ($options,$restofline,$done) = &getnextoptionalgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($options,$restofline,$done) = &getnextoptionalgroup($line); } else { die "EOF found whilst scanning for \\sortapplyCSVfile optional argument on line $lineno\n"; } } } $line=$restofline; ($csvname,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($csvname,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\sortapplyCSVfile argument on line $lineno\n"; } } $csvname=~s/\\csname (.*)\\roman{(.*)}\\endcsname/$data{$1}[$counter{$2}]/; $line=$restofline; ($body,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($body,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\sortapplyCSVfile argument on line $lineno\n"; } } if ($starred) { &sortapplyCSVfilestar($csvname, $body, $options); } else { &sortapplyCSVfile($csvname, $body, $options); } print OUTPUT "$restofline\n"; } elsif (/^(.*)\\sortCSVto(tabular|longtable)(.*)$/) { print OUTPUT "$1$comment"; $comment=""; $envname = $2; $restofline = $3; $lineno = $.; $restofline = &eatinitialspaces($restofline); $restofline = &eatcomments($restofline); $line=$restofline; $options = ''; if ($restofline=~/^\s*\[/) { ($options,$restofline,$done) = &getnextoptionalgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($options,$restofline,$done) = &getnextoptionalgroup($line); } else { die "EOF found whilst scanning for \\sortCSVto$envname first argument on line $lineno\n"; } } } $line=$restofline; ($csvname,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($csvname,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\sortCSVto$envname argument on line $lineno\n"; } } $csvname=~s/\\csname (.*)\\roman{(.*)}\\endcsname/$data{$1}[$counter{$2}]/; $line=$restofline; ($align,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($align,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\sortCSVto$envname argument on line $lineno\n"; } } $line=$restofline; ($headrow,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($headrow,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\sortCSVto$envname argument on line $lineno\n"; } } $line=$restofline; ($midrows,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($midrows,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\sortCSVto$envname argument on line $lineno\n"; } } $line=$restofline; ($lastrow,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($lastrow,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\sortCSVto$envname argument on line $lineno\n"; } } &sortCSVtotabular($csvname,$align,$headrow,$midrows,$lastrow, $envname,$options); print OUTPUT "$restofline\n"; } elsif (/^(.*)\\csvpiechart(\*)?(.*)$/) { print OUTPUT "$1$comment"; $comment=""; $lineno = $.; $starred = defined($2); $restofline = $3; $restofline = &eatinitialspaces($restofline); $restofline = &eatcomments($restofline); $startrow= $starred ? 1 : 2; $line=$restofline; ($options,$restofline,$done) = &getnextoptionalgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($options,$restofline,$done) = &getnextoptionalgroup($line); } else { die "EOF found whilst scanning for \\csvpiechart optional argument on line $lineno\n"; } } $line=$restofline; ($label,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($label,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\csvpiechart second argument on line $lineno\n"; } } $line=$restofline; ($csvname,$restofline,$done) = &getnextgroup($line); while (!$done) { if ($nextline= ) { $line = $restofline . $nextline; ($csvname,$restofline,$done) = &getnextgroup($line); } else { die "EOF found whilst scanning for \\csvpiechart second argument on line $lineno\n"; } } print OUTPUT "\\csvpiesetkeys{$options}%\n"; if ($options=~m/firstrow\s*=\s*(\d+)/) { $startrow=$1; } if ($starred) { &csvpiechartstar($label, $csvname, $startrow); } else { &csvpiechart($label, $csvname, $startrow); } print OUTPUT "$restofline\n"; } else { print OUTPUT "$_$comment"; } } sub doapplyCSV{ local($csvrow, $body, @idx, %HoH) = @_; local($row, $entry); $counter{'csvrownumber'} = 0; print OUTPUT "\\setcounter{csvrownumber}{0}\\relax\n"; foreach ($row=1; $row <= $csvrow; $row++) { print "."; local($THISROW) = $body; $THISROW = &csvSaveEntry($THISROW,$row,%HoH); foreach $entry ( keys %{$HoH[$row]}) { $replacementval=$HoH[$row]{$entry}; $THISROW =~ s/\\insertbyname{$entry}/$replacementval/g; $THISROW =~ s/\\insert$entry/$replacementval/g; local($column) = $idx{$entry}+1; $THISROW =~ s/\\field{(\d+)}/$1eq$column?$replacementval:$&/emg; $THISROW = &ifnextrowlast($THISROW, ($row==$csvrow-1)); } print OUTPUT "\\refstepcounter{csvrownumber}\\relax\n$THISROW"; $counter{'csvrownumber'}++; } } sub doapplyCSVstar{ local($body, $csvrow, $numcols, @entry) = @_; local($row,$column); print OUTPUT "\\setcounter{csvrownumber}{0}\\relax\n"; $counter{'csvrownumber'} = 0; for ($row=0; $row < $csvrow; $row++) { print "."; $THISROW = $body; for ($column=0; $column < $numcols; $column++) { $replacementval=$entry[$row][$column]; $THISROW =~ s/\\field{(\d+)}/$1eq$column+1?$replacementval:$&/emg; $THISROW = &ifnextrowlast($THISROW, ($row==$csvrow-1)); } print OUTPUT "\\refstepcounter{csvrownumber}$THISROW"; $counter{'csvrownumber'}++; } } sub getCSVvalues{ local($CSVFILE, $startrow) = @_; local(@idx, %HoH, @fields); open FH, $CSVFILE or die "Can't open file '$CSVFILE'\n"; local ($csvrow)=0; while () { if ($. == 1) { # get header row chop; @fields = &getEntries($_); local($numcols) = 0; foreach $field (@fields) { $idx{$field} = $numcols; $numcols++; } } elsif ($. >= $startrow) { next unless /$separator/; chop; $csvrow++; $rec = {}; $HoH[$csvrow] = $rec; local(@entries) = &getEntries($_); local ($i) = 0; foreach $e (@entries) { $rec->{$fields[$i]} = $e; $i++; } } } close FH; return ($csvrow, @idx, %HoH); } sub getCSVvaluesstar { local($CSVFILE, $startrow) = @_; open FH, $CSVFILE or die "Can't open file '$CSVFILE'\n"; $csvrow=0; while () { if ($. >= $startrow) { chop; @fields = &getEntries($_); $numcols = 0; foreach $field (@fields) { $entry[$csvrow][$numcols] = $field; $numcols++; } $csvrow++; } } close FH; return ($csvrow, $numcols, @entry); } sub csvpiechart { local($value, $CSVFILE, $startrow) = @_; print "Converting \\csvpiechart[$options]{$label}{$csvname}\n"; print OUTPUT "% \\csvpiechart[$options]{$label}{$csvname} converted using csvtools.pl\n"; print OUTPUT "%>> START INSERT\n"; # read in data from CSV file local($csvrow, @idx, %HoH) = &getCSVvalues($CSVFILE,$startrow); # first compute parameters print OUTPUT "\\edef\\csvstartang{\\thecsvstartangle}\\relax\n"; $body = "\\csvsetsegmentparams{\\thecsvrownumber}{$value}\\relax\n"; &doapplyCSV($csvrow, $body, @idx, %HoH); print OUTPUT "\\setcounter{csvstartangle}{\\csvstartang}\\relax\n"; # compute offsets for cutaway segments print OUTPUT "\\ifthenelse{\\equal{\\csvpiecutaways}{}}{}{\\csvcomputeoffsets}\\relax\n"; # now do the pie chart print OUTPUT "\\begin{tikzpicture}\n"; $body = "\\renewcommand*{\\csvpieinnerlabel}{$csvpieinnerlabel}\n"; $body .= "\\renewcommand*{\\csvpieouterlabel}{$csvpieouterlabel}\n"; $body .= "\\csvpiesegment{$value}\n"; &doapplyCSV($csvrow, $body, %HoH); print OUTPUT "\\end{tikzpicture}"; print OUTPUT "%<< END INSERT\n"; print "\n"; } sub csvpiechartstar { local($value, $CSVFILE, $startrow) = @_; print "Converting \\csvpiechart*[$options]{$label}{$csvname}\n"; print OUTPUT "% \\csvpiechart*[$options]{$label}{$csvname} converted using csvtools.pl\n"; print OUTPUT "%>> START INSERT\n"; local($csvrow, $numcols, @entry) = &getCSVvaluesstar($CSVFILE, $startrow); # first compute parameters print OUTPUT "\\edef\\csvstartang{\\thecsvstartangle}\\relax\n"; $body = "\\csvsetsegmentparams{\\thecsvrownumber}{$value}\\relax\n"; &doapplyCSVstar($body, $csvrow, $numcols, @entry); print OUTPUT "\\setcounter{csvstartangle}{\\csvstartang}\\relax\n"; # compute offsets for cutaway segments print OUTPUT "\\ifthenelse{\\equal{\\csvpiecutaways}{}}{}{\\csvcomputeoffsets}\\relax\n"; $body = "\\renewcommand*{\\csvpieinnerlabel}{$csvpieinnerlabel}\\relax\n"; $body .= "\\renewcommand*{\\csvpieouterlabel}{$csvpieouterlabel}\\relax\n"; $body .= "\\csvpiesegment{$value}\\relax\n"; print OUTPUT "\\begin{tikzpicture}\n"; &doapplyCSVstar($body, $csvrow, $numcols, @entry); print OUTPUT "\\end{tikzpicture}"; print OUTPUT "%<< END INSERT\n"; print "\n"; } sub applyCSVfile { my ($CSVFILE,$body,$startrow) = @_; print "Converting \\applyCSVfile{$CSVFILE}"; print OUTPUT "\% \\applyCSVfile{$CSVFILE}... converted using csvtools.pl\n"; print OUTPUT "\%>> START INSERT\n"; # read in data from CSV file local($csvrow, @idx, %HoH) = &getCSVvalues($CSVFILE,$startrow); # apply data &doapplyCSV($csvrow, $body, @idx, %HoH); print "\n"; print OUTPUT "\%<< END INSERT\n"; } sub sortapplyCSVfile { my ($CSVFILE,$body,$options) = @_; print "Converting \\sortapplyCSVfile{$CSVFILE}"; print OUTPUT "\% \\sortapplyCSVfile{$CSVFILE}... converted using csvtools.pl\n"; print OUTPUT "\%>> START INSERT\n"; # get options %hash = &getKeyValues($options) if $options; local($startrow) = defined($hash{'firstdataline'}) ? $hash{'firstdataline'} : $globalsort{'firstdataline'}; local ($sortorder) = $hash{'sort'}; # read in data from CSV file local($csvrow, @idx, %HoH) = &getCSVvalues($CSVFILE,$startrow); # get sort variable local($variable) = defined($hash{'variable'}) ? $hash{'variable'} : $globalsort{'variable'}; local(@rowdata)=(); print OUTPUT "\\bgroup\n"; # set field labels foreach $entry (keys %{$HoH[1]}) { print OUTPUT "\\setcsvfieldlabel{"; print OUTPUT ($idx{$entry}+1).'}{'; print OUTPUT "$entry}%\n"; } print OUTPUT "\\def\\csvsortlist{%\n"; # construct list foreach ($row=1; $row <= $csvrow; $row++) { print "."; local($sortkey) = $variable; $sortkey = &csvSaveEntry($sortkey,$row,%HoH); foreach $entry ( keys %{$HoH[$row]}) { $replacementval=$HoH[$row]{$entry}; $sortkey =~ s/\\insertbyname{$entry}/$replacementval/g; $sortkey =~ s/\\insert$entry/$replacementval/g; local($column) = $idx{$entry}+1; $rowdata[$idx{$entry}] = $replacementval; $sortkey =~ s/\\field{(\d+)}/$1eq$column?$replacementval:$&/emg; } print OUTPUT "{$sortkey}{".join($separator,@rowdata)."}"; print OUTPUT ($row == $csvrow ? "%\n" : ",%\n"); } print OUTPUT "}%\n"; print OUTPUT "\\sortapplyCSVdata"; if (defined($sortorder)) { print OUTPUT "[$sortorder]"; } print OUTPUT "{\\csvsortlist}{%\n"; print OUTPUT "$body}%\n"; print OUTPUT "\\egroup\n"; print OUTPUT "\%<< END INSERT\n"; print "\n"; } sub sortapplyCSVfilestar { my ($CSVFILE,$body,$options) = @_; print "Converting \\sortapplyCSVfile*{$CSVFILE}"; print OUTPUT "\% \\sortapplyCSVfile*{$CSVFILE}... converted using csvtools.pl\n"; print OUTPUT "\%>> START INSERT\n"; # get options %hash = &getKeyValues($options) if $options; local($startrow) = defined($hash{'sfirstdataline'}) ? $hash{'sfirstdataline'} : $globalsort{'sfirstdataline'}; local ($sortorder) = $hash{'sort'}; local($csvrow, $numcols, @entry) = &getCSVvaluesstar($CSVFILE, $startrow); # get sort variable local($variable) = defined($hash{'variable'}) ? $hash{'variable'} : $globalsort{'variable'}; local(@rowdata)=(); print OUTPUT "\\bgroup\n"; print OUTPUT "\\def\\csvsortlist{%\n"; # construct list for ($row=0; $row < $csvrow; $row++) { print "."; local($sortkey) = $variable; for ($column=0; $column < $numcols; $column++) { $replacementval=$entry[$row][$column]; $sortkey =~ s/\\field{(\d+)}/$1eq$column+1?$replacementval:$&/emg; $rowdata[$column] = $replacementval; } print OUTPUT "{$sortkey}{".join($separator,@rowdata)."}"; print OUTPUT ($row == $csvrow-1 ? "%\n" : ",%\n"); } print OUTPUT "}%\n"; print OUTPUT "\\sortapplyCSVdata*"; if (defined($sortorder)) { print OUTPUT "[$sortorder]"; } print OUTPUT "{\\csvsortlist}{%\n"; print OUTPUT "$body}%\n"; print OUTPUT "\\egroup\n"; print "\n"; print OUTPUT "\%<< END INSERT\n"; } sub sortCSVtotabular { my ($CSVFILE,$align,$headrow,$midrows,$lastrow,$envname,$options) = @_; print "Converting \\sortCSVto${envname}{$CSVFILE}"; print OUTPUT "\% \\sortCSVto${envname}{$CSVFILE}... converted using csvtools.pl\n"; print OUTPUT "\%>> START INSERT\n"; # get options %hash = &getKeyValues($options) if $options; local($startrow) = defined($hash{'firstdataline'}) ? $hash{'firstdataline'} : $globalsort{'firstdataline'}; local ($sortorder) = $hash{'sort'}; # read in data from CSV file local($csvrow, @idx, %HoH) = &getCSVvalues($CSVFILE,$startrow); # get sort variable local($variable) = defined($hash{'variable'}) ? $hash{'variable'} : $globalsort{'variable'}; local(@rowdata)=(); print OUTPUT "\\bgroup\n"; # set field labels foreach $entry (keys %{$HoH[1]}) { print OUTPUT "\\setcsvfieldlabel{"; print OUTPUT ($idx{$entry}+1).'}{'; print OUTPUT "$entry}%\n"; } print OUTPUT "\\def\\csvsortlist{%\n"; # construct list foreach ($row=1; $row <= $csvrow; $row++) { print "."; local($sortkey) = $variable; $sortkey = &csvSaveEntry($sortkey,$row,%HoH); foreach $entry ( keys %{$HoH[$row]}) { $replacementval=$HoH[$row]{$entry}; $sortkey =~ s/\\insertbyname{$entry}/$replacementval/g; $sortkey =~ s/\\insert$entry/$replacementval/g; local($column) = $idx{$entry}+1; $rowdata[$idx{$entry}] = $replacementval; $sortkey =~ s/\\field{(\d+)}/$1eq$column?$replacementval:$&/emg; } print OUTPUT "{$sortkey}{".join($separator,@rowdata)."}"; print OUTPUT ($row == $csvrow ? "%\n" : ",%\n"); } print OUTPUT "}%\n"; if (defined($sortorder)) { print OUTPUT "\\setkeys{csvsort.sty}{sort=$sortorder}"; } print OUTPUT "\\sortCSVdatatotabular"; print OUTPUT "{$csvrow}{\\csvsortlist}{$align}%\n"; print OUTPUT "{$headrow}%\n"; print OUTPUT "{$midrows}%\n"; print OUTPUT "{$lastrow}%\n"; print OUTPUT "{$envname}%\n"; print OUTPUT "\\egroup\n"; print OUTPUT "\%<< END INSERT\n"; print "\n"; } sub csvSaveEntry { my ($STR,$row,%HoH) = @_; $rowctr = "csvrownumber"; while (($pos = index($STR, "\\csvSaveEntry")) > -1) { $start = substr($STR,0,$pos); $restofline = substr($STR,$pos+13); $restofline = &eatcomments($restofline); $restofline = &eatinitialspaces($restofline); if (substr($restofline,0,1) eq "[") { if (($i = index($restofline,"]")) > -1) { $rowctr = substr($restofline, 1, $i-1); $restofline = substr($restofline, $i+1); } else { die "unmatched [ in \\csvSaveEntry\n"; } $row = $counter{$rowctr}; } ($group,$restofline,$done) = &getnextgroup($restofline); if (!$done) { die "argument to \\csvSaveEntry[$rowctr] not found in >>$restofline<<\n"; } $val = $HoH[$row]{$group}; $STR = $start . "\\expandafter\\gdef\\csname $group\\roman{$rowctr}\\endcsname{$val}" . $restofline; $data{$group}[$row] = $HoH[$row]{$group}; } return $STR; } sub applyCSVfilestar { my ($CSVFILE,$body,$startrow) = @_; print "Converting \\applyCSVfile*{$CSVFILE}"; $counter{'csvrownumber'} = 0; local($csvrow, $numcols, @entry) = &getCSVvaluesstar($CSVFILE, $startrow); &doapplyCSVstar($body, $csvrow, $numcols, @entry); print "\n"; print OUTPUT "\%<< END INSERT\n"; } sub csvtotabular { my ($CSVFILE,$ALIGN,$START,$MID,$END,$environment) = @_; print "Converting \\CSVtotabular{$CSVFILE}"; print OUTPUT "\% \\CSVtotabular{$CSVFILE}... converted using $0\n"; print OUTPUT "\%>> START INSERT\n"; # read in data from CSV file local($csvrow, @idx, %HoH) = &getCSVvalues($CSVFILE,2); print OUTPUT "\\setcounter{csvrownumber}{0}%\n"; $counter{csvrownumber} = 0; print OUTPUT "\\begin{$environment}{$ALIGN}\n"; print OUTPUT "$START"; foreach ($row=1; $row <= $csvrow; $row++ ) { print "."; if ($row == $csvrow) { $THISROW=$END; } else { $THISROW=$MID; } foreach $entry ( keys %{$HoH[$row]}) { $replacementval=$HoH[$row]{$entry}; $THISROW =~ s/\\insertbyname{$entry}/$replacementval/g; $THISROW =~ s/\\insert$entry/$replacementval/g; $column = $idx{$entry}; $THISROW =~ s/\\field{$column}/$replacementval/g; $THISROW = &ifnextrowlast($THISROW, ($row==$csvrow-1)); } print OUTPUT "\\refstepcounter{csvrownumber}$THISROW"; $counter{csvrownumber}++; } print OUTPUT "\\end{$environment}"; print OUTPUT "\%<< END INSERT\n"; print "\n"; } sub ifnextrowlast { my ($STR, $nextislast) = @_; if (($pos = index($STR, "\\ifnextrowlast")) > -1) { $strbegin = substr($STR,0,$pos); $strend = substr($STR,$pos+14); ($firstarg,$strend,$done) = &getnextgroup($strend); if (!$done) { die "Can't find first argument to \\ifnextrowlast\n"; } ($secondarg,$strend,$done) = &getnextgroup($strend); if (!$done) { die "Can't find second argument to \\ifnextrowlast\n"; } if ($nextislast) { $STR = $strbegin . $firstarg . $strend; } else { $STR = $strbegin . $secondarg . $strend; } } return $STR; } sub eatcomments { local ($_) = @_; if (m/(\\{1}%[^%]*)*(\\*)%/) { $pre = $`; $pre .= $1 if defined($1); $pre .= $2; local($n) = length($2); if ($n%2 == 0) { $_ = $pre; } } return $_; } sub eatinitialspaces { local ($_) = @_; # while (substr($STR,0,1) eq " " or substr($STR,0,1) eq "\n" # or substr($STR,0,1) eq "\r" or substr($STR,0,1) eq "\t") # { # $STR = substr($STR,1); # } s/^(\s)*//sg; return $_; } sub getnextgroup { my($curline) = @_; $curline = &eatcomments($curline); $curline = &eatinitialspaces($curline); # check to see if current string is blank if ($curline!~/[^\s]+/m) { return ("",$curline,0); } if (($group = substr($curline,0,1)) ne "{") { # next group hasn't been delimited with braces # return first non-whitespace character $curline = substr($curline,1); # unless it's a backslash, in which case get command name if ($group eq "\\") { if ($curline=~/([a-zA-Z]+)(^[a-zA-Z].*)/m) { $group = $1; $curline = $2; } else { # command is made up of backslash followed by symbol $curline=~/([\W_0-9\s\\])(.*)/m; $group = $1; $curline = $2; } } return ($group,$curline,1); } my $pos=index($curline, "{"); my $startpos=$pos; my $posopen=0; my $posclose=0; my $bracelevel = 1; my $done=0; while (!$done) { $pos++; $posopen = index($curline, "{", $pos); # check to make sure it's not a \{ while ((substr($curline, $posopen-1,1) eq "\\") and ($posopen > 0)) { # count how many backlashes come before it. $i = $posopen-1; $numbs = 1; while ((substr($curline, $i-1,1) eq "\\") and ($i > 0)) { $numbs++; $i--; } # is $numbs is odd, we have a \{, otherwise we have \\{ if ($numbs%2 == 0) { last; } else { $posopen = index($curline, "{", $posopen+1); } } $posclose= index($curline, "}", $pos); # check to make sure it's not a \} while ((substr($curline, $posclose-1,1) eq "\\") and ($posclose > 0)) { # count how many backlashes come before it. $i = $posclose-1; $numbs = 1; while ((substr($curline, $i-1,1) eq "\\") and ($i > 0)) { $numbs++; $i--; } # is $numbs is odd, we have a \}, otherwise we have \\} if ($numbs%2 == 0) { last; } else { $posclose = index($curline, "}", $posclose+1); } } if (($posopen==-1) and ($posclose==-1)) { $done=1; } elsif ($posopen==-1) { $pos=$posclose; $bracelevel--; if ($bracelevel==0) { $group = substr($curline, $startpos+1, $pos-$startpos-1); $curline = substr($curline, $pos+1); return ($group,$curline,1); } } elsif ($posclose==-1) { $pos=$posopen; $bracelevel++; } elsif ($posopen<$posclose) { $pos=$posopen; $bracelevel++; } elsif ($posclose<$posopen) { $pos=$posclose; $bracelevel--; if ($bracelevel==0) { $group = substr($curline, $startpos+1, $pos-$startpos-1); $curline = substr($curline, $pos+1); return ($group,$curline,1); } } } # closing brace must be on another line return ("", $curline, 0); } sub getnextoptionalgroup { my($curline) = @_; $curline = &eatcomments($curline); $curline = &eatinitialspaces($curline); # check to see if current string is blank if ($curline!~/[^\s]+/m) { return ("","",0); } if (($group = substr($curline,0,1)) ne "[") { # no optional argument present return ("", $curline, 1); } $posopen = index($curline, "["); # search for first occurance of ] $posclose = index($curline, "]"); if ($posclose < 0) { # closing bracket not on this line return ("", $curline, 0); } # is there a { in the substring? $group = substr($curline, $posopen, $posclose+1); $contents = $group; $restofline = substr($curline, $posclose+$posopen+1); while (($idx=index($group, "{")) > 0) { $post = substr($group, $idx); $subgroup=""; ($subgroup,$remainder,$done) = &getnextgroup($post); if ($done) { $group = $remainder; } else { # ] is in a group, so look for next one $posclose = index($restofline, "]"); if ($posclose < 0) { # closing bracket not on this line return ("", $curline, 0); } else { $contents .= substr($restofline,0,$posclose); $group = $contents; } } } $contents=~s/^\[(.*)\]$/$1/; return ($contents, $restofline, 1); } # get all entries in a row # modified version of code given in Perl FAQ sub getEntries{ local($row)=@_; @entries = (); push(@entries, $+) while $row =~ m{ "([^\"\\]*(?:\\.[^\"\\]*)*)"$separator? # groups the phrase inside the quotes | ([^$separator]+)$separator? | $separator }gx; push(@entries, undef) if substr($row,-1,1) eq $separator; @entries; } # extract key=value pairs from list sub getKeyValues{ local($list,%defaults)=@_; local(%hash)=(); # remove leading commas and spaces $list=~s/^[\s,]*//; while ($list=~/^\s*([^,=]+)\s*/) { $key=$1; $value = ''; if ($list=~s/^\s*$key\s*=\s*{/{/) { ($value,$list,$done) = &getnextgroup($list); } elsif ($list=~s/^\s*$key\s*=\s*([^,]*)\s*//) { $value = $1; } elsif ($list=~s/^\s*$key\s*(,|$)//) { if (defined($defaults{$key})) { $value = $defaults{$key}; } else { print "don't know default value for $key key\n"; } } $hash{$key} = $value; # remove trailing commas $list=~s/^[\s,]*//; } %hash; } 1;