#!/usr/bin/env python
"""
jo.py (jEdit Open) - A script to open a file (or files) in an already
running instance of `jEdit`_.

If jEdit isn't running, jo.py can start it.  This script is similar 
to the jEdit Launcher on Windows (R) or `jeo`_  (the jEdit Opener).

.. _jEdit: http://www.jedit.org/
.. _jeo: http://www.jedit.org/devel/people/dmoebius/jeo.html

+ add an option to specify location of server file.
+ add an option to not try to use server file?
+ it would be nice to have an option to hide 
  "Tip Window" when jEdit opens.

Copyright (C) 2003, Ollie Rutherfurd <oliver@rutherfurd.net>

The latest version of this script can be found at:

    http://www.rutherfurd.net/jedit/jo/index.html

Here's the default configuration for jo.py.  This will
be written to ${HOME}/.jedit/jo.conf, and you must edit
it before jo.py will be able to launch jEdit:

##
## configuration settings for jo.py (jEdit Open)
##
## Comments start with '#' and blank lines are ignored.
##

# Location of java executable.  If ``java``  is not 
# specified, then ``jo.py`` will look for java by 
# looking at JAVA_HOME and PATH environment variables.
#java=/usr/bin/java

# options for java executable
#java.opts=-Xmx32m

# Location of jEdit jar file.
# ** THIS IS REQUIRED **
#jedit.jar=~/jEdit/jedit.jar

# options for jEdit
#jedit.opts=-noserver -nosettings

# :mode=properties:

$Id: jo.py 36 2003-04-04 00:51:14Z oliver $
"""

import glob
import os
import socket
import struct
import sys

SCRIPT = """\
EditServer.handleClient(true, "%(directory)s", new String[]{\
%(files)s\
});"""

OPEN_ARGS = """%(java.opts)s -jar %(jedit.jar)s %(jedit.opts)s"""

SETTINGS = (
    'java',
    'java.opts',
    'jedit.jar',
    'jedit.opts',
)

class NoServerFile(Exception):
    """Couldn't find jEdit server file."""
    pass

class NoJavaFound(Exception):
    """Couldn't find ``java`` using JAVA_HOME or PATH."""
    pass

class UnknownSetting(Exception):
    """Unknown setting found in config file."""
    pass

class ConfigFileError(Exception):
    """Error reading config file."""
    pass

class CantFindJar(Exception):
    """Can't start jEdit - jedit.jar not specified in jo.conf."""
    pass

def get_home():
    """finds users's HOME directory"""
    if os.name == 'nt':
        return os.environ['USERPROFILE']
    else:
        return os.environ['HOME']
    
def get_server_info(path=None):
    """returns (port,auth_key) from server file, or raises
    an exception if no server file found."""
    if path is None:
        path = os.path.join(get_home(), '.jedit', 'server')
    if not os.path.exists(path):
        raise NoServerFile
    f = open(path)
    f.readline()    # skip first line
    port = int(f.readline())
    auth_key = int(f.readline())
    f.close()
    return port,auth_key

def find_java():
    """look for ``java`` executable in some regular places"""
    java = ['java','javaw.exe'][os.name == 'nt']
    java_home = os.environ.get('JAVA_HOME',None)
    if java_home:
        if os.path.exists(os.path.join(java_home,'bin',java)):
            return os.path.join(java_home,'bin',java)
    for d in os.environ['PATH'].split(os.pathsep):
        if os.path.exists(os.path.join(d,'bin',java)):
            return os.path.join(d,'bin',java)
    raise NoJavaFound

def get_files(args):
    """globs files on windows"""
    files = []
    if os.name == 'nt':
        for arg in args:
            files.extend(glob.glob(arg))
    else:
        files = args
    return files

def get_setting_path():
    return os.path.join(get_home(), '.jedit', 'jo.conf')

def create_config_file():
    """creates default config file if none exists."""
    settings_file = get_setting_path()
    if not os.path.exists(settings_file):
        text = sys.modules[__name__].__doc__
        start = text.find('##')
        end = text.find('$Id')
        f = open(settings_file,'w')
        f.write(text[start:end])
        f.close()

def get_settings():
    """reads config file and returns settings, with 
    empty strings for unset settings

    creates default config file if none is found."""
    settings_file = get_setting_path()
    opts = dict([(s,'') for s in SETTINGS])
    f = open(settings_file)
    for line in f.readlines():
        if not line:
            break
        line = line.strip()
        if line.startswith('#') or len(line) == 0:
            continue
        try:
            name,value = [x.strip() for x in line.split('=',1)]
            if name == 'jedit.jar':
                value = os.path.expanduser(value)
        except ValueError,e:
            print line
        if name not in SETTINGS:
            raise UnknownSetting(name)
        opts[name] = value
    f.close()
    return opts

def usage():
    print """usage: %s FILENAME [FILENAME+] 

settings in: %s""" % (os.path.split(sys.argv[0])[-1], get_setting_path())
    sys.exit(1)

def main():
    create_config_file()    # ensure a config file exists

    # show usage message if not given any files (or if user asks for help)
    if len(sys.argv) == 1 or sys.argv[1].lower() in ('/?','-h','--help'):
        usage()

    files = get_files(sys.argv[1:])

    #
    # try to connect to running instance of jEdit
    #
    script = SCRIPT % {
        'directory': os.getcwd().replace('\\','\\\\'),
        'files': ','.join(['"%s"' % f.replace('\\','\\\\') for f in files]),
    }
    try:
        port,auth_key = get_server_info()
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect(('localhost',port))
            s.send(struct.pack('!i',auth_key))
            s.send(struct.pack('!h',len(script)))
            s.send(script)
        finally:
            s.close()
        return 0
    except NoServerFile,e:
        pass
    except socket.error,e:
        print >> sys.stderr, "Warning: couldn't connecting to server: %s" % e[1]

    #
    # start a new instance of jEdit
    #
    try:
        settings = get_settings()
        if not settings['jedit.jar']:
            raise CantFindJar
        java = settings['java']
        if not java:
            java = find_java()
        args = OPEN_ARGS % settings
        _args = [os.path.split(java)[-1]]
        _args.extend(args.split())
        _args.extend(files)
        os.execvp(java, _args)
    except (NoJavaFound,CantFindJar),e:
        print >> sys.stderr, '\nERROR:', e.__doc__
        print >> sys.stderr, """
You can find the settings file here:

    %s

Please supply the missing value and try again.""" % get_setting_path()
        sys.exit(1)
    except OSError,e:
        print >> sys.stderr, 'ERROR:', str(e)
        print >> sys.stderr, 'Is java location correct?'

    return 0


if __name__ == '__main__':
    sys.exit(main())

# :indentSize=4:lineSeparator=\n:maxLineLen=0:noTabs=true:tabSize=4:
