#!/usr/bin/perl
#
# This creates, dynamically, menus for fvwm95.  It lists files, mostly.
# $Id: fvwm95.find,v 1.3 1997/07/12 04:32:32 ryo2 Exp ryo2 $

# Lines per menu
$MenuLines = 28;

# Include directory tree in the first menu.
# Define following line &YES if the directory tree crash fvwm95
$IncludeTree = &YES;

# Default file viewer. %s will be replaced by filename.
$viewer = 'xterm -sb -ls -T %s -e less %s';

# Start directory
$curr = $ENV{'HOME'};

# Files to ignore
@ignore = ('*.o', '#*', '*~');

# File name of .mime.types (similar format to xclass-exploror)
$mimetypes = ".smime.types";

# The rule to sort by --- Lexico, DirFirst, BySuffix
$rule = 'DirFirst';

# Default editor
unless ($editor = $ENV{'EDITOR'}) {
    $editor = 'xterm -sb -ls -T %s -e vi';
}
$editor .= " %s";
$usemime = &YES;
$debug = &NO;

$FileScript = $0;
# If this script is placed in the executable path,
# use following line instead.
#($FileScript) = ($0 =~ /([^\/]+)$/);

OPT:
while (@ARGV > 0) {
    $_ = shift;
    /^-d/ && ($debug = &YES, next);
    /^-l[\s\t]*(\S)/ && do {$1 =~ /([a-zA-OQ-Z])/ && ($fopt ="-$1"); next};
    /^-v[\s\t]*(\S*)/ && do {$1 =~ /edit/ && ($usemime =&NO); next};
    /^-n(\S*)/ && do {$1 && push(@primaries, "-name '$1'"); next};
    /^-c\s*(\S*)/ && do {$1 && push(@primaries, "-ctime '$1'"); next};
    /^-m\s*(\S*)/ && do {$1 && push(@primaries, "-mtime '$1'"); next};
    /^-a\s*(\S*)/ && do {$1 && push(@primaries, "-atime '$1'"); next};
    /^-u\s*(\S*)/ && do {$1 && push(@primaries, "-user $1"); next};
    /^-g\s*(\S*)/ && do {$1 && push(@primaries, "-group $1"); next};
    /^-p\s*(\S*)/ && do {$1 && push(@primaries, "-perm '$1'"); next};
    /^-s\s*(\S*)/ && do {$1 && push(@primaries, "-size '$1'"); next};
    /^-e\s*(\S*)/ && do {$1 && push(@primaries, "-newer '$1'"); next};
    /^-t[\s\t]*(\S*)/ && do {$1 !~ /a/ && push(@primaries,"-type '$1'"); next};
    /^-U[\s\t]*(\S*)/ && do {$1 && push(@primaries, "-nouser"); next};
    /^-G[\s\t]*(\S*)/ && do {$1 && push(@primaries, "-nogroup"); next};
    $curr = $_; $curr =~ s,/$,,; last;
}

# Default search directory is HOME not to search all the file system.
$curr || ($curr = $ENV{'HOME'});
$findopt_exist = join("", @primaries);
$allopt = "$fopt $curr ". join(" ", @primaries);

$debug && $findopt_exist && do {
    $debstr = "( find $allopt )"; $debstr =~ s/\s+(\S)/ $1/g;
};

# Read mime.types if it exists.
open(INI, "$ENV{'HOME'}/$mimetypes") && &readMimeTypes;

if ($findopt_exist) {
    $_ = `find $allopt -print`;
    @fullpaths = split(/\n/);
} else {
    # Get a file list.
    while (<$curr/*>) { push(@fullpaths, $_); }
    # Comment following line to exclude dot files.
    while (<$curr/.*>) { /^$curr\/\.+$/ || push(@fullpaths, $_); }
}
foreach (@ignore) { s/\./\\\./; s/\*/\.\*/; };

 LOOP: foreach (@fullpaths) {
     s/^$curr\///;
     -b "$curr/$_" && next;
     foreach $i (@ignore) {
	 /^.$i$/ && next LOOP;
     }
     ($head) = ($_ =~ /^(.)/);
     # Index whether it's a directory or not.
     $ftype{$_} = ((-d _) ? "1" : "2") . $head;
     # Index of Suffixes
     if ($head eq '.'){
	 # Move dot files last in case of sorting BySuffixes
	 $head = 'zz'; $ext = 'z' x 5; $trunk = $_;
     } else {
	 unless (($trunk, $ext) = (/(.*)\.([^\.]+)$/)) { $trunk = $_; }
     }
     $fsuf{$_} = "$ext$head$ftype{$_}$trunk";
 }
$MoreCount = $count = 0;

# Re-start a "Files" menu.
$buf = "\nDestroyMenu \"Files\"\n";
$buf .= "AddToMenu Files \\\n";
# Title shows the current directory that works as "cd ../".
$topdir = ($curr)? $curr : '/';
$buf .= "  \"$topdir$debstr%mini-lower.xpm%\" PipeRead \"$FileScript ".
    &dirname($curr) . "\"\n+ \"\" Nop\n";

# Create directory tree from current path
# Define
$dircount = $#compdir +1;
$buf_tree = ($IncludeTree)? ""
    : "\nDestroyMenu \"DirTree\"\nAddToMenu DirTree \\\n".
    " \"Higher Directory\" Nop\n" . "+ \"\" Nop\n";
while (@compdir > 0) {
    $full = join('/', @compdir);
    $i = pop(@compdir);
    $full || last;
    $buf_tree .= "+ \"$i %mini-ofolder.xpm%\" ".
	"PipeRead \"$FileScript  '$full'\"\n";
}
$buf_tree .= "+ \"/ %mini-ofolder.xpm%\" PipeRead \"$FileScript  '/'\"\n";
if ($IncludeTree) {
    $buf .= "$buf_tree\n\+ \"\" Nop\n"; $count += $dircount;
    $buf_tree = "";
} else {
    $buf .= "+ \"Jump To...%mini-ofolder.xpm%\" Popup DirTree\n";
    $count++;
}

# Show the list.
foreach $file (sort $rule keys %ftype) {
    # Make a new menu every $MenuLines entries or so...
    $count++;
    if ($count == $MenuLines) {
	# If too many entries, make a new menu.
	$MoreCount++;
	$buf .= "+ More... Popup More$MoreCount\n\n";
	$buf .= "DestroyMenu \"More$MoreCount\"\n";
	$buf .= "AddToMenu More$MoreCount \"More($MoreCount)...\"    Nop\n";
	$buf .= "+ \"\" Nop\n";
	$count = 1;
    }
    if (-d "$curr/$file") {
	$buf .= "+ \"$file%mini-folder.xpm%\" PipeRead \"$FileScript \'$curr/$file\'\"\n";
    } else {
	if (-x "$curr/$file") {
	    # Executable files have "exec" as their menu command.
	    $tmp = "+ \"$file%" . (($usemime)?
		("mini-run.xpm%\" Exec $curr/$file\n")
		: ("mini-edit.xpm%\" Exec ". &edits("$curr/$file") ." &\n"));
	} else {
	    # Everything else should be viewed by the default viewer.
	    $viewer_tmp = $viewer; $viewer_tmp =~ s,%s,$curr/$file,g;
	    $tmp = "+ \"$file%". (($usemime)?
		("mini-doc.xpm%\" Exec $viewer_tmp&\n")
		: ("mini-edit.xpm%\" Exec ". &edits("$curr/$file") ." &\n"));
	}
	$usemime && do {
	# Execute viewer written in mime.types
	    foreach $i (keys %ACT) {
		if ($file =~ /^$i$/) {
		    $act = $ACT{$i}; $act =~ s,%s,$curr/$file,g;
		    $tmp = "+ \"$file%$ICON{$i}%\" Exec $act &\n";
		    last;
		}
	    }
	};
	$buf .= $tmp;
    }
}
$| = 1;
print $buf_tree . $buf;
exit 0;

sub readMimeTypes {
    local(@each);
    while (<INI>) {
	/^\s*$/ && do {undef $s_pat; undef $s_icon; undef $s_act;};
	/^\s*pattern[\s\t]*=[\s\t]*(\S.*)$/i && ($s_pat = $1);
	/^\s*icon[\s\t]*=[\s\t]*(\S.*)$/i && ($s_icon = $1);
	/^\s*action[\s\t]*=[\s\t]*(\S.*)$/i && ($s_act = $1);
	$s_pat && $s_icon && $s_act && do {
	    @each = split(/[\s\t]+/, $s_pat);
	    foreach $i (@each) {
		$i =~ s/\./\\\./; $i =~ s/\*/\.\*/;
		$ICON{$i} = $s_icon; $ACT{$i} = $s_act;
	    }
	};
    }
}

sub DirFirst {
    $ftype{$a} cmp $ftype{$b};
}

sub BySuffix {
    $fsuf{$a} cmp $fsuf{$b};
}

sub Lexico {
    $a cmp $b;
}

sub dirname {
    local($_) = @_;
    @compdir = split(/\//);
    pop(@compdir);
    if ($#compdir < 1) {
	@compdir = ("");
	return '/';
    }
    join('/', @compdir);
}

sub edits {
    local($path) = @_;
    local($tmp);
    $tmp = $editor; $tmp =~ s,%s,$path,g;
    $tmp;
}

sub YES {1;}
sub NO {undef;}


syntax highlighted by Code2HTML, v. 0.9.1