""" CommandLine - Get and parse command line options

    NOTE: This still is very much work in progress !!! 

"""

__copyright__ = """\
    Copyright (c) 1997,1998 by Marc-Andre Lemburg; mailto:mal@lemburg.com

                          All Rights Reserved.

    Permission to use, copy, modify, and distribute this software and
    its documentation for any purpose and without fee is hereby
    granted, provided that the above copyright notice appear in all
    copies and supporting documentation.

    This software comes with NO WARRANTY. Use at your own risk.
"""

__version__ = '0.2'

import sys,getopt,string,glob

def _getopt_flags(options):

    """ Convert the option list to a getopt flag string and long opt list
    """
    s = []
    l = []
    for o in options:
	if o.prefix == '-':
	    # short option
	    s.append(o.name)
	    if o.takes_argument:
		s.append(':')
	else:
	    # long option
	    if o.takes_argument:
		l.append(o.name+'=')
	    else:
		l.append(o.name)
    return string.join(s,''),l

class NoDefault:

    """ Use this as default value if no default value should be assumed.
    """

class Option:

    """ Option base class.

        If an option takes an argument which has no default value,
	pass NoDefault as default value. Options which take no arguments
	should not pass anything for default.
    """

    default = None
    helptext = ''
    prefix = '-'
    takes_argument = 0
    has_default = 0

    def __init__(self,name,help=None,default=None):
	
	if not name[:1] == '-':
	    raise TypeError,'option names must start with "-"'
	if name[1:2] == '-':
	    self.prefix = '--'
	    self.name = name[2:]
	else:
	    self.name = name[1:]
	if default is not None:
	    self.default = default
	    self.takes_argument = 1
	    if default is not NoDefault:
		self.has_default = 1
	if help:
	    self.help = help

    def __str__(self):

	o = self
	if o.default is None:
	    return '%-10s %s' % (o.prefix+o.name,o.help)
	elif o.default is NoDefault:
	    return '%-10s %s' % (o.prefix+o.name+' arg',o.help)
	else:
	    return '%-10s %s (%s)' % (o.prefix+o.name+' arg',o.help,o.default)

class Application:

    """ Command line application interface with builtin argument
        parsing.
    """

    # Options the program accepts (Option instances)
    options = []

    # Standard settings; these are appended to options in __init__
    preset_options = [Option('-h','show this help text'),
		      Option('--help','show this help text'),
		      Option('--copying','show copyright')]

    # The help layout looks like this:
    # [header]   - defaults to <filename>
    #
    # [version]  - formatted as 'Version: %s' % self.version, if given
    #
    # options:
    # [options]  - formatted from self.options
    # [about]    - defaults to ''
    # [copyright]- defaults to ''

    # Header (defaults to program name)
    header = ''

    # Version (optional)
    version = ''

    # General information printed after the possible options (optional)
    about = ''

    # Copyright to show
    copyright = __copyright__

    # Do file globbing ?
    globbing = 1

    # Instance variables:
    values = None	# Dictionary of passed options (or default values)
			# indexed by the options name, e.g. '-h'
    files = None	# List of passed filenames

    def __init__(self,argv=None):

	if argv is None:
	    argv = sys.argv
	self.filename = argv[0]
	self.arguments = argv[1:]
	self.options = self.options + self.preset_options
	self.startup()
	self.parse()
	self.main()

    def startup(self):

	""" Set user defined instance variables
	"""
	return

    def help(self):

	if self.header:
	    print self.header
	else:
	    print self.filename
	if self.version:
	    print
	    print 'Version:',self.version
	print
	self.print_options()
	if self.about:
	    print self.about

    def print_options(self):

	options = self.options
	print 'Options and default settings:'
	if not options:
	    print '  None'
	    return
	long = filter(lambda x: x.prefix == '--', options)
	short = filter(lambda x: x.prefix == '-', options)
	items = short + long
	for o in options:
	    print ' ',o
	print

    #
    # Example handlers:
    #
    def handle_h(self,arg):

    	self.help()
	sys.exit(1)
    
    # Handlers for long options have two underscores in their name
    def handle__help(self,arg):

	self.help()
	sys.exit(1)

    def handle__copying(self,arg):

	print self.header
	print
	print self.copyright
	sys.exit(1)

    def parse(self):

	""" Parse the command line and fill in self.values and self.files.
	    This also calls handlers for the options if given.
	"""
	self.values = values = {}
	for o in self.options:
	    if o.has_default:
		values[o.prefix+o.name] = o.default
	    else:
		values[o.prefix+o.name] = 0
	flags,lflags = _getopt_flags(self.options)
	try:
	    optlist,files = getopt.getopt(self.arguments,flags,lflags)
	    if self.globbing:
		l = []
		for f in files:
		    gf = glob.glob(f)
		    if not gf:
			l.append(f)
		    else:
			l[len(l):] = gf
		files = l
	    self.optionlist, self.files = optlist, files
	except getopt.error,why:
	    if self.header:
		print self.header
	    else:
		print self.filename
	    print
	    print '*',why
	    print
	    self.print_options()
	    sys.exit(1)
	for o,v in optlist:
	    try:
		v = string.atoi(v)
	    except ValueError:
		pass
	    try:
		if o[:2] == '--':
		    m = getattr(self,'handle__'+o[2:])
		else:
		    m = getattr(self,'handle_'+o[1:])
		m(v)
	    except AttributeError:
		if v == '':
		    # count the number of occurances
		    if values.has_key(o):
			values[o] = values[o] + 1
		    else:
			values[o] = 1
		else:
		    values[o] = v

    def main(self):

	""" Override this method as program entry point.
	"""
	return None

# Alias
CommandLine = Application

def _test():

    class MyCmdline(CommandLine):
	header = 'Test CommandLine'
	version = __version__
	options = [Option('-v','verbose')]
	
	def handle_v(self,arg):
	    print 'VERBOSE, Yeah !'

    cmd = MyCmdline()
    if not cmd.values['-h']:
	cmd.help()
    print 'files:',cmd.files
    print 'Bye...'

if __name__ == '__main__':
    _test()
