#!/usr/bin/perl
#
#  wmanagerrc-update -- update your wmanager settings automatically
#  Copyright (C) 2000  Tommi Virtanen <tv@debian.org>
#  Copyright (C) 2008, 2010, 2014, 2015  Peter Pentchev <roam@ringlet.net>

use 5.010;
use strict;
use warnings;

use File::Spec::Link;
use File::Temp;
use POSIX qw/WEXITSTATUS WIFEXITED/;

sub collect_user()
{
	my $fname = "$ENV{HOME}/.wmanagerrc.user";
	open my $f, '<', $fname or do {
		if ($!{ENOENT}) {
			return ();
		} else {
			die "Could not open $fname: $!\n";
		}
	};
	my @res = <$f>;
	close $f or
	    die "Could not close $fname: $!\n";

	s/[\r\n]*$// for @res;
	return @res;
}

sub collect_alternatives()
{
	my $pid = open my $f, '-|';
	if ($pid == -1) {
		die "Could not fork for update-alternatives: $!\n";
	} elsif ($pid == 0) {
		my @cmd = ('update-alternatives', '--query', 'x-window-manager');
		exec { $cmd[0] } @cmd;
		die "Could not execute @cmd: $!\n";
	}

	my $alt;
	my %p;
	while (<$f>) {
		s/[\r\n]*$//;
		if (/^Alternative:\s+([\/\w.+-]+)$/) {
			if (defined $alt) {
				die "Could not parse the update-alternatives output: Alternative without Priority\n";
			}
			$alt = $1;
		} elsif (/^Priority:\s+(\d+)$/) {
			if (!defined $alt) {
				die "Could not parse the update-alternatives output: Priority without Alternative\n";
			}
			push @{$p{$1}}, $alt;
			undef $alt;
		}
	}
	close $f;
	my $res = $?;
	if (!WIFEXITED($res) || WEXITSTATUS($res) != 0) {
		die "The update-alternatives query failed\n";
	}

	my @files = map { sort @{$p{$_}} } sort { $a <=> $b } keys %p;
	@files = grep defined, map { File::Spec::Link->resolve($_) } @files;

	my @output;
	$pid = open $f, '-|';
	if ($pid == -1) {
		die "Could not fork for dpkg -S: $!\n";
	} elsif ($pid == 0) {
		my @cmd = ('dpkg', '-S', '--', @files);
		exec { $cmd[0] } @cmd;
		die "Could not exec @cmd: $!\n";
	}
	while (<$f>) {
		s/[\r\n]*$//;
		next if /^dpkg:/;
		s/:[[:space:]]+/=/;
		push @output, $_;
	}
	close $f;
	$res = $?;
	if (!WIFEXITED($res) || WEXITSTATUS($res) != 0) {
		die "The dpkg -S query failed\n";
	}
	return @output;
}

sub order_alternatives(@)
{
	my @cand = @_;
	my @split = map {
		if (/^([^=]+?)[[:space:]]*=/) {
			[$1, $_]
		} else {
			()
		}
	} @cand;

	my $fname = "$ENV{HOME}/.wmanagerrc.order";
	open my $f, '<', $fname or do {
		if ($!{ENOENT}) {
			return @cand;
		} else {
			die "Could not open $fname: $!\n";
		}
	};
	my %c = map { ($_->[0], $_->[1]) } @split;
	my @order;
	while (<$f>) {
		s/[\r\n]*$//;
		next unless exists $c{$_};
		push @order, $c{$_};
		delete $c{$_};
	}
	close $f;

	push @order, map $_->[1], grep exists $c{$_->[0]}, @split;
	return @order;
}

sub write_menu(@)
{
	my @cand = @_;

	my $tempf = File::Temp->new(TEMPLATE=> '.wmanagerrc.XXXXXX', DIR => $ENV{HOME});
	print $tempf "$_\n" for @cand;
	my $fname = "$ENV{HOME}/.wmanagerrc";
	rename $tempf->filename(), $fname or
	    die "Could not rename ".$tempf->filename()." to $fname: $!\n";
	$tempf->unlink_on_destroy(0);
}

MAIN:
{
	my @cand = (collect_user, collect_alternatives);
	@cand = order_alternatives @cand;
	write_menu @cand;
}
