
import os
import sys
import signal

class OsUtilsException(Exception):
    pass

class RunningExecutableFailed(OsUtilsException):
    def __init__(self, cmd):
        OsUtilsException.__init__(self,
          _("Running the command %s failed.") % cmd)

class ExecutableDoesNotExist(OsUtilsException):
    """
    We should raise this exception if os.system fails and returns
    anything else than 0.
    """
    def __init__(self, cmd):
        OsUtilsException.__init__(self,
          _("The executable '%s' does not exist.") % cmd)

class ExecutableFormatStringException(OsUtilsException):
    def __init__(self):
        OsUtilsException.__init__(self,
          _("The executable string should contain zero or one '%s'."))


__all__ = ['run_external_program',
    'OsUtilsException',
    'ExecutableDoesNotExist',
    'ExecutableFormatStringException'
]
__child_pid = None

def kill_external_program():
    """
    Kill the previous ran program if it is still running.
    Do nothing if there is nothing to kill.
    """
    global __child_pid
    if sys.platform != 'win32':
        if __child_pid:
            try:
                os.kill(__child_pid, signal.SIGKILL)
            except OSError:
                pass
            os.wait()
            __child_pid = None

def run_external_program(cmdstring, wdir, argument):
    global __child_pid
    if not cmdstring:
        raise ExecutableDoesNotExist(cmdstring)
    if "%s" not in cmdstring:
        cmdline = "%s %s" % (cmdstring, argument)
    else:
        try:
            cmdline = cmdstring % argument
        except TypeError:
            raise ExecutableFormatStringException()
    wdir = os.path.normpath(wdir)
    cur_dir = os.getcwdu()
    v = cmdline.split()
    v = v[:1] + v
    if not os.path.exists(os.path.join(wdir, v[0])):
        raise ExecutableDoesNotExist(v[0])
        return
    if sys.platform == 'win32':
        os.chdir(wdir)
        os.system(cmdline)
        os.chdir(cur_dir)
        return
    kill_external_program()
    pid = os.fork()
    if pid == 0:
        os.chdir(wdir)
        try:
            os.execlp(*v)
        except OSError, x:
            print >> sys.stderr, "OS Error:", x
            os._exit(-1)
            os.chdir(cur_dir)
        os.chdir(cur_dir)
    else:
        __child_pid = pid


def find_progs(execs):
    """
    Return a list of full path names to the executables named in execs
    if they are found on the path.

    execs - a tuple of possible executable names
    """
    retval = []
    for p in os.environ['PATH'].split(os.pathsep):
        for e in execs:
            if os.path.exists(os.path.join(p, e)):
                retval.append(os.path.join(p, e))
    return retval

def find_mma_executables():
    """
    Return a list of command lines that we think will run MMA.
    """
    if sys.platform != 'win32':
        return find_progs(("mma",))
    else:
        retval = []
        drives = ('c',)
        for drive in drives:
            for dirname in os.listdir("%s:\\" % drive):
                if dirname.lower().startswith('mma'):
                    dir = "%s:\\%s" % (drive, dirname)
                    mma_py = "%s:\\%s\\mma.py" % (drive, dirname)
                    if os.path.exists(mma_py):
                        retval.append("%s %s" % (sys.executable, mma_py))
                    mma_bat = "%s:\\%s\\mma.bat" % (drive, dirname)
                    if os.path.exists(mma_bat):
                        retval.append(mma_bat)
    return retval

