#!/usr/bin/perl -w
#$Id: vfmg,v 1.83 2005/03/15 19:01:25 sparky 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_enc="";
my $o_xterm="xterm -name xterm-%1 -e %2";
my $o_nomenu=0;

$o_xterm="$ENV{'VFMG_TERM'}" if $ENV{'VFMG_TERM'};

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, fbpanel, fluxbox, fvwm, fvwm2, icewm, metisse, openbox, 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 blackbox, fluxbox, openbox, xfce4)
	-o, --output=	- output in given encoding (e.g. iso-8859-2)
	-r, --xterm=	- set x terminal application (default \"xterm -name xterm-%1 -e %2\")
				example \"gnome-terminal -t Terminal.%1 -x %2\"\n";
	exit;
}

die "Missing argument {aewm, afterstep, blackbox, enlightenment, fbpanel, fluxbox, fvwm, fvwm2, icewm, metisse, openbox, 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|fbpanel|fluxbox|fvwm|fvwm2|icewm|metisse|openbox|olvwm|qvwm|wmaker|wmaker-old|xfce4|xpde)$/;

$o_full=1 if $o_icons and $o_output=~/^(enlightenment|metisse)$/;
	
my $tmp="$ENV{'HOME'}/.local/share";
$tmp="$ENV{'XDG_DATA_HOME'}" if $ENV{'XDG_DATA_HOME'};
my @tmp=("/usr/local/share","/usr/share");
@tmp=split(/:+/,$ENV{'XDG_DATA_DIRS'}) if $ENV{'XDG_DATA_DIRS'};
my @xdg_data_dirs=grep -d, $tmp,@tmp;

#$tmp="$ENV{'HOME'}/.config";
$tmp="$ENV{'XDG_CONFIG_HOME'}" if $ENV{'XDG_CONFIG_HOME'};
@tmp=("/etc/xdg");
@tmp=split(/:+/,$ENV{'XDG_CONFIG_DIRS'}) if $ENV{'XDG_CONFIG_DIRS'};
my @xdg_config_dirs=map{"$_/menus"} grep -d, $tmp,@tmp;

@tmp=map {"$_/icons/"} @xdg_data_dirs;
my @icondirs=(grep (-d, ("$ENV{'HOME'}/.icons/",@tmp,"/usr/share/pixmaps/","/usr/share/icons/default.kde/48x48/apps/","/usr/share/icons/hicolor/48x48/apps/")), "");

my @path;
if($o_exec) {
	@path=(grep (-d, split(/:+/,$ENV{'PATH'})), "");
	if($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;	# $apps{name}{category}=[icon,exec]
my @happs;
find(\&wanted, grep -d, map {"$_/applications"} @xdg_data_dirs);

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::Find::name: $!\n" and return;
	while(<F_IN>) {
		if (/^\s*Name\s*=\s*(.+?)\s*$/ && $lang<1) {
			$name=$1;
		}
		if (/^\s*Name\[$lang1\]\s*=\s*(.+?)\s*$/ && $lang<2) {
			$name=$1;
			$lang=1;
		}
		if (/^\s*Name\[$lang2\]\s*=\s*(.+?)\s*$/ && $lang<3) {
			$name=$1;
			$lang=2;
		}
		if (/^\s*Name\[$lang3\]\s*=\s*(.+?)\s*$/ && $lang<4) {
			$name=$1;
			$lang=3;
		}
		if (/^\s*Name\[$lang4\]\s*=\s*(.+?)\s*$/ && $lang<5) {
			$name=$1;
			$lang=4;
		}
		$icon=$1 if /^\s*Icon\s*=\s*(.+?)\s*$/;
		$exec=$1 if /^\s*Exec\s*=\s*(.+?)\s*$/;
		$cats=$1 if /^\s*Categories\s*=\s*(.+?)\s*$/;
		$term=1 if /^\s*Terminal\s*=\s*(?i:1|true)/;
		$app=1 if /^\s*Type\s*=\s*(?i:application)/;
		$enc=$1 if /^\s*Encoding\s*=\s*(.+?)\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(@icondirs) {
			if(-f "$dir$icon") {
			} elsif(-f "$dir$icon.svg") {
				$icon.=".svg";
			} elsif(-f "$dir$icon.xpm") {
				$icon.=".xpm";
			} elsif(-f "$dir$icon.png") {
				$icon.=".png";
			} else {
				next;
			}
			$exists=1;
			$icon="$dir$icon" if $o_full;
			last;
		}
		$icon="" unless $exists;
	}
	my $utfname;
	if($name ne "") {
		if($enc eq "Legacy-Mixed") {
			warn "$File::Find::name: Legacy-Mixed encoding is depreciated.\n";
			if(!$lang) {
				$enc="iso-8859-1";
			} elsif ($lang==1) {
				warn "$File::Find::name: 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 "$File::Find::name: wrong encoding!\n";
			$utfname=$name;
		}
	} else {
		$utfname=$file;
		$utfname=~s/\.desktop$//;
		warn "$File::Find::name: 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::Find::name, removing\n" if ($exec=~s/(%[^%\s])//);
	$exec=~s/%%/%/g;
	push @happs, [$utfname,$icon,$exec];
	foreach my $cat(split(/;+/,$cats)) {
		$apps[$#happs]{$cat}=1;	# two apps can have same names now:)
	}
}

# read XDG menu specification

my $file="";

foreach $tmp (@xdg_config_dirs) {
	open F_IN, "$tmp/applications.menu" or next;
	while(<F_IN>) {
		s/\s+//g;
		$file.=$_;
	}
	close F_IN;
	goto OK;
}
warn "No readable applications.menu in:\n";
foreach (@xdg_config_dirs) { warn "- $_\n" };
die "Last error: $!\n";

OK:;

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

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

my @menu;	# $menu[parent]{name}=[x=menu number,icon]
		# $menu[parent]{name}=[-1=entry,icon,exec]
my $level=0;
my $mno=0;	# menu number
my $trigger=0;
my $tag;
my $name;
my $dir;
my $icon_dir;
my @directory;	# $directory[level]=[name,icon]
my $include;
my $tmpapps;
my @menustack;

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

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

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

@tmp=grep -d, map {"$_/desktop-directories/"} @xdg_data_dirs;
$#menu++;
while($trigger==0 || $level>0) {
	$tag=lc(gettag());
	if(($tag eq 'category')&& $include) {
		$dir=getname();
		foreach my $name(0..$#happs) {
			$menu[$mno]{$happs[$name][0]}=[-1,$happs[$name][1],$happs[$name][2]]
				if exists $apps[$name]{$dir};
		}
		gettag();	# must be </category> else GIGO and we don't care
		next;
	}
	if(($tag=~/^(and|or|not)$/)&& $include) {
		if ($tag eq 'or') {
			$tmpapps=cor();
		} elsif ($tag eq 'and') {
			$tmpapps=cand();
		} else {
			$tmpapps=cnot();
		}
		foreach my $name(0..$#$tmpapps) {
			next unless defined $tmpapps->[$name];
		foreach my $dir (keys %{$apps[$name]}) {
			$menu[$mno]{$happs[$name][0]}=[-1,$happs[$name][1],$happs[$name][2]];
		}
		}
		next;
	}
	if($tag eq 'menu') {
		$level++;
		$#menu++;
		$menustack[$#menustack+1]=$mno;
		$mno=$#menu;
		$name="";
		$include=0;
		$trigger=1;
		next;
	}
	if($tag eq '/menu') {
		#for(my $i=0; $i<=$#menu; $i++) {
		my $i=$menustack[$#menustack];
		if(exists $directory[$level]) {
			foreach my $entry(keys %{$menu[$i]}) {
				if($menu[$i]{$entry}[0]==$mno) {
					$directory[$level][0]=$entry unless $directory[$level][0];
					delete($menu[$i]{$entry});
				}
			}
			$menu[$i]{$directory[$level][0]}=[$mno,$directory[$level][1]];
			delete($directory[$level]);
		}
		#}
		$mno=$i;
		$#menustack--;
		$level--;
		next;
	}
	if($tag eq 'include') {
		$include=1;
		next;
	}
	if($tag eq '/include') {
		$include=0;
		next;
	}
	if($tag eq 'name') {
		$name=getname();
		$menu[$menustack[$#menustack]]{" $name"}=[$mno,""];
		gettag();	# must be </name> else GIGO and we don't care
		next;
	}
	if($tag eq 'directory') {
		$dir=getname();
		my $lang=0;
		my $name="";
		my $icon="";
		my $enc="iso-8859-1";
		my $ok=0;
		foreach $tmp(@tmp) {
			open F_IN, "$tmp$dir" or next;
			while(<F_IN>) {
				if (/^\s*Name\s*=\s*(.+?)\s*$/ && $lang<1) {
					$name=$1;
				}
				if (/^\s*Name\[$lang1\]\s*=\s*(.+?)\s*$/ && $lang<2) {
					$name=$1;
					$lang=1;
				}
				if (/^\s*Name\[$lang2\]\s*=\s*(.+?)\s*$/ && $lang<3) {
					$name=$1;
					$lang=2;
				}
				if (/^\s*Name\[$lang3\]\s*=\s*(.+?)\s*$/ && $lang<4) {
					$name=$1;
					$lang=3;
				}
				if (/^\s*Name\[$lang4\]\s*=\s*(.+?)\s*$/ && $lang<5) {
					$name=$1;
					$lang=4;
				}
				$icon=$1 if /^\s*Icon\s*=\s*(.+?)\s*$/;
				$enc=$1 if /^\s*Encoding\s*=\s*(.+?)\s*$/;
			}
			close F_IN;
			if($o_icons) {
				my $exists=0;
				foreach my $dir(@icondirs) {
					if(-f "$dir$icon") {
					} elsif(-f "$dir$icon.xpm") {
						$icon.=".xpm";
					} elsif(-f "$dir$icon.png") {
						$icon.=".png";
					} else {
						next;
					}
					$exists=1;
					$icon="$dir$icon" if $o_full;
					last;
				}
				$icon="" unless $exists;
			}
			if($name ne "") {
				my $utfname=decode($enc,$name);
				unless($utfname) {
					warn "$tmp$dir: wrong encoding!\n";
					$utfname=$name;
				}
				$directory[$level]=[" $utfname",$icon];
			} else {
				warn "$tmp$dir: missing Name tag!\n";
				$directory[$level]=["",$icon];
			}
			$ok=1; last;
		}
		unless($ok) {
			warn "No readable $dir in:\n";
			foreach $tmp(@tmp) { warn "- $tmp\n" };
			warn "Last error: $!\n";
		}
		gettag();	# must be </directory> else GIGO and we don't care
		next;
	}
	if($tag eq 'mergefile') {
		$name=getname();
		gettag();	# must be </mergefile> else GIGO and we don't care
		my $ok="";
		foreach $tmp(@xdg_config_dirs, "") {
			open F_IN, "$tmp/$name" or next;
			while(<F_IN>) {
				s/\s+//g;
				$ok.=$_;
			}
			close F_IN;
			$file=$ok.$file;
			last;
		}
		unless($ok) {
			warn "No readable $name in:\n";
			foreach $tmp(@xdg_config_dirs) { warn "- $tmp\n" };
			warn "Last error: $!\n";
		}
		next;
	}
	if(($tag=~/^\/(name|directory|and|or|not|category|mergefile)$/)&& $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]}) {
			$mno=$menu[$i]{$entry}[0];
			if($mno<0) {
				$empty[$i]=0;
			} else {
				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=substr($newname,1);
								$newname="$entry:$newname";
								$menu[$i]{$newname}=[$menu[$mno]{$name}[0],"$menu[$mno]{$name}[1]"];
								delete($menu[$i]{$name});
							}
						}
						delete($menu[$i]{$entry});
					}
					$count[$i]++;
				}
			}
		}
	}
}

my $DoConvert = `which convert`;
sub scale_icon {
	my $icon_in = $_[0];

	return $icon_in unless $DoConvert;

	my $icon_out = $icon_in;
	my $icon_ext = $_[1];
	my $convert_options = $_[2];

	$icon_out =~ s/^.*\///;
	$icon_out =~ s/\..*$//;
	$icon_out = "$icon_dir/$icon_out.$icon_ext";
	if( ! -f "$icon_out") {
		system("convert $convert_options $icon_in $icon_out");
	}
	return $icon_out;
}

$level="";
sub icewm {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		#next if $menu[$no]{$entry}[0]==0;
		print "$level";
		#print "$no $menu[$no]{$entry}[0] $entry\n";
		$name=$entry;
		$name=~s/\"/\\\"/g;
		if($menu[$no]{$entry}[0]<0) {
			print "prog \"$name\" \"$menu[$no]{$entry}[1]\" $menu[$no]{$entry}[2]\n";
		} else {
			$menu[$no]{$entry}[1]="folder" if $menu[$no]{$entry}[1] eq "";
			$name=substr($name,1);
			print "menu \"$name\" \"$menu[$no]{$entry}[1]\" {\n";
			$level.="	";
			icewm($menu[$no]{$entry}[0]);
			$level=substr($level,1);
			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=substr($name,1);
			print "[submenu] ($name)\n";
			$level.="	";
			blackbox($menu[$no]{$entry}[0]);
			$level=substr($level,1);
			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=substr($name,1);
			print "$name\" MENU\n";
			$level.="	";
			wmakerold($menu[$no]{$entry}[0]);
			$level=substr($level,1);
			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=substr($name,1);
			print "$name\"";
			$level.="	";
			wmaker($menu[$no]{$entry}[0],",");
			$level=substr($level,1);
			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]\" icon=\"$menu[$no]{$entry}[1]\"/>\n";
		} else {
			$name=substr($name,1);
			print "menu name=\"$name\" icon=\"$menu[$no]{$entry}[1]\" visible=\"yes\">\n";
			$level.="	";
			xfce4($menu[$no]{$entry}[0]);
			$level=substr($level,1);
			print "$level</menu>\n";
		}
	}
}

sub afterstep {
	my $no=$_[0];
	my $dir=$_[1];
	print "dir=".$dir;
	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=substr($name,1);
			$name=~s|/||g;
			mkpath("$dir/$name",0,0700);
			afterstep($menu[$no]{$entry}[0],"$dir/$name");
		}
	}
}

sub fvwm {
	my $no=$_[0];
	my $file=$_[1];
	$file=~s|^\.||;
	my $basename=$_[2];
	foreach my $entry(keys %{$menu[$no]}) {
		if($menu[$no]{$entry}[0]>=0) {
			$name=substr($entry,1);
			$name=~s|/||g;
			my $menutitle=$name;
			$name=~s/\s+/_/g;
			fvwm($menu[$no]{$entry}[0],"$file.$name","$menutitle");
		}
	}
	if (!$file) {
		return;
	}
	print "Popup \"$file\"\n";
	print "\tTitle \"$basename\"\n"; 
	foreach my $entry(keys %{$menu[$no]}) {
		if($menu[$no]{$entry}[0]<0) {
			$name=$entry;
			$name=~s/\"/\\\"/g;
			print "\tExec \"$name\"\texec $menu[$no]{$entry}[2]\n";
		} else { 
			$name=substr($entry,1);
			$name=~s|/||g;
			my $menutitle=$name;
			$name=~s/\s+/_/g;
			print "\tPopup \"$menutitle\"\t$file.$name\n";
		}
	}
	print "EndPopup\n\n";
}

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=substr($name,1);
			$file=$name;
			$file=~s/\s+/_/g;
			$level.=".$file";
			print "$name\"	Popup $level\n";
			print "DestroyMenu recreate $level\n\n";
			print "AddToMenu $level	\"$name\"	Title\n";
			fvwm2($menu[$no]{$entry}[0]);
			$level=~s/\.$file$//;
		}
	}
	print "\n";
}

sub metisse {
	my $no=$_[0];
	my $file=$_[1];
	$file=~s|^\.||;
	my $basename=$_[2];
	my $icon="";
	foreach my $entry(keys %{$menu[$no]}) {
		if($menu[$no]{$entry}[0]>=0) {
			$name=substr($entry,1);
			$name=~s|/||g;
			my $menutitle=$name;
			$name=~s/\s+/_/g;
		      metisse($menu[$no]{$entry}[0],"$file.$name","$menutitle");
		}
	}
	if (!$file) {
		return;
	}
	print "DestroyMenu $file\n";
	print "AddToMenu $file \"$basename\" Title\n";
	foreach my $entry(sort keys %{$menu[$no]}) {
		$icon=$menu[$no]{$entry}[1] if $o_icons;
		$icon=scale_icon($icon,"png","-geometry 21x18") if $icon;
		$icon="\%$icon\%" if $icon;
		
		if($menu[$no]{$entry}[0]<0) {
			$name=$entry;
			$name=~s/\"/\\\"/g;
			print "+ \"$icon$name\"\tExec exec $menu[$no]{$entry}[2]\n";
		} else {
			$name=substr($entry,1);
			$name=~s|/||g;
			my $menutitle=$name;
			$name=~s/\s+/_/g;
			print "+ \"$icon$menutitle\"\tPopup\t$file.$name\n";
		}
	}
	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=substr($name,1);
			print "\"$name\" MENU\n";
			$level.="	";
			olvwm($menu[$no]{$entry}[0]);
			$level=substr($level,1);
			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=substr($name,1);
			print "\"$name\" \"$menu[$no]{$entry}[1]\"\n$level+\n";
			$level.="	";
			qvwm($menu[$no]{$entry}[0]);
			$level=substr($level,1);
			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=substr($name,1);
			print "menu \"$name\"\n";
			$level.="	";
			aewm($menu[$no]{$entry}[0]);
			$level=substr($level,1);
			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=encode($o_enc,$entry);
		my $icon="";
		$icon=$menu[$no]{$entry}[1] if $o_icons;
		$icon=scale_icon($icon,"png","-geometry 18x18") if $icon;
		if($menu[$no]{$entry}[0]<0) {
			$name=~s/\"/\'/g;
			print $F_OUT "\"$name\" \"$icon\" exec \"$menu[$no]{$entry}[2]\"\n";
		} else {
			$name=substr($name,1);
			$name=~s|/||g;
			my $name2=$name;
			$name2=~s/\"/\'/g;
			print $F_OUT "\"$name2\" \"$icon\" 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=substr($name,1);
			$name=~s|/||g;
			mkpath("$dir/$name",0,0700);
			xpde($menu[$no]{$entry}[0],"$dir/$name");
		}
	}
}

sub fbpanel {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$level";
		if($menu[$no]{$entry}[0]<0) {
			print "item {\n";
			print "${level}	name = $entry\n";
			print "${level}	action = $menu[$no]{$entry}[2]\n";
			print "${level}	image = $menu[$no]{$entry}[1]\n";
		} else {
			$name=$entry;
			$name=substr($name,1);
			print "menu {\n";
			print "${level}	name = $name\n";
			print "${level}	image = $menu[$no]{$entry}[1]\n";
			$level.="	";
			fbpanel($menu[$no]{$entry}[0]);
			$level=substr($level,1);
		}
		print "${level}}\n";
	}
}

sub openbox {
	my $no=$_[0];
	foreach my $entry(sort keys %{$menu[$no]}) {
		print "$level";
		if($menu[$no]{$entry}[0]<0) {
			print "<item label=\"$entry\">\n";
			print "${level}	<action name=\"Execute\">\n";
			print "${level}	 <execute>$menu[$no]{$entry}[2]</execute>\n";
			print "${level}	</action>\n";
			print "${level}</item>\n";
		} else {
			$name=$entry;
			$name=substr($name,1);
			print "<menu id=\"$name\" label=\"$name\">\n";
			$level.="	";
			openbox($menu[$no]{$entry}[0]);
			$level=substr($level,1);
			print "$level</menu>\n";
		}
	}
}

$o_enc||="utf-8" if $o_utf;
#use open OUT => ':utf8';
#eval "use open OUT => ':locale'" unless $o_enc;	# $o_enc always set
unless ($o_enc) {
	require I18N::Langinfo;
	I18N::Langinfo->import(qw(langinfo CODESET));
	$o_enc=langinfo(CODESET()) unless $o_enc;
}
binmode STDOUT, ":encoding($o_enc)";

if($o_output eq "icewm") {
	icewm($o_strip);
	exit;
}
if($o_output eq "blackbox") {
	print "[begin] (Blackbox)\n" unless $o_nomenu;
	blackbox($o_strip);
	print "[end]\n" unless $o_nomenu;
	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") {
	print "<xfdesktop-menu>\n";
	print "<title name=\"Desktop Menu\" visible=\"yes\"/>\n" unless $o_nomenu;
	$level.="	";
	xfce4($o_strip);
	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 "fvwm") {
	fvwm($o_strip,"", "fvwm");
	exit;
}
if($o_output eq "fluxbox") {	# This is the same as blackbox (I hope so)
	print "[begin] (Fluxbox)\n" unless $o_nomenu;
	blackbox($o_strip);
	print "[end]\n" unless $o_nomenu;
	exit;
}
if($o_output eq "fvwm2") {
	$level="fvwm2";
	fvwm2($o_strip);
	exit;
}

if($o_output eq "metisse") {
	$icon_dir="$ENV{'HOME'}/.fvwm-metisse/icons";
	if(! -d "$icon_dir") {
		if(-f "$icon_dir") {
			rename("$icon_dir","$icon_dir.old");
		}
		mkpath("$icon_dir",0,0700);
	}
	metisse($o_strip,"", "metisse");
	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
	$icon_dir="$ENV{'HOME'}/.enlightenment/icons";
	if(! -d "$icon_dir") {
		if(-f "$icon_dir") {
			rename("$icon_dir","$icon_dir.old");
		}
		mkpath("$icon_dir",0,0700);
	}
	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;
}
if($o_output eq "fbpanel") {
	fbpanel($o_strip);
	exit;
}
if($o_output eq "openbox") {
	print "<openbox_menu>\n" unless $o_nomenu;
	print "<menu id=\"root-menu\" label=\"PLD Linux\">\n" unless $o_nomenu;
	openbox($o_strip);
	print "</menu>\n" unless $o_nomenu;
	print "</openbox_menu>\n" unless $o_nomenu;
	exit;
}
