#! /usr/bin/perl 
# csv2lyx
# Copyright (C) 2001 Antonio Gulino
# This program is free software; you can redistribute it and/or modify it under
# the term of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or any later version.
# RCS:
# $Date: 2001/11/01 11:18:26 $
# $Revision: 1.6 $
# $Source: /home/antonio/tesi/pl/csv2lyx/csv2lyx,v $
# $Log: csv2lyx,v $
# Revision 1.6  2001/11/01 11:18:26  antonio
# ho messo a posto un po la documentazione
# in particolare ho reso la parte Options compatibile con pod2man
# e ho aggiustato il titolo della sezione --lyxserver
# Gnter Milde mi aveva segnalato appositamente i problemi con le man-pages.
# Li conoscevo di gi ma avevo dato la precedenza alla versione html.
# Adesso ho deciso di far prevalere la versione man.
#
# Revision 1.5  2001/10/30 12:42:49  antonio
# Translation and improvement of Documentation
# Thanks to Gnter Milde <G.Milde@physik.tu-dresden.de>
# This will be the version 0.0.6fix2
#
# Revision 1.4  2001/10/29 17:41:19  antonio
# ho messo a posto dei link nella documentazione
# e ho aggiunto la documentazione riguardante --fontsize
# dovrebbe diventare la versione 0.0.6fix1
#
# Revision 1.3  2001/10/10 16:10:23  antonio
# Neue option --fontsize=-3|-2|-1|+1|+2|+3, welche die Grsse aller Zellen ndert,
# aber nicht jene des Titels.
# --help eingefhrt
# -L als alternative zu --lyxserver eingefhrt
# --multicolumn als Alternative zu --colspan (weil LaTeX-Ausdruck, whrend colspan Html-Ausdruck ist)
#
# Revision 1.2  2001/09/29 22:14:47  antonio
# usando --lyxserver il file temporaneo viene messo in /tmp
# Ho modificato la documentazione e introdotto la parte BUGS del pod
#

=head1 NAME

csv2lyx - converter from csv-file to a LyX table

=head1 DESCRIPTION

Converts a Comma Separated Value file to a float table in LyX format.
Good for LyX version 1.1.4fix1 and other. You can use it whit the LyX-server
too.

=head1 HOW USE IT

=head2 Creating a LyX file

under Linux one of the follow (see also the many L<"Options">):

 antonio@bidone:~ > cat mycsvfile | csv2lyx > mytmptable.lyx
 antonio@bidone:~ > cat mycsvfile | csv2lyx --output=mytmptable.lyx
 antonio@bidone:~ > csv2lyx mycsvfile > mytmptable.lyx
 antonio@bidone:~ > csv2lyx mycsvfile --output=mytmptable.lyx  
 antonio@bidone:~ > csv2lyx mycsvfile       -o mytmptable.lyx  
 antonio@bidone:~ > csv2lyx mycsvfile --lyxserver  

As default this  will create a Float-Table in the LyX format,
version 2.15 (used by LyX-1.1.4fix1 but readable by higher versions as well).

=head2 Using the created LyX file

=over 4

=item Include as external LyX file

as usal: Insert --> Include File --> Use Include (mytmptable.lyx)

=item Insert directly in your LyX document

as usal: Insert --> LyXfile (mytmptable.lyx)

=back

=head1 Options

 -s or --separator="what you want"
 -o or --output='A filename, please!'
 -l or --label='A label for crossreference to the table'
 -f or --fontsize=-3|-2|-1|+1|+2|+3
 -c or --colspan="a pattern" or --multicolumn="a pattern" 
 -vl or --vlines
 -hl or --hlines   
 -g or --grid      
 -b or --border    
 -h or --header='N'  
 -t or --title="A pretty title for a pretty table" 
 -L or --lyxserver
 --nofloat
 --help

=head2 --fontsize

 -f or --fontsize=-3|-2|-1|+1|+2|+3 

-1 means I<small>, -2 means I<smaller> , +1 means I<large>,  and so on.
Very usefull in LyX-1.1.6, where formatting each cell is I<very> cumbersome.
 

=head2 --separator 


 -s or --separator="what you want"

default: "\t" for a tab-separated file

=head2 --output

 -o or --output='A filename, please!' 

default: STDOUT aka Screen. That means, that you can use the pipe, if you want:

 antonio@bidone > csv2lyx pippo.csv > pippo.lyx

is the same as

 antonio@bidone > csv2lyx pippo.csv -o pippo.lyx

If you use L<"--lyxserver">, then --output is overwriten by a random filename.

=head2 --lyxserver

 -L or --lyxserver 

With this option, you can past the table direct in a LyX-document at the
current editing point (the place of the cursor).

Of course, to have this working, you need to have  an open LyX-document.

This option is valid only when the lyxserver is started:

What's the "lyxserver"?

Good question. A partial, but simple answord is:
go in your lyxrc and write the line 

 \serverpipe "~/.lyxpipe"

The lyxserver works on *NIX, OS/2, but not Win32!

If you have problems, close LyX, rm ~/.lyxpipe.in and ~/.lyxpipe.out. Restart. 
Better: look on the LyX-Documentation "Configuration.lyx".

=head2 --title

 -t or --title="A pretty title for a pretty table" 

default: inputfilename otherwise, when you use the pipe, "Title of the LyX
	 table"

=head2 --label

 -l or --label='A label for crossreference to the table'

default: none

=head2  --nofloat

If you don't want a Float-Table. In this case the options L<"--title"> and
L<"--label"> are ignored.

default: Float-Table

=head2 --header 

 -h or --header='N'  (--header is the same as --header='1')

The first N rows are a Header.

Default: noheader

A line is made under the last row of the Header. If L<"--border">,
L<"--grid"> or L<"--hlines"> are activated, then are two lines unter the
last row of the Header.

=head2 --border

 -b or --border    

A line around the table.

Default: noborder

=head2 --grid

 -g or --grid      

All cells separated by lines.
The same as  L<"--border">, L<"--vlines">, L<"--hlines">.

Default: nogrid

=head2 --hlines

 -hl or --hlines   

Horizonal line between each row.

=head2 --vlines

 -vl or --vlines   

Vertical lines between each column.


=head2 --colspan

 -c or --colspan="a pattern"

Multicolumns: cells that span over several columns

One can indicate, line for line whether some columns should be joined.
Number I<n> indicates how many columns the I<n>th value should be in. The
slash separates lines.  If less than the existing columns are indicated, the
remaining numbers are assumed to be 1 (i.e. one column per value). Also,
'empty' lines are automatically set to 'normal' (one column per value).

Here are some examples:

=over 4

=item --colspan="23"  or --colspan="23/////" or --colspan="23/11111////  or ...

   +-------------+----------------------+
 1 |  2 columns  |         3 columns    |
   +-----+-------+-----+--------+-------+
 2 |     |       |     |        |       |


=item --colspan="23/221" or --colspan="23/22"

   +-------------+----------------------+
 1 |  2 columns  |         3 columns    |
   +-----+-------+-----+--------+-------+
 2 |  2 columns  |   2 columns  | 1 col |
   +-----+-------+-----+--------+-------+
 3 |     |       |     |        |       |


=item --colspan="23//221"  or --colspan="23/11111/221"

   +-------------+----------------------+
 1 | 2 columns   |         3 columns    |
   +-----+-------+-----+--------+-------+
 2 |     |       |     |        |       |
   +-----+-------+-----+--------+-------+
 3 | 2 columns   |   2 columns  | 1 col |
   +-----+-------+-----+--------+-------+
 4 |     |       |     |        |       |


=item --colspan="/12/212"  or --colspan="11111/1211/212"

   +-----+-------+-------+--------+-------+
 1 |     |       |       |        |       |
   +-----+-------+-------+--------+-------+
 2 |1 col|     2 columns |        |       |
   +-----+-------+-------+--------+-------+
 3 | 2 columns   | 1 col |      2 columns |
   +-----+-------+-------+--------+-------+
 4 |     |       |       |        |       |


=back

=head1 WILL COME (maybe)

Next features are:

=over 4

=item --format="|p{3cm}|c|"

formatting the column, with LaTeX syntax

=back

=head1 BUGS

Using L<"--lyxserver"> your data are on the C</tmp> directory. Maybe
everybody can see your data.

=head1 AUTHOR, COPYLEFT and THANKS

Copyright (C) 2001, Antonio Gulino <antonio.gulino@tin.it>

This program is free software; you can redistribute it and/or modify it under
the term of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or any later version.

Thanks to Guenter Milde <G.Milde@physik.tu-dresden.de>  for translation and improvement of the Documentation.

=cut

###########################################################################################
# di default il separatore e' un tabulatore

use strict;
use Getopt::Long;
my $OPT_separator = "\t";
my $OPT_header = '-1';
my ($OPT_output, $OPT_title , $OPT_border, $OPT_nofloat,
    $OPT_label,$OPT_grid,$OPT_hlines,$OPT_vlines, $OPT_colspan,
    $OPT_lyxserver, $OPT_fontsize, $OPT_help
    );
$OPT_help=1 unless @ARGV; # if no one option, then --help   
&GetOptions( "separator|s=s" => \$OPT_separator,
             "title|t=s"     => \$OPT_title,
             "output|o=s"    => \$OPT_output,
             "fontsize|f=s"  => \$OPT_fontsize,
             "header|h:i"    => \$OPT_header,
             "border|b"      => \$OPT_border,
             "nofloat"       => \$OPT_nofloat,
             "label|l=s"     => \$OPT_label,
             "grid|g"        => \$OPT_grid,
             "hlines|hl"     => \$OPT_hlines,
             "vlines|vl"     => \$OPT_vlines,
             "colspan|c|multicolumn=s"   => \$OPT_colspan,
             "lyxserver|L"   => \$OPT_lyxserver,
             "help"          => \$OPT_help,
           );

# if you use --help, then the other options are ignored
if ($OPT_help) {
print <<EOH

under Linux one of the follow (see also the many options):
 
 antonio\@bidone:~ > cat mycsvfile | csv2lyx > mytmptable.lyx
 antonio\@bidone:~ > cat mycsvfile | csv2lyx --output=mytmptable.lyx
 antonio\@bidone:~ > csv2lyx mycsvfile > mytmptable.lyx
 antonio\@bidone:~ > csv2lyx mycsvfile --output=mytmptable.lyx
 antonio\@bidone:~ > csv2lyx mycsvfile       -o mytmptable.lyx
 antonio\@bidone:~ > csv2lyx mycsvfile --lyxserver
        
As default this  will create a Float-Table in the LyX format, version 2.15 (used by LyX-1.1.4fix1).

 -s or --separator='what you want'
 -o or --output='A filename, please!'
 -l or --label='A label for crossreference to the table'
 -f or --fontsize=-3|-2|-1|+1|+2|+3
 -c or --colspan='a pattern' or --multicolumn='a pattern'
 -vl or --vlines
 -hl or --hlines
 -g or --grid
 -b or --border
 -h or --header='N'
 -t or --title='A pretty title for a pretty table'
 --nofloat
 --lyxserver
 --help
EOH
;
die "(c) 2001, Antonio Gulino <antonio.gulino\@tin.it>\n"
}


# if you dont use the --output option, then the output is redirect to the STDOUT (=screen)
# if --lyxserver, then --output='/tmp/csv2lyxRANDOM.tmp.lyx'
if ($OPT_lyxserver) {
    my $random;
#    $random = sprintf "%0.6d", rand(999999);
    $OPT_output = sprintf "/tmp/csv2lyx%0.6d.tmp.lyx", rand(999999);
}
if ($OPT_output) {
    open OUTPUT, ">$OPT_output" or die "Cannot write to file $OPT_output\n";
    select OUTPUT;
}

#####
# I add '1' to $OPT_header, so if no OPT_header, then =0
# otherwise = numer of the rows that are included in "header"
$OPT_header++ if $OPT_header<1;

#####
# if $OPT_grid, then $OPT_hlines and $OPT_vlines and $OPT_border too
if ($OPT_grid) {
   $OPT_hlines = '1';
   $OPT_vlines = '1';
   $OPT_border = '1';
}

##############################################################################
# in the first case, the data are in a file $inputFileName
# in the second case, the data comes from the pipe
# @csv have the data, in both case
my $inputFileName = shift;
my @csv;
if ($inputFileName) {
    open CSV, $inputFileName or die "Cannot read file :$inputFileName\n";
    @csv = <CSV>;
    close CSV;
} else {
    @csv = <>;
}
chomp(@csv);

###############
$OPT_title ||=$inputFileName?$inputFileName:'Title of the LyX table';

# vediamo quante righe ci sono
# how many row (=righe) are there?
my $righe = $#csv + 1;

# looking in $OPT_colspan, if I want change the default of colspans
# /         idiot
# /121      first line default, second line=121, third, forth,...line = default
# /121/112  first line default, second line=121, third line=112, forth ... line = default
# 22//112   first line=22, second line default, third line=112, forth ... line = default
# 22/*/211  first line=22, second... line default, last line=211
# */211     all lines default, but last line=211
# / mean: next line
# after * mean: beginning by the bottom

my @colspan;
for (my $i=0; $i<$righe; $i++) {
    last if $OPT_colspan eq '';        # do nothing if $OPT_colspan is empty
    last if $OPT_colspan =~ /^\*/;     # if the first charachter is a star,
                                       # then I must begin by the bottom
    next if $OPT_colspan =~ s/^\///;   # if the first charachter is a slash, then go to next line
    if ($OPT_colspan =~ s/^([1-9]+)\/?//) {
        $colspan[$i] = $1;
    }
}

# here the code for the colspan beginning by the bottom
#
#

# adesso leggo l'intero array e creo una matrice: @matrix
# contemporaneamente calcolo il numero massimo di campi, per sapere quante colonne mi servono
# now i read the whole array and put it into a matrix : @matrix
# at the same time i look how many column (=colonne) have the biggest row
my $col = 0;
my ($colonne, @matrix);
my $rn = 0;
foreach my $r (@csv) {
    my @r = split $OPT_separator, $r;
    my $sumcol;
    foreach (split('',$colspan[$rn])) {
        $sumcol += $_;
    }
    $colonne = $#r+1 - length($colspan[$rn]) + $sumcol 
        if $colonne <= $#r - length($colspan[$rn]) + $sumcol;
    push @matrix, [@r];
    $rn++;
}

# Default: each value have only one cell, over only one column . colspan = 1
my $rn = 0;
foreach (@matrix) {
    my $sumcol;
    foreach (split('',$colspan[$rn])) {
        $sumcol += $_;
    }
    $colspan[$rn] .= '1' x ($colonne - $sumcol);
    $rn++;
}


#########################################################################################
# adesso stampo il codice lyx. l'allineamento avviene a destra
# (immagino che di solito siano cifre)
# produco automaticamente una float table,
# versione funzionante per la 1.1.4fix1
# now I print the LyX format. Align=right (I think this is a matrix of numbers)
# dafault: a float table

my $insetLabel;
if ($OPT_label) {
    $insetLabel = "\n\\begin_inset LatexCommand \\label\{$OPT_label\}\n\n\\end_inset\n\n\n";
} else {$insetLabel=''}

my $insetFloat;
unless ($OPT_nofloat) {
    $insetFloat = "\\begin_float tab\n\\layout Caption\n\n$OPT_title $insetLabel\n\\layout Standard\n";
}

#####################
# print first part
####################
print <<LYX;
\#csv2lyx created this file
\\lyxformat 2.15

\\layout Standard

$insetFloat
\\align center \\LyXTable
multicol5
$righe $colonne 0 0 -1 -1 -1 -1
LYX
;
#####################

#### Horizontal Lines
#
# qui viene deciso se ci sono righe in alto o in basso di ciascuna riga
# se la prima cifra e' "1" allora c'e una linea in alto
# se la seconda cifra e' "1" allora c'e una linea in basso
# here I say if there are a border
# if the first number is "1", then a border on the top of the line
# if the second number is "1", then a border on the bottom of the line
my $rownumber = 0;
my ($top,$bottom);
foreach (@matrix) {
    if ($rownumber < $OPT_header) {
        $bottom   = ($OPT_hlines or ($rownumber==$OPT_header-1))?1:0;
        $top      = (($rownumber==0) and $OPT_border)?1:0;
    } elsif ($rownumber == $#matrix) {   # this is the last row of the table
        $top    = ($OPT_hlines or $rownumber==$OPT_header)?1:0;
        $bottom = $OPT_border?1:0;
    } else {
        $top   =$OPT_hlines?1:0;
        $bottom='0';
    }
    print "$top $bottom 0 0\n";
    $rownumber++;
};

##### Vertical lines  and   align in each column
# adesso viene deciso il formato delle colonne
# se la prima cifra e' 2, allora allineamento a sinistra
# se la prima cifra e' 4, allora allineamento a destra
# now the align of the column
# if the first number is "2", then align=left
# if the first number is "4", then align=right
# if the second number is '1', then left=line else not
# if the third number is '1', then right=line else not
my $align = '4';   # default: align=right (I think it is a number)
my ($leftline,$rightline);
for (my $i=0; $i<$colonne; $i++) {
    if ($i == 0) {
        $leftline = ($OPT_border or $OPT_vlines)?1:0;
        $rightline= '0';
    } elsif ($i == $colonne-1) {  
        $leftline = ($OPT_vlines)?1:0;
        $rightline= ($OPT_border or $OPT_vlines)?1:0;
    } else {
        $leftline =$OPT_vlines?1:0;
        $rightline='0';   
    }
    print "$align $leftline $rightline \"\" \"\"\n";
};

#########################################################
# adesso tocca alla formattazione delle singole celle.
# personalmente non ci capisco nulla e dunque faccio come ho trovato
# le  celle della prima riga hanno delle informazioni supplementari che di fatto pare riguardino
# tutta la colonna sottostante
# la primissima cella pare che abbia una formattazione ancora differente dalle altre

# now the format of each Cell
# I have make only a little "reverse engeneering", I think it's enough.

# le altre celle, riga per riga, 
# the other cells, row by row,
for (my $j=0; $j<$righe; $j++) {
    for (my $i=0; $i<$colonne; $i++) {
        my $colspan = (&colspan($colspan[$j]))[$i];
        my ($underline,$overline);
        # a special format is for the multicolumn cells, in the header
        if ($colspan eq '1' and $j<$OPT_header) {
            # I look if the cell under the current cell have the same colspan
            # this is the case, if the same calspan-pattern, 
            # beginning with 1 and the same number of'2'
            my $diffColspan=0;
            if ((&colspan($colspan[$j+1]))[$i] =~ /[01]/ and $j+1<$OPT_header) {
                # how is the colspan of the actual cell ?
                my $accs; # actual cell col span
                for (my $c=$i+1;  $c<$colonne; $c++) {
                    if ( (&colspan($colspan[$j]))[$c]  ne '2') {
                        $accs = $c;
                        $c=$colonne;
                    }
                }
                $accs = $accs - $i;
                # how is the colspan of the cell under the actual ?
                my $uccs;  # under cell col span
                for (my $c=$i+1;  $c<$colonne; $c++) {
                    if ( (&colspan($colspan[$j+1]))[$c]  ne '2') {
                        $uccs = $c;
                        $c=$colonne;
                    }
                }
                $uccs = $uccs - $i;

                # are the two cells the same colspan ?
                $diffColspan = $accs - $uccs;
            }

            $underline = '1'; if ($diffColspan <= 0 and $j<$OPT_header-1) { $underline = '0'}
            $overline  = '0'; if ($j==0 and ($OPT_border)) {$overline = '1'}
        } else {
	    if ($OPT_grid and $j<$OPT_header) {
                $underline = '1';
                $overline  = '1';
            } else {
                $underline = '0';
	        $overline  = '0'; if ($OPT_border) {$overline = '1'}
            }
	}
	print "$colspan 8 $overline $underline 0 0 0 \"\" \"\"\n";
    }
}

# segue una riga vuota
# a empty line
print "\n";

######################################################################################
# adesso segue il contenuto dell celle, riga per riga
# mi raccomando di scrivere qualcosa anche per le celle non esistenti nel file iniziale
# now the contents of each cell, row by row
# remember to put something in each cell

my %fontsize = (
    '-3' => 'scriptsize',
    '-2' => 'footnotesize',
    '-1' => 'small',
    '+1' => 'large',
    '+2' => 'larger',
    '+3' => 'largest'
);

for (my $j=0; $j<$righe; $j++) {
#    for (my $i=0; $i<$colonne; $i++) {
    for (my $i=0; $i<length($colspan[$j]); $i++) {
        if ($fontsize{$OPT_fontsize}) {
            print "\n\\size $fontsize{$OPT_fontsize}\n",$matrix[$j][$i],"\n\\size default\n\n"
        } else {
            print $matrix[$j][$i],"\n";
        }    
	unless ($j==$righe-1 and $i==$colonne-1) {print "\\newline\n"};
    }
}
print "\\end_float\n\n" unless $OPT_nofloat;
print "\\the_end\n";


##########################################################################################
# using LyX-server
##########################################################################################

if ($OPT_lyxserver and $OPT_output =~ /^\/tmp\/csv2lyx\d{6}\.tmp\.lyx$/) {
    close OUTPUT;
    system("echo \"LYXCMD:csv2lyx:file-insert\" $OPT_output > ~/.lyxpipe.in");
#    system("rm $OPT_output");
}


############################################################################################
# Subroutines are here
############################################################################################
sub colspan { # input a $string='111213', output an @array=qw(0 0 0 1 2 0 1 2 2)
    my $mystring = shift;                 # = '111213'
    my @mystring = split ('', $mystring); # = qw(1 1 1 2 1 3)
    my @result;
    foreach my $span (@mystring) {
        if ($span == 1) {
            push @result, '0'; 
        } else {
            push @result, '1';
            for (my $i=2; $i<=$span; $i++) {
                push @result, '2';
            }
        }
    }
    return @result
}

1

