#!/usr/bin/perl -w
#$Id: vfmg,v 1.53 2004/07/11 16:32:09 havner Exp $

use strict;
use File::Find;
use Getopt::Long;
use Encode;

my $o_help=0;
my $o_tags=0;
my $o_end=0;
my $o_icons=0;
my $o_exec=0;
my $o_clear=0;
my $o_promote=0;
my $o_verbose=0;
my $o_full=0;
my $o_strip=0;
my $o_output="";
my $o_utf=0;
my $o_nomenu=0;
my $o_enc="";
my $o_xterm="xterm -T xterm.%1 -e %2";

GetOptions('help'=>\$o_help,
	'tags'=>\$o_tags,
	'end|e'=>\$o_end,
	'icons'=>\$o_icons,
	'exec|x'=>\$o_exec,
	'clear'=>\$o_clear,
	'promote'=>\$o_promote,
	'verbose'=>\$o_verbose,
	'full'=>\$o_full,
	'strip'=>\$o_strip,
	'utf8'=>\$o_utf,
	'nomenu|m'=>\$o_nomenu,
	'output=s'=>\$o_enc,
	'xterm|r=s'=>\$o_xterm
	);

if($o_help) {
	print "Usage:	$0 [options] aewm|afterstep|blackbox|enlightenment|fluxbox|fvwm2|icewm|olvwm|qvwm|wmaker|wmaker-old|xfce4|xpde
	-h, --help	- print this help
	-t, --tags	- echo omitted tags to stderr
	-e, --end	- echo omitted XDG file ending to stderr
	-v, --verbose	- verbose stderr output
	-i, --icons	- check for icons existence
	-x, --exec	- check for binaries existence
	-f, --full	- extend icons and binaries to full path (needs -i or -x)
	-c, --clear	- remove empty menus
	-p, --promote	- promote submenus with single entry
	-s, --strip	- strip 1st level menu
	-u, --utf8	- output in utf8 (default is locale setting)
	-m, --nomenu	- don't add additional menu info (valid for fluxbox, blackbox, xfce4)
	-o, --output=	- output in given encoding (e.g. iso-8859-2)
	-r, --xterm=	- set x terminal application (default \"xterm -T xterm.%1 -e %2\")
				example \"gnome-terminal -t Terminal.%1 -x %2\"\n";
	exit;
}

die "Missing argument (aewm|afterstep|blackbox|enlightenment|fluxbox|fvwm2|icewm|olvwm|qvwm|wmaker|wmaker-old|xfce4|xpde)\n"
	unless exists $ARGV[0];
$o_output=$ARGV[0];
die "Unrecognized argument: $o_output\n"
	unless $o_output=~/^(aewm|afterstep|blackbox|enlightenment|fluxbox|fvwm2|icewm|olvwm|qvwm|wmaker|wmaker-old|xfce4|xpde)$/;

my @path;
if($o_exec) {
	@path=split(/:+/,$ENV{'PATH'});
}
if($o_exec and $o_xterm) {
	my $exists=0;
	my $bin=$o_xterm;
	$bin=~s/(\S+).*/$1/;  #v---------v
	foreach my $dir(@path,"") {	# would be "//dir/.../file" correct?
		if(-x "$dir/$bin") {#----^
			$exists=1;
			$o_xterm="$dir/$o_xterm" if $o_full;
			last;
		}
	}
	$o_xterm="" unless $exists;
}

# get locale (for Name[*])

my $lang4="";
$lang4=$ENV{'LANG'} if exists $ENV{'LANG'};
$lang4=$ENV{'LC_MESSAGES'} if exists $ENV{'LC_MESSAGES'};
$lang4=$ENV{'LC_ALL'} if exists $ENV{'LC_ALL'};
my $lang3=$lang4;
$lang3=~s/@.*//;
my $lang2=$lang3;
$lang2=~s/\..*//;
my $lang1=$lang2;
$lang1=~s/_.*//;

# what is $DESKTOP_FILE_PATH?

my %apps;
	my $ae=0;	# application exec
	my $ai=1;	# application icon
find(\&wanted,'/usr/share/applications/');

sub wanted {
	return if (!-f || /^\./ || !/\.desktop$/);
	my $file=$_;
	my $name="";
	my $exec="";
	my $icon="";
	my $cats="";
	my $term=0;
	my $lang=0;
	my $app=0;
	my $enc="iso-8859-1";
	open F_IN, "$file" or warn "$file: $!\n" and return;
	while(<F_IN>) {
		if (/^Name=(.+)/ && $lang<1) {
			$name=$1;
		}
		if (/^Name\[$lang1\]=(.+?)\s*$/ && $lang<2) {
			$name=$1;
			$lang=1;
		}
		if (/^Name\[$lang2\]=(.+?)\s*$/ && $lang<3) {
			$name=$1;
			$lang=2;
		}
		if (/^Name=\[$lang3\](.+?)\s*$/ && $lang<4) {
			$name=$1;
			$lang=3;
		}
		if (/^Name=\[$lang4\](.+?)\s*$/ && $lang<5) {
			$name=$1;
			$lang=4;
		}
		$icon=$1 if /^Icon=(.+?)\s*$/;
		$exec=$1 if /^Exec=(.+?)\s*$/;
		$cats=$1 if /^Categories=(.+?)\s*$/;
		$term=1 if /^Terminal=(?i:1|true)/;
		$app=1 if /^Type=(?i:application)/;
		$enc=$1 if /^Encoding=(.+?)\s*$/;
	}
	close F_IN;
	return unless $app;
	return if $term and not $o_xterm;
	my $bin=$exec;
		$bin=~s/(\S+).*/$1/;  #v---------v
	if($o_exec) {
		my $exists=0;
		foreach my $dir(@path,"") {	# would be "//dir/.../file" correct?
			if(-x "$dir/$bin") {#----^
				$exists=1;
				$exec="$dir/$exec" if $o_full;
				last;
			}
		}
		return unless $exists;
	}
	if($term) {
		$bin=~s|.*/||;
		$_=$o_xterm;
		s/%1/$bin/;
		s/%2/$exec/;
		$exec=$_;
	}
	if($o_icons) {
		my $exists=0;
		foreach my $dir("","$ENV{'HOME'}/.icons","/usr/share/icons","/usr/share/pixmaps") {
			$icon.=".xpm" if -f "$dir/$icon.xpm";
			$icon.=".png" if -f "$dir/$icon.png";
			if(-f "$dir/$icon") {
				$exists=1;
				$icon="$dir/$icon" if $o_full;
				last;
			}
		}
		$icon="" unless $exists;
	}
	my $utfname;
	if($name ne "") {
		if($enc eq "Legacy-Mixed") {
			warn "/usr/share/applications/$file: Legacy-Mixed encoding is depreciated.\n";
			if(!$lang) {
				$enc="iso-8859-1";
			} elsif ($lang==1) {
				warn "/usr/share/applications/$file: cannot get encoding name for `$lang1'. Assuming iso-8859-1\n";
				$enc="iso-8859-1";
			} else {
				use POSIX qw(locale_h);
				my $old_locale=setlocale(LC_ALL);
				setlocale(&POSIX::LC_ALL,$lang);
				eval {
					require I18N::Langinfo;
					I18N::Langinfo->import(qw(langinfo CODESET));
					$enc=langinfo(CODESET());
				};
				setlocale(&POSIX::LC_ALL,$old_locale);
			}
		}
		$utfname=decode($enc,$name);
		unless($utfname) {
			warn "/usr/share/applications/$file: wrong encoding!\n";
			$utfname=$name;
		}
	} else {
		$utfname=$file;
		$utfname=~s/\.desktop$//;
		warn "/usr/share/applications/$file: missing Name tag! using $utfname\n";
	}
	$exec=~s/([^%])%v/$1/g;
	$exec=~s/([^%])%k/$1$file/g;
	$exec=~s/([^%])%c/$1$utfname/g;
	my $iicon="";
	$iicon="--icon $icon" if $icon;
	$exec=~s/([^%])%i/$1$iicon/g;
	$exec=~s/%(?i:[fudn])//g;
	warn "Unknown Exec parameter variable: $1 in file $file, removing\n" if ($exec=~s/(%[^%\s])//);
	$exec=~s/%%/%/g;
	foreach my $cat(split(/;+/,$cats)) {
		#$apps{$cat}{$name}[$ae]="$exec";
		#$apps{$cat}{$name}[$ai]="$icon";
		$apps{$cat}{$utfname}=[$exec,$icon];	# two apps shouldn't have same names
	}
}

=for comment print apps
foreach my $category(sort keys %apps) {
	print "$category	\n";
	foreach my $name(sort keys %{$apps{$category}}) {
		#printf "	%s|%s|%s\n",$name,$apps{$category}{$name}[$ai],$apps{$category}{$name}[$ae];
		printf "	%s|%s|%s\n",$name,$apps{$category}{$name}[1],$apps{$category}{$name}[0];
	}
	print "\n";
}
=cut

# read XDG menu specification

my $file="";

open F_IN, "/etc/xdg/menus/applications.menu" or die "/etc/xdg/menus/applications.menu: $!\n";
while(<F_IN>) {
	#s/\s*//g;
	chop;
	$file.=$_;
}
close F_IN;

sub gettag {
	$file=~s/.*?<(.*?)>\s*//;
	$1;
}

sub getname {
	$file=~s/\s*(.*?)\s*<//;
	$file="<".$file;
	$1;
}

my @menu;
	my $ml=0;	# positive level: submenu
			# negative level: entry
	my $mi=1;
	my $me=2;
=for menustruct
	menu[x] {ble}	-1	ble.png	/usr/bin/ble
		{bla}	3	bla.xpm
		{foo}	5	foo.png
		{bar}	7	bar.xpm

test example:

$menu[0]{zadad}[0]=-1;
$menu[0]{zadad}[1]="foobar.png";
$menu[0]{zadad}[2]="/foo/bar/tralala";
$menu[0]{dad}[0]=1;
$menu[0]{dad}[1]="bar.png";
$menu[1]{dcd}[0]=-1;
$menu[1]{dcd}[1]="foo.png";
$menu[1]{dcd}[2]="/foo/tralala";
=cut

my $level=0;
my $mno=0;	# menu number
my $trigger=0;
my $tag;
my $name;
my $dir;
my @directory;
	my $dn=0;	# directory name
	my $di=1;	# directory icon
my $include;
my %tmpapps;
my @menustack;

sub cand {
	my %iapps;	# internal applications list
	foreach my $dir (keys %apps) {
	foreach my $name(keys %{$apps{$dir}}) {
		#$iapps{$dir}{$name}[$ai]=$apps{$dir}{$name}[$ai];
		#$iapps{$dir}{$name}[$ae]=$apps{$dir}{$name}[$ae];
		#$iapps{$dir}{$name}=[$apps{$dir}{$name}[0],$apps{$dir}{$name}[1]];
		$iapps{$dir}{$name}=$apps{$dir}{$name};
	}
	}
	while (1) {
		$tag=gettag();
		last if($tag=~/^\/and$/i);
		if($tag=~/^category$/i) {
			$dir=getname();
			foreach my $cat (keys %iapps) {
			foreach my $name(keys %{$iapps{$cat}}) {
				delete($iapps{$cat}{$name}) unless exists $iapps{$dir}{$name};
			}
			}
			gettag();	# must be </category> else GIGO and we don't care
			next;
		}
		if($tag=~/^(and|or|not)$/i) {
			my %tmpapps;
			%tmpapps=cand() if $tag=~/^and$/i;
			%tmpapps=cor() if $tag=~/^or$/i;
			%tmpapps=cnot() if $tag=~/^not$/i;
			foreach my $cat (keys %iapps) {
			foreach my $name(keys %{$iapps{$cat}}) {
				delete($iapps{$cat}{$name}) unless exists $tmpapps{$cat}{$name};
			}
			}
			next;
		}
		warn "warning: possible XDG file corruption!: $tag\n" if $o_verbose;
	}
	%iapps;
}

sub cor {
	my %iapps;	# internal applications list
	while (1) {
		$tag=gettag();
		last if($tag=~/^\/or$/i);
		if($tag=~/^category$/i) {
			$dir=getname();
			foreach my $name(keys %{$apps{$dir}}) {
				#$iapps{$dir}{$name}[$ai]=$apps{$dir}{$name}[$ai];
				#$iapps{$dir}{$name}[$ae]=$apps{$dir}{$name}[$ae];
				#$iapps{$dir}{$name}=[$apps{$dir}{$name}[0],$apps{$dir}{$name}[1]];
				$iapps{$dir}{$name}=$apps{$dir}{$name};
			}
			gettag();	# must be </category> else GIGO and we don't care
			next;
		}
		if($tag=~/^(and|or|not)$/i) {
			my %tmpapps;
			%tmpapps=cand() if $tag=~/^and$/i;
			%tmpapps=cor() if $tag=~/^or$/i;
			%tmpapps=cnot() if $tag=~/^not$/i;
			foreach my $cat (keys %tmpapps) {
			foreach my $name(keys %{$tmpapps{$cat}}) {
				#$iapps{$cat}{$name}[$ai]=$tmpapps{$cat}{$name}[$ai];
				#$iapps{$cat}{$name}[$ae]=$tmpapps{$cat}{$name}[$ae];
				#$iapps{$cat}{$name}=[$tmpapps{$cat}{$name}[0],$tmpapps{$cat}{$name}[1]];
				$iapps{$cat}{$name}=$tmpapps{$cat}{$name};
			}
			}
			next;
		}
		warn "warning: possible XDG file corruption!: $tag\n" if $o_verbose;
	}
	%iapps;
}

sub cnot {
	my %iapps;	# internal applications list
	foreach my $dir (keys %apps) {
	foreach my $name(keys %{$apps{$dir}}) {
		#$iapps{$dir}{$name}[$ai]=$apps{$dir}{$name}[$ai];
		#$iapps{$dir}{$name}[$ae]=$apps{$dir}{$name}[$ae];
		#$iapps{$dir}{$name}=[$apps{$dir}{$name}[0],$apps{$dir}{$name}[1]];
		$iapps{$dir}{$name}=$apps{$dir}{$name};
	}
	}
	while (1) {
		$tag=gettag();
		last if($tag=~/^\/not$/i);
		if($tag=~/^category$/i) {
			$dir=getname();
			foreach my $cat (keys %iapps) {
			next if $cat eq $dir;
			foreach my $name(keys %{$iapps{$cat}}) {
				delete($iapps{$cat}{$name}) if exists $iapps{$dir}{$name};
			}
			}
			delete($iapps{$dir});
			gettag();	# must be </category> else GIGO and we don't care
			next;
		}
		if($tag=~/^(and|or|not)$/i) {	# I think it doesn't make any sense
			warn "How did you get here!?\n" if $o_verbose;
			my %tmpapps;
			%tmpapps=cand() if $tag=~/^and$/i;
			%tmpapps=cor() if $tag=~/^or$/i;
			%tmpapps=cnot() if $tag=~/^not$/i;
			foreach my $cat (keys %iapps) {
			foreach my $name(keys %{$iapps{$cat}}) {
				delete($iapps{$cat}{$name}) if exists $tmpapps{$cat}{$name};
			}
			}
			next;
		warn "warning: possible XDG file corruption!: $tag\n" if $o_verbose;
		}
	}
	%iapps;
}

$#menu++;
while($trigger==0 || $level>0) {
	$tag=gettag();
	if($tag=~/^menu$/i) {
		$level++;
		$#menu++;
		$menustack[$#menustack+1]=$mno;
		$mno=$#menu;
		$name="";
		$include=0;
		$trigger=1;
		next;
	}
	if($tag=~/^\/menu$/i) {
		#for(my $i=0; $i<=$#menu; $i++) {
		my $i=$menustack[$#menustack];
		if(exists $directory[$level]) {
			foreach my $entry(keys %{$menu[$i]}) {
				#delete($menu[$i]{$entry}) if $menu[$i]{$entry}[$ml]==$mno;
				if($menu[$i]{$entry}[0]==$mno) {
					$directory[$level][0]=$entry unless $directory[$level][0];
					delete($menu[$i]{$entry});
				}
			}
			#$name=$directory[$level][$dn];
			#$menu[$i]{$name}[$ml]=$mno;
			#$menu[$i]{$name}[$mi]=$directory[$level][$di];
			$menu[$i]{$directory[$level][0]}=[$mno,$directory[$level][1]];
			delete($directory[$level]);
		}
		#}
		$mno=$i;
		$#menustack--;
		$level--;
		next;
	}
	if($tag=~/^name$/i) {
		$name=getname();
		#$menu[$menustack[$#menustack]]{$name}[$ml]=$mno;
		#$menu[$menustack[$#menustack]]{$name}[$mi]="";
		$menu[$menustack[$#menustack]]{" $name"}=[$mno,""];
		gettag();	# must be </name> else GIGO and we don't care
		next;
	}
	if($tag=~/^directory$/i) {
		$dir=getname();
		my $lang=0;
		my $name="";
		my $icon="";
		my $enc="iso-8859-1";
		if(open F_IN, "/usr/share/desktop-directories/$dir") {
	while(<F_IN>) {
		if (/^Name=(.+)/ && $lang<1) {
			$name=$1;
		}
		if (/^Name\[$lang1\]=(.+?)\s*$/ && $lang<2) {
			$name=$1;
			$lang=1;
		}
		if (/^Name\[$lang2\]=(.+?)\s*$/ && $lang<3) {
			$name=$1;
			$lang=2;
		}
		if (/^Name=\[$lang3\](.+?)\s*$/ && $lang<4) {
			$name=$1;
			$lang=3;
		}
		if (/^Name=\[$lang4\](.+?)\s*$/ && $lang<5) {
			$name=$1;
			$lang=4;
		}
		$icon=$1 if /^Icon=(.+?)\s*$/;
		$enc=$1 if /^Encoding=(.+?)\s*$/;
	}
	close F_IN;
	if($o_icons) {
		my $exists=0;
		foreach my $dir("$ENV{'HOME'}/.icons","/usr/share/icons","/usr/share/pixmaps") {
			$icon.=".xpm" if -f "$dir/$icon.xpm";
			$icon.=".png" if -f "$dir/$icon.png";
			if(-f "$dir/$icon") {
				$exists=1;
				$icon="$dir/$icon" if $o_full;
				last;
			}
		}
		$icon="" unless $exists;
	}
		if($name ne "") {
			#$directory[$level][$dn]=$name;
			#$directory[$level][$di]=$icon;
			my $utfname=decode($enc,$name);
			unless($utfname) {
				warn "/usr/share/desktop-directories/$dir: wrong encoding!\n";
				$utfname=$name;
			}
			$directory[$level]=[" $utfname",$icon];
		} else {
			warn "/usr/share/desktop-directories/$dir: missing Name tag!\n";
			$directory[$level]=["",$icon];
		}
		} else {
			warn "/usr/share/desktop-directories/$dir: $!\n";
		}
		gettag();	# must be </directory> else GIGO and we don't care
		next;
	}
	if($tag=~/^include$/i) {
		$include=1;
		next;
	}
	if($tag=~/^\/include$/i) {
		$include=0;
		next;
	}
	if(($tag=~/^(and|or|not)$/i)&& $include) {
		%tmpapps=cand() if $tag=~/^and$/i;
		%tmpapps=cor() if $tag=~/^or$/i;
		%tmpapps=cnot() if $tag=~/^not$/i;
		foreach my $dir (keys %tmpapps) {
		foreach my $name(keys %{$tmpapps{$dir}}) {
			#$menu[$mno]{$name}[$ml]=-1;
			#$menu[$mno]{$name}[$mi]=$tmpapps{$dir}{$name}[$ai];
			#$menu[$mno]{$name}[$me]=$tmpapps{$dir}{$name}[$ae];
			$menu[$mno]{$name}=[-1,$tmpapps{$dir}{$name}[1],$tmpapps{$dir}{$name}[0]];
		}
		}
		next;
	}
	if(($tag=~/^category$/i)&& $include) {
		$dir=getname();
		foreach my $name(keys %{$apps{$dir}}) {
			#$menu[$mno]{$name}[$ml]=-1;
			#$menu[$mno]{$name}[$mi]=$apps{$dir}{$name}[$ai];
			#$menu[$mno]{$name}[$me]=$apps{$dir}{$name}[$ae];
			$menu[$mno]{$name}=[-1,$apps{$dir}{$name}[1],$apps{$dir}{$name}[0]];
		}
		gettag();	# must be </category> else GIGO and we don't care
		next;
	}
	if($tag=~/^mergefile$/i) {
		$name=getname();
		gettag();	# must be </mergefile> else GIGO and we don't care
		my $tmpfile="";
		open F_IN, "/etc/xdg/menus/$name" or (
			warn("/etc/xdg/menus/$name: $!\n"),
			next
		);
		while(<F_IN>) {
			chop;
			$tmpfile.=$_;
		}
		close F_IN;
		$file=$tmpfile.$file;
		next;
	}
	if(($tag=~/^\/(name|directory|and|or|not|category|mergefile)$/i)&& $include) {
		warn "warning: XDG file corrupted!\n" if $o_verbose;
	}
	warn "Omitted tag: $tag\n" if $o_tags;
}

warn "Omitted ending: $file\n" if(($file ne "")&& $o_end);

if(($o_clear)&&(!$o_promote)) {
	my @empty;
	for(my $i=$#menu; $i>=0; $i--) {
		$empty[$i]=1;
		foreach my $entry(keys %{$menu[$i]}) {
			#if($menu[$i]{$entry}[$ml]<0) {}
			$mno=$menu[$i]{$entry}[0];
			if($mno<0) {
				$empty[$i]=0;
			} else {
				#if($empty[$menu[$i]{$entry}[$ml]]) {}
				if($empty[$mno]) {
					delete($menu[$i]{$entry});
				} else {
					$empty[$i]=0;
				}
			}
		}
	}
}

if($o_promote) {
	my @count;
	for(my $i=$#menu; $i>=0; $i--) {
		$count[$i]=0;
		foreach my $entry(keys %{$menu[$i]}) {
			$mno=$menu[$i]{$entry}[0];
			if($mno<0) {
				$count[$i]++;
			} else {
				if($count[$mno]==0) {
					delete($menu[$i]{$entry});
				} else {
					if($count[$mno]==1) {
						foreach my $name(keys %{$menu[$mno]}) {
							if($menu[$mno]{$name}[0]==-1) {
								$menu[$i]{$name}=[-1,"$menu[$mno]{$name}[1]","$menu[$mno]{$name}[2]"];
							} else {
								my $newname=$name;
								$newname=~s/^ //;
								$newname="$entry:$newname";
								$menu[$i]{$newname}=[$menu[$mno]{$name}[0],"$menu[$mno]{$name}[1]"];
								delete($menu[$i]{$name});
							}
						}
						delete($menu[$i]{$entry});
					}
					$count[$i]++;
				}
			}
		}
	}
}

$level="";
sub icewm {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		#next if $menu[$no]{$entry}[$ml]==0;
		print "$level";
		#if($menu[$no]{$entry}[$ml]<0) {
		$name=$entry;
		$name=~s/\"/\\\"/g;
		if($menu[$no]{$entry}[0]<0) {
			#print "Entry: \"$entry\" \"$menu[$no]{$entry}[$mi]\" $menu[$no]{$entry}[$me]\n";
			print "prog \"$name\" \"$menu[$no]{$entry}[1]\" $menu[$no]{$entry}[2]\n";
		} else {
			#$menu[$no]{$entry}[$mi]="folder" if $menu[$no]{$entry}[$mi] eq "";
			$menu[$no]{$entry}[1]="folder" if $menu[$no]{$entry}[1] eq "";
			#print "Menu: \"$entry\" \"$menu[$no]{$entry}[$mi]\"\n";
			$name=~s/^ //;
			print "menu \"$name\" \"$menu[$no]{$entry}[1]\" {\n";
			$level.="	";
			#outgen($menu[$no]{$entry}[$ml]);
			icewm($menu[$no]{$entry}[0]);
			$level=~s/	//;
			print "$level}\n";
		}
	}
}

sub blackbox {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$level";
		if($menu[$no]{$entry}[0]<0) {
			print "[exec] ($entry) {$menu[$no]{$entry}[2]}\n";
		} else {
			$name=$entry;
			$name=~s/^ //;
			print "[submenu] ($name)\n";
			$level.="	";
			blackbox($menu[$no]{$entry}[0]);
			$level=~s/	//;
			print "$level\[end]\n";
		}
	}
}

sub wmakerold {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$level\"";
		my $name=$entry;
		$name=~s/\"/\\\"/g;
		if($menu[$no]{$entry}[0]<0) {
			print "$name\" EXEC $menu[$no]{$entry}[2]\n";
		} else {
			$name=~s/^ //;
			print "$name\" MENU\n";
			$level.="	";
			wmakerold($menu[$no]{$entry}[0]);
			$level=~s/	//;
			print "$level\"$name\" END\n";
		}
	}
}

sub wmaker {
	my $no=$_[0];
	my $coma=$_[1];
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$coma\n$level(\"";
		$name=$entry;
		$name=~s/\"/\\\"/g;
		if($menu[$no]{$entry}[0]<0) {
			$menu[$no]{$entry}[2]=~s/\"/\\\"/g;
			print "$name\", EXEC, \"$menu[$no]{$entry}[2]\")";
		} else {
			$name=~s/^ //;
			print "$name\"";
			$level.="	";
			wmaker($menu[$no]{$entry}[0],",");
			$level=~s/	//;
			print "\n$level)";
		}
	}
}

sub xfce4 {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$level<";
		$name=$entry;
		$name=~s/&/&amp;/g;
		$name=~s/\"/&quot;/g;
		$name=~s/</&lt;/g;
		$name=~s/>/&gt;/g;
		if($menu[$no]{$entry}[0]<0) {
			$menu[$no]{$entry}[2]=~s/&/&amp;/g;
			$menu[$no]{$entry}[2]=~s/\"/&quot;/g;
			$menu[$no]{$entry}[2]=~s/</&lt;/g;
			$menu[$no]{$entry}[2]=~s/>/&gt;/g;
			print "app name=\"$name\" cmd=\"$menu[$no]{$entry}[2]\"/>\n";
		} else {
			$name=~s/^ //;
			print "menu name=\"$name\" visible=\"yes\">\n";
			$level.="	";
			xfce4($menu[$no]{$entry}[0]);
			$level=~s/	//;
			print "$level</menu>\n";
		}
	}
}

sub afterstep {
	my $no=$_[0];
	my $dir=$_[1];
	foreach my $entry(keys %{$menu[$no]}) {
		$name=$entry;
		if($menu[$no]{$entry}[0]<0) {
			$file=$entry;
			$file=~s/\s+/_/g;
			$file=~s|/||g;
			$name=~s/\"/\\\"/g;
			open F_OUT, ">> $dir/$file" or warn "$dir/$file: $!\n";
			print F_OUT "Exec \"$name\" exec $menu[$no]{$entry}[2]\n";
			print F_OUT "MiniPixmap \"$menu[$no]{$entry}[1]\"\n" if $menu[$no]{$entry}[1] ne "";
			#print F_OUT "MiniPixmap \"mini-app.xpm\"\n";
			close F_OUT;
		} else {
			$name=~s/^ //;
			$name=~s|/||g;
			mkpath("$dir/$name",0,0700);
			afterstep($menu[$no]{$entry}[0],"$dir/$name");
		}
	}
}

sub fvwm2 {
	my $no=$_[0];
	my $file;
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "AddToMenu $level	\"";
		$name=$entry;
		$name=~s/\"/\\\"/g;
		if($menu[$no]{$entry}[0]<0) {
			print         "$name\"	Exec $menu[$no]{$entry}[2] &\n";
			print "Style \"$name\"	Icon $menu[$no]{$entry}[1]\n" if $menu[$no]{$entry}[1] ne "";
		} else {
			$name=~s/^ //;
			$file=$name;
			$file=~s/\s+/_/g;
			$level.=".$file";
			print "$name\"	Popup $level\n\n";
			print "AddToMenu $level	\"$name\"	Title\n";
			fvwm2($menu[$no]{$entry}[0]);
			$level=~s/\.$file$//;
		}
	}
	print "\n";
}

sub olvwm {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$level";
		my $name=$entry;
		$name=~s/\"/\\\"/g;
		if($menu[$no]{$entry}[0]<0) {
			print "\"$name\"	exec $menu[$no]{$entry}[2]\n";
		} else {
			$name=~s/^ //;
			print "\"$name\" MENU\n";
			$level.="	";
			olvwm($menu[$no]{$entry}[0]);
			$level=~s/	//;
			print "$level\"$name\" END PIN\n";
		}
	}
}

sub qvwm {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$level";
		$name=$entry;
		$name=~s/\"/\\\"/g;
		if($menu[$no]{$entry}[0]<0) {
			$menu[$no]{$entry}[2]=~s/\"/\\\"/g;
			print "\"$name\" \"$menu[$no]{$entry}[1]\" \"$menu[$no]{$entry}[2]\"\n";
		} else {
			$name=~s/^ //;
			print "\"$name\" \"$menu[$no]{$entry}[1]\"\n$level+\n";
			$level.="	";
			qvwm($menu[$no]{$entry}[0]);
			$level=~s/	//;
			print "$level-\n";
		}
	}
}

sub aewm {
	my $no=$_[0];
	my $name;
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$level";
		$name=$entry;
		$name=~s/\"/\\\"/g;
		if($menu[$no]{$entry}[0]<0) {
			$menu[$no]{$entry}[2]=~s/\"/\\\"/g;
			print "cmd \"$name\" \"$menu[$no]{$entry}[2]\"\n";
		} else {
			$name=~s/^ //;
			print "menu \"$name\"\n";
			$level.="	";
			aewm($menu[$no]{$entry}[0]);
			$level=~s/	//;
			print "${level}end\n";
		}
	}
}

sub enlightenment {
	my $no=$_[0];
	my $F_OUT;
	open $F_OUT, ">> $dir/$_[1].menu" or warn "$dir/$_[1].menu: $!\n";
	print $F_OUT "\"$_[2]\"\n";
	foreach my $entry(sort keys %{$menu[$no]}) {
		$name=$entry;
		if($menu[$no]{$entry}[0]<0) {
			$name=~s/\"/\\\"/g;
			print $F_OUT "\"$name\" \"$menu[$no]{$entry}[1]\" exec \"$menu[$no]{$entry}[2]\"\n";
		} else {
			$name=~s/^ //;
			$name=~s|/||g;
			my $name2=$name;
			$name2=~s/\"/\\\"/g;
			print $F_OUT "\"$name2\" \"$menu[$no]{$entry}[1]\" menu \"$dir/$name2.menu\"\n";
			enlightenment($menu[$no]{$entry}[0],"$name","$name");
		}
	}
	close $F_OUT;
}

sub xpde {
	my $no=$_[0];
	my $dir=$_[1];
	foreach my $entry(keys %{$menu[$no]}) {
		$name=$entry;
		if($menu[$no]{$entry}[0]<0) {
			$file=$entry;
			$file=~s|/||g;
			$name=~s/\"/\\\"/g;
			open F_OUT, ">> $dir/$file.lnk" or warn "$dir/$file.lnk: $!\n";
			print F_OUT "[Shortcut]\nCaption=$name\nCommand=$menu[$no]{$entry}[2]\n";
			print F_OUT "Icon=$menu[$no]{$entry}[1]\n" if $menu[$no]{$entry}[1] ne "";
			close F_OUT;
		} else {
			$name=~s/^ //;
			$name=~s|/||g;
			mkpath("$dir/$name",0,0700);
			xpde($menu[$no]{$entry}[0],"$dir/$name");
		}
	}
}

$o_enc="utf-8" if ($o_utf and not $o_enc);
#use open OUT => ':utf8';
eval "use open OUT => ':locale'" unless $o_enc;
binmode(STDOUT, ":encoding($o_enc)") if $o_enc;

if($o_output eq "icewm") {
	icewm($o_strip);
	exit;
}
if($o_output eq "blackbox") {
	if(!$o_nomenu) {
		print "[begin] (Blackbox)\n";
	}
	blackbox($o_strip);
	if(!$o_nomenu) {
		print "[end]\n";
	}
	exit;
}
if($o_output eq "wmaker-old") {
	wmakerold($o_strip);
	exit;
}
if($o_output eq "wmaker") {
	if($o_strip) {
		wmaker(1,",");
	} else {
		wmaker(0,"");
	}
	exit;
}
if($o_output eq "xfce4") {
	if(!$o_nomenu) {
		print "<xfdesktop-menu>
		<title name=\"Desktop Menu\" visible=\"yes\"/>\n";
	}
	$level.="	";
	xfce4($o_strip);
	if(!$o_nomenu) {
		print "</xfdesktop-menu>\n";
	}
	exit;
}
if($o_output eq "afterstep") {
	use File::Path;
	my $dir="$ENV{'HOME'}/GNUstep/Library/AfterStep/start";
	if((-d "$dir")||(-f "$dir")) {
		rmtree("$dir.old",0,0);
		rename("$dir","$dir.old");
		mkpath("$dir",0,0700);
		#open F_OUT, ">> $dir/\.include" or warn "$dir/.include: $!\n";
		#print F_OUT "keepname\ninclude \"../start.old\"\n";
		#close F_OUT;
	} else {
		mkpath("$dir",0,0700);
	}
	afterstep($o_strip,$dir);
	exit;
}
if($o_output eq "fluxbox") {	# This is the same as blackbox (I hope so)
	if(!$o_nomenu) {
		print "[begin] (Fluxbox)\n";
	}
	blackbox($o_strip);
	if(!$o_nomenu) {
		print "[end]\n";
	}
	exit;
}
if($o_output eq "fvwm2") {
	$level="fvwm2";
	fvwm2($o_strip);
	exit;
}
if($o_output eq "olvwm") {
	olvwm($o_strip);
	exit;
}
if($o_output eq "qvwm") {
	print "[StartMenu]\n";
	qvwm($o_strip);
	exit;
}
if($o_output eq "aewm") {
	aewm($o_strip);
	exit;
}
if($o_output eq "enlightenment") {
	use File::Path;
	$dir="$ENV{'HOME'}/.enlightenment/menus";
	if((-d "$dir")||(-f "$dir")) {
		rmtree("$dir.old",0,0);
		rename("$dir","$dir.old");
	}
	mkpath("$dir",0,0700);	# or die
	enlightenment($o_strip,"index","Enlightenment");
	exit;
}
if($o_output eq "xpde") {
	use File::Path;
	my $dir="$ENV{'HOME'}/.xpde/Start Menu/Programs";
	if((-d "$dir")||(-f "$dir")) {
		rmtree("$dir.old",0,0);
		rename("$dir","$dir.old");
	}
	mkpath("$dir",0,0700);
	xpde($o_strip,$dir);
	exit;
}
