#!/usr/bin/perl -w
# (c) Copyright 2003-2008. CodeWeavers, Inc.
use strict;

# Portable which(1) implementation
sub cxwhich($$;$)
{
    my ($dirs, $app, $noexec)=@_;
    if ($app =~ /^\//)
    {
        return $app if ((-x $app or $noexec) and -f $app);
    }
    elsif ($app =~ /\//)
    {
        require Cwd;
        my $path=Cwd::cwd() . "/$app";
        return $path if ((-x $path or $noexec) and -f $path);
    }
    else
    {
        foreach my $dir (split /:/, $dirs)
        {
            return "$dir/$app" if ($dir ne "" and (-x "$dir/$app" or $noexec) and -f "$dir/$app");
        }
    }
    return undef;
}

# Fast dirname() implementation
sub _cxdirname($)
{
    my ($path)=@_;
    return undef if (!defined $path);
    return "." if ($path !~ s!/+[^/]+/*$!!s);
    return "/" if ($path eq "");
    return $path;
}

# Locate where CrossOver is installed by looking for the directory
# where this this script is located, unwinding symlinks on the way
sub locate_cx_root()
{
    if (!defined $ENV{CX_ROOT})
    {
        my $argv0=cxwhich($ENV{PATH},$0);
        $argv0=$0 if (!defined $argv0);
        if ($argv0 !~ m+^/+)
        {
            require Cwd;
            $argv0=Cwd::cwd() . "/$argv0";
        }
        my $dir=_cxdirname($argv0);
        while (!-x "$dir/cxmenu" or !-f "$dir/cxmenu")
        {
            last if (!-l $argv0);
            $argv0=readlink($argv0);
            $argv0="$dir/$argv0" if ($argv0 !~ m+^/+);
            $dir=_cxdirname($argv0);
        }
        $dir =~ s%(/\.)*$%%;
        $dir =~ s%(/\./(\./)*)%/%;
        $ENV{CX_ROOT}=_cxdirname($dir);
    }
    if (!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu")
    {
        my $name0=$0;
        $name0 =~ s+^.*/++;
        print STDERR "$name0:error: could not find CrossOver in '$ENV{CX_ROOT}'\n";
        exit 1;
    }
    return $ENV{CX_ROOT};
}

BEGIN {
    unshift @INC, locate_cx_root() . "/lib/perl";
}
use CXLog;
use CXUtils;

# Strings to match in the autorun.inf DisplayNames field.
# Matching will be case-sensitive.
my @display_names=
("_msoffice97_"        => "Microsoft\\s+(Office|Word|Excel|PowerPoint|Access|Outlook).*97",
 "_msoffice2000_"      => "Microsoft\\s+(Office|Word|Excel|PowerPoint|Access|Outlook).*2000",
 "_msofficexp_"        => "Microsoft\\s+(Office|Word|Excel|PowerPoint|Access|Outlook).*XP",
 "_msoffice2003_"      => "Microsoft\\s+(Office|Word|Excel|PowerPoint|Access|Outlook).*2003",
 "_msoffice2000_"      => "Microsoft\\s+(Office|Word|Excel|PowerPoint|Access|Outlook)",
 "Adobe Photoshop 7.0" => "Photoshop",
 "Quicken"             => "Quicken",
 "AWR"                 => "Microwave Office",
 "AWR"                 => "AWR Design"
);

# Specifies a list of greps to perform. Each line is of the form
# ["<product>", "<file-glob>", ["<regexp1>","<regexp2>",...]]
# Where
#  <product>    is the product code
#  <file-glob>  specifies a case-insensitive file glob
#  <regexpX>    specifies a list of case-sensitive regular expressions
#               (use '((?i)case-insensitive)' where needed). For a file
#               to match it must match all specified regular expressions.
#               If none is specified (i.e. if that field is 'undef'), then
#               the file merely has to exist to match.
my @grep_specs=
(["Adobe Photoshop CS3", "Adobe CS3", undef],
 ["Adobe Photoshop CS2", "Adobe(R) Photoshop(R) CS2", undef],
 ["Adobe Photoshop CS", "Photoshop/*.ini", ["Photoshop CS"]],
 ["Visio",           "autorun.inf",     ["Visio"]],
 ["Quicken2007",         "autorun.ini",     ["Quicken 2007"]],
 ["Quicken",         "autorun.ini",     ["Quicken"]],
 ["Quicken",         "autorun/*.ini",   ["Quicken"]],
 ["Quicken",         "autorun.ini",   ["Quicken"]],
 ["Dreamweaver",     "autorun.inf",     ["dw_mx.ico"]],
 ["_msproject2000_", "SETUP*.INI",      ["Project 2000 Windows installer"]],
 ["_msproject2002_", "FILES/SETUP/SETUP.INI", ["Project .*2002"]],
 ["_msproject2003_", "FILES/SETUP/SETUP.INI", ["Project .*2003"]],
 ["_visio2003_",     "FILES/SETUP/SETUP.INI", ["Visio .*2003"]],
 ["_visio2002_",     "FILES/SETUP/SETUP.INI", ["Visio .*2002"]],
 ["_msoutlook2003_", "FILES/SETUP/SETUP.INI", ["Microsoft Office Outlook.*2003"]],
 ["_msoffice2003_",  "FILES/SETUP/SETUP.INI", ["Microsoft\\s+(Office|Word|Excel|PowerPoint|Access|Outlook).*2003"]],
 ["_msoffice2007_",  "Office.en-us/branding.xml", ["Microsoft Office .* 2007"]],
 ["maptitude", "setup.ini", ["Maptitude"]],
#["project2002",     "msproj10.cab",    undef],
#["publisher2002",   "pub.msi",         undef],
#["vstudio6",        "vb98*1.inf",      undef], # Visual Basic
#["vstudio6",        "vc98*1.inf",      undef], # Visual C++
#["vstudio6",        "vf98*1.inf",      undef], # Visual FoxPro
#["vstudio6",        "vj98*1.inf",      undef], # Visual J++
#["vstudio6",        "vs98ent.mif",     undef], # Visual Studio
#["vstudio.net",     "vs_setup.msi",    undef],
 ["QuickBooks",      "cd.dat",          ["SKU=CD(?:BASIC|PRO|SUPERPRO)","QuickBooks"]],
 ["DynEd",           "LICENSE.TXT",     ["DynEd SOFTWARE LICENSING AGREEMENT"]],
 ["Lotus Notes",     "autorun.inf",     ["\\bLotus\\b\\W*\\bNotes\\b"]],
 ["_msoffice2003_",  "owc11.msi",       undef],
 ["_msofficexp_",    "owc10.msi",       undef],
 ["_msoffice97_",    "Off97*.inf",      undef],
 ["EndNote",         "MSI/EN6.MSI",     undef],  # This is a guess -- I don't have a CD!
 ["EndNote",         "MSI/EN7.MSI",     undef],  # This is a guess -- I don't have a CD!
 ["EndNote 8",       "MSI/EN8.MSI",     undef],
 ["Adobe FrameMaker", "FM/DATA.TAG", ["Application=.*FrameMaker"]],
 ["Adobe Photoshop 6.0", "Adobe Photoshop 6/DATA.TAG", ["Application=Photoshop"]],
 ["Adobe Photoshop 7.0", "Autoplay/*.ini", ["Adobe Photoshop 7.0.1"]],
 ["Flash",           "Flash MX/Flash MX Installer.exe", undef],
 ["IExplorer55",     "ie5setup.exe",    undef],
 ["Half-Life",       "DATA.TAG",        ["Application=Half-Life"]],
 ["The Orange Box or other Steam-based games",   "steam.inf",       undef],
 ["The Orange Box or other Steam-based games",   "steam_Japanese.ini",       undef],
 ["_worldofwarcraft_", "Installer Tome.mpq", undef],
 ["B1ABE481-F759-4F02-85D3-B09CAFF462E1", "hpb/setup.ini", ["ProductGUID=B1ABE481-F759-4F02-85D3-B09CAFF462E1"]],
 ["A3EFC567-851F-4083-84CB-E670D991818C", "hpb/setup.ini", ["ProductGUID=A3EFC567-851F-4083-84CB-E670D991818C"]],
 ["C12BC664-B5AB-4C85-8AEC-85F43BA39AFC", "hpb/setup.ini", ["ProductGUID=C12BC664-B5AB-4C85-8AEC-85F43BA39AFC"]],
 ["60268FDB-04EC-47DE-8410-CD53BDBBD83F", "AUTORUN.INF", ["RESOURCE=ApT16.DLL"]],
 ["CEF62F0A-3BD6-41C0-9195-1D3C3A80EF7E", "AUTORUN.INF", ["RESOURCE=ApT17.DLL"]],
 ["YSO_WIN","FALCOM.INF",["Title=\"Ys Origin\""]],
 ["ED6_WIN","FALCOM.INF",["Title=\"ED6_WIN\""]],
 ["ED_SORA2","FALCOM.INF",["Title=\"ED_SORA2\""]],
 ["ED_SORA3","FALCOM.INF",["Title=\"ED_SORA3\""]],
 ["36B3F8D7-F1C7-4558-A348-7C8171BB6404",     "GT.ico",    undef],
 ["KGD_PX",     "PX.ico",    undef],
 ["F1C1C21B-F56E-400B-B0B0-270D817889F3", "setup.ini", ["AppName=VOCALOID2"]],
 ["F1C1C21B-F56E-400B-B0B0-270D817889F3", "Disk1/setup.ini", ["AppName=VOCALOID2"]]
);

my $dir;


# Utility functions

# We assume that we will read only a few directories,
# so it's reasonable to keep them all in a cache.
my %glob_cache;
sub get_files($)
{
    my ($pattern)=@_;
    if (!$glob_cache{$pattern})
    {
        my @files=CXUtils::cxglob($dir, $pattern, "i");
        cxlog("  $pattern -> [@files]\n");
        $glob_cache{$pattern}=\@files;
    }
    return $glob_cache{$pattern};
}

# We assume that we will read only a few relatively small files,
# so it's reasonable to keep them all in a cache.
my %file_cache;
sub read_file($)
{
    my ($filename)=@_;
    if (!$file_cache{$filename})
    {
        my @lines=();
        if (open(my $fh, "<", $filename))
        {
            @lines=<$fh>;
            close($fh);
        }
        $file_cache{$filename}=\@lines;
    }
    return $file_cache{$filename};
}

sub file_grep($$)
{
    my ($lines, $regexps)=@_;

    my $match=1;
    if ($regexps)
    {
        foreach my $regexp (@$regexps)
        {
            if (!grep /$regexp/,@$lines)
            {
                $match=0;
                last;
            }
        }
    }
    return $match;
}

sub multi_grep($$)
{
    my ($file_pattern, $regexps)=@_;

    cxlog("$file_pattern\n");
    my $files=get_files($file_pattern);
    if (!@$files)
    {
        cxlog("  file not found\n");
        return 0;
    }
    if (!$regexps)
    {
        cxlog("  file found\n");
        return 1;
    }

    foreach my $file (@$files)
    {
        my $lines=read_file($file);
        cxlog("  $file [\"",join("\",\"",@$regexps),"\"]\n");
        return 1 if (file_grep($lines,$regexps));
    }
    return 0;
}


# Process command-line options
my $opt_verbose;
my $opt_help;
require CXOpts;
my $cxopts=CXOpts->new(["stop_on_non_option"]);
$cxopts->add_options(["verbose!"    => \$opt_verbose,
                      "?|h|help"    => \$opt_help
                     ]);
my $err=$cxopts->parse();
CXLog::fdopen(2) if ($opt_verbose);


# Validate the command line options
my $usage;
if ($err)
{
    cxerr("$err\n");
    $usage=2;
}
elsif ($opt_help)
{
    $usage=0;
}
else
{
    if (@ARGV != 1)
    {
        cxerr("you must specify a file or directory path\n");
        $usage=2;
    }
    else
    {
        $dir=$ARGV[0];
        if (-f $dir)
        {
            $dir=cxdirname($dir);
        }
    }
}


# Print usage
if (defined $usage)
{
    my $name0=cxname0();
    if ($usage)
    {
        cxerr("try '$name0 --help' for more information\n");
        exit $usage;
    }
    print "Usage: $name0 MOUNTPOINT\n";

    print "\n";
    print "Detects supported Windows application CD-ROMs and issues an identifying code.\n";

    print "\n";
    print "Options:\n";
    print "  MOUNTPOINT   The CD-ROM's mount point\n";
    print "  --verbose    Print more information about what is going on\n";
    print "  --help, -h   Shows this help message\n";
    exit 0;
}


# Find and read the autorun.inf file
for (my $i=0;$i<@display_names;$i+=2)
{
    my $regexp="^\\s*((?i)DisplayName)\\s*=\\s*$display_names[$i+1]";
    if (multi_grep("autorun.inf",[$regexp]))
    {
        print "$display_names[$i]\n";
        exit 0;
    }
}

foreach my $grep_spec (@grep_specs)
{
    cxlog("\n@$grep_spec[0]:\n");
    if (multi_grep(@$grep_spec[1],@$grep_spec[2]))
    {
        print "@$grep_spec[0]\n";
        exit 0;
    }
}

print "other\n";
exit 0;
