#!/usr/bin/perl

# runScriptFromClipboard:
# This script runs the script (shell script or AppleScript)
# that is currently in the Clipboard.
# (i.e. the script text that had previously been copied to the Clipboard)
# Any command-line arguments will be sent to the script.
#
# Cameron Hayne (macdev@hayne.net)  Oct 2006

use strict;
use warnings;

# runAppleScript: Runs the supplied AppleScript
#                 The arguments to this function are:
#                 - the text of the AppleScript
#                 - the command-line args to be passed to the AppleScript
sub runAppleScript($$)
{
    my ($ascript, $args) = @_;

    my $result = `/usr/bin/osascript - $args<<"    EOT"
    $ascript
    EOT
    `;
    chomp($result);
    return $result;
}


# getTextFromClipboard: Gets the contents of the clipboard
sub getTextFromClipboard()
{
    my $text = `pbpaste`;
    return $text;
}

# writeStringToTempFile: saves the given string to a temp file
#                        and returns the name of that file
sub writeStringToTempFile($)
{
    my ($str) = @_;
    
    use IO::File;
    use POSIX qw(tmpnam);
    
    # the following is from the Perl Cookbook
    my $filename;
    my $fh;
    # try new temporary filenames until we get one that didn't already exist
    do { $filename = tmpnam() }
        until $fh = IO::File->new($filename, O_RDWR|O_CREAT|O_EXCL);
        
    print $fh $str;
    $fh->close();
    
    return $filename;
}

# saveScriptToFile: Saves the given text to a temp file,
#                   makes that file executable,
#                   and returns its name
sub saveScriptToFile($)
{
    my ($script) = @_;

    my $scriptFile = writeStringToTempFile($script);

    chmod 0700, $scriptFile or die "Can't make script executable: $!\n";
    return $scriptFile;
}

sub escapeDoubleQuotes($)
{
    my ($str) = @_;

    # we need to escape both backslashes and double-quotes
    # e.g.: " -> \"   \ -> \\   \" -> \\\"
    $str =~ s/([\\"])/\\$1/g;
    return $str;
}

# confirmScript: Asks the user to confirm that he/she wants to run this script.
#                Returns 1 if confirmed, 0 otherwise
sub confirmScript($$$)
{
    my ($scriptType, $script, $scriptArgs) = @_;

    my $message = "Please confirm that you want to run this ";
    $message .= "$scriptType " unless $scriptType eq "unknown";
    $message .= "with the arguments: $scriptArgs" if $scriptArgs;
    
    my $timeout = "120";  # seconds

    my $ascript =<<"    EOT";
    tell application "TextEdit"
        make new document at end of documents
        set text of document 1 to "$script"
        activate
        set status to 0
        try
            display dialog "$message" giving up after $timeout
            if (gave up of result) is false then
                set status to 1
            end if
        on error
            -- we get here if user canceled, nothing to do
        end try
        close document 1 without saving
    end tell

    -- bring Terminal to the front again
    tell application "Terminal"
        activate
    end tell

    set result to status
    EOT
 
    my $status = runAppleScript($ascript, "");
    return $status;
}

# determineScriptType: tries to determine what type the given script file is
sub determineScriptType($)
{
    my ($filename) = @_;
    
    my $scriptType = "unknown";
    
    # first try to identify it via the 'file' utility
    my $outputFromFile = `/usr/bin/file -b $filename`;
    if ($outputFromFile =~ /(.*) script text executable$/)
    {
        $scriptType = $1;
    }
    
    if ($scriptType eq "unknown")
    {
        open(FILE, "<$filename") or die "Can't open file \"$filename\": $!\n";
        while (<FILE>)
        {
            if (/^\s*tell\s+/)
            {
                # has a line starting with 'tell'
                $scriptType = "AppleScript";
                last;
            }
            
            if (/^\s*do\s+shell\s+script\s+/)
            {
                # has a line starting with 'do shell script'
                $scriptType = "AppleScript";
                last;
            }
        }
        close(FILE);
    }
    
    return $scriptType;
}

MAIN:
{
    my $scriptArgs = join(" ", @ARGV);
    
    my $script = getTextFromClipboard();
    die "Error: no script found\n" unless $script;
    
    my $scriptFile = saveScriptToFile($script);
    my $scriptType = determineScriptType($scriptFile);
    
    if (confirmScript($scriptType, escapeDoubleQuotes($script), $scriptArgs))
    {
        if ($scriptType eq "AppleScript")
        {
            runAppleScript($script, $scriptArgs);
        }
        else
        {
            # assume it's a shell script
            system("$scriptFile $scriptArgs");
        }
    }
    
    unlink($scriptFile);
}

