#!/usr/bin/perl -w
# parser "ls -lR"
# Autor: Michał Mirosław   2003/11/12
use strict;
use IO::Handle;
use IO::File;

# uwaga - IO::Filter 0.01 ma błąd w funkcji getline

# sprawdzenie argumentów
if (@ARGV != 1) {
	print STDERR "$0 plik_ls-lR\n";
	if (eval { require IO::Filter; $IO::Filter::VERSION > 0.01; }) {
		print STDERR "obsługuje gzip i bzip2\n";
	} elsif (eval { require IO::Zlib; }) {
		print STDERR "dekompresja gz dostępna; brakuje filtru bzip2 (IO::Filter\n";
		print STDERR "jest niedostępny bądź ma błąd wykluczający jego użycie\n";
	} else {
		print STDERR "brakuje modułu IO::Filter do obsługi gzip i bzip2\n";
		print STDERR "lub moduł ten ma błąd wykluczający jego użycie\n";
		print STDERR "dekompresja gzip jest możliwa też po zainstalowaniu modułu IO::Zlib\n";
	}
	exit (@ARGV > 0);
}

my $file = $ARGV[0];
my $io;

# otwieramy plik
if ($file eq '-') {
	$io = new IO::Handle;
	die "nie mogę otworzyć STDIN: $!" unless $io->fdopen(fileno(STDIN), 'r');
} else {
	$io = new IO::File($file, "r");
	die "nie mogę otworzyć pliku: $!" unless $io;

	if ($file =~ s/\.gz$//) {
		if (eval { require IO::Zlib; }) {
			$io->close;
			$io = new IO::Zlib "$file.gz", "r";
			die "nie mogę dekompresora zlib: $!" unless $io;
		} elsif (eval { require IO::Filter; $IO::Filter::VERSION > 0.01; }) {
			require IO::Filter::gunzip;
			$io = new IO::Filter::gunzip $io, "r";
			die "nie mogę otworzyć filtra dekompresji gzip: $!" unless $io;
		} else {
			die "filtr gzip niedostępny";
		}
	} elsif ($file =~ s/\.bz2$//) {
		if (eval { require IO::Filter; $IO::Filter::VERSION > 0.01; }) {
			require IO::Filter::bunzip2;
			$io = new IO::Filter::bunzip2 $io, "r";
			die "nie mogę otworzyć filtra dekompresji bzip2: $!" unless $io;
		} else {
			die "filtr bzip2 niedostępny";
		}
	}
}

# mamy już plik, teraz czas pohaczyć

use constant WANTDIR => 1;
use constant WANTTOTAL => 2;
use constant PARSEFILES => 3;

my $state = WANTDIR;
my $dir = undef;
my ($type, $mode, $links, $user, $group, $size, $mday, $mmonth, $mhouroryear, $name);

while (defined ($_ = $io->getline)) {
	chomp $_;
#	print "($state) -> $_\n";

	if ($_ eq '') {
		$state = WANTDIR;
		next;
	} elsif ($state == WANTDIR) {
		if (s/:$/\//) {
			s/^\.\//\//;	# './' -> '/'
			$_ = "/$_" unless '/' eq substr $_, 0, 1;
			$dir = $_;
			$state = WANTTOTAL;
		} else {
			die "śmieci w linii $.";
		}
		next;
	} elsif ($state == WANTTOTAL) {
		$state = PARSEFILES;
		if (m/^total [0-9]+$/) {
			next;
		} else {
			warn "w linii $. nie ma 'total ...' - traktuję jako początek listy plików";
		}
	}

	# przetwarzanie właściwe
	# przykładowe linie z 'LC_ALL=C ls -lR`

# -rwx------    1 mirq     users       21741 Jul 13 07:31 avidump
# drwx------    2 mirq     users          48 Nov 12 17:53 dupa?jasia
# lrwxrwxrwx    1 mirq     users          24 Oct 16  2002 ww -> /usr/local/bin/wine-wrap
# lrwxrwxrwx    1 mirq     users          13 Nov 12 19:24 bfsgdf -> ble -> safsd -> blah

	# tego ostatniego przypadku nie obsługujemy; tj. obsługujemy obcinając
	# nazwę linka do pierwszego ' -> ';
	# user musiał to sam z premedytacją stworzyć, więc niech się sam teraz
	# zastanawia, dlaczego jest to źle interpretowane; pliki specjalne
	# wywalamy z listingu

	next unless m/^([-dl])/;
	$type = $1;
	($mode, $links, $user, $group, $size, $mday, $mmonth, $mhouroryear, $name)
		= split(/\s+/, $_, 9);
	next unless defined $name and $name ne '.' and $name ne '..';

	if ($type eq 'd') {
		$size = 0;
		$name .= '/';
	} elsif ($type eq 'l') {
		$size = -1;		# TAK MA BYĆ ?
		($name) = split(/ -> /, $name);
	}

	print "$size\t$dir$name\n";
}
