#!/usr/bin/perl
# The Missing Textutils, Ondrej Bojar, obo@cuni.cz
# http://www.cuni.cz/~obo/textutils
#
# Given any number of files as args, 'ziplines' produces a file where first
# comes a line from the first file, then a line from the second, etc.
# It produces blank lines if any of the files is shorter, so the output is 
# as long as the longest file.
#
# $Id: ziplines,v 1.10 2013-06-17 19:04:41 bojar Exp $
#

use strict; use warnings;
use IO::File;
use Getopt::Long;

my $diffonly = 0; # print only blocks with differing lines
my $delim = 0; # add a blank line after each block
my $blockdelimiter = undef; # what should be the delimiter of the blocks
my $itemdelimiter = "\n"; # what should be the delimiter of items within each block
my $namedelimiter = "\t"; # delimits filename and the line
my $showname = 0; # show file name as the first column
my $walk = 0;
GetOptions(
  "diff-only" => \$diffonly,
  "delim" => \$delim,
  "block-delimiter=s" => \$blockdelimiter,
  "item-delimiter=s" => \$itemdelimiter,
  "name-delimiter=s" => \$namedelimiter,
  "showname" => \$showname,
  "walk-further-if-repeated" => \$walk,
    # if the same filename is given many times, keep walking it further, do not
    # print the same line several times
) or exit 1;
$itemdelimiter =~ s/\\n/\n/g;
$itemdelimiter =~ s/\\t/\t/g;
$itemdelimiter =~ s/\\\\/\\/g;

die "Don't use --block-delimiter and --delim at the same time."
  if defined $blockdelimiter && $delim;
$blockdelimiter = "\n\n" if $delim;
$blockdelimiter = "\n" if !defined $blockdelimiter;

$blockdelimiter =~ s/\\n/\n/g;
$blockdelimiter =~ s/\\t/\t/g;
$blockdelimiter =~ s/\\\\/\\/g;

my @files = @ARGV;
my %fn_to_stream;
my @streams = map {
  my $fn = $_;
  my $openstr = ($_ =~ /\.gz$/ ? "zcat $_ |" : "< $_");
  my $stream;
  if ($walk && defined $fn_to_stream{$fn}) {
    $stream = $fn_to_stream{$fn};
  } else {
    $stream = IO::File->new($openstr) or die "Can't open '$openstr'";
    $fn_to_stream{$fn} = $stream;
  }
  $stream; # output value to the map
} @files;
while (1) {
  my $eofs = 0;
  my @block = ();
  my $all_same = 1;
  for(my $i=0; $i<=$#streams; $i++) {
    my $line;
    if (eof($streams[$i])) {
      $line = "";
    } else {
      $line = readline($streams[$i]);
      chomp $line;
    }
    push @block, $line;
    $all_same = 0 if $diffonly && $block[-1] ne $block[0];
    $eofs++ if eof($streams[$i]);
  }
  if (!$diffonly || !$all_same) {
    @block = map { $files[$_].$namedelimiter.$block[$_] } (0..$#block)
      if $showname;
    print join($itemdelimiter, @block);
    print $blockdelimiter;
  }
  last if $eofs == scalar @streams;
}
