#!/usr/bin/python

# tzsh.py
#       --copyright--                   Copyright 2011 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       February 15, 2011       bar
#       February 18, 2011       bar     make all programs in the path (that don't collide) global functions
#       February 19, 2011       bar     use os.system for running programs so they print while running
#       March 4, 2011           bar     A# variables for sys.argv items
#                                       print_list() and find_files()
#       March 5, 2011           bar     AA and dummy A args beyond the end of 'em
#       March 9, 2011           bar     ooops AA doesn't want sys.argv[0]
#                                       the_temp_dir and name changes in that area
#       March 20, 2011          bar     note
#       May 19, 2011            bar     make direct command line operation sorta like tzcl.py
#       October 5, 2011         bar     experiment with execfile
#       October 6, 2011         bar     mess with __doc__ directly
#                                       get default ext working in windows (XP tested)
#                                       version number
#       November 11, 2011       bar     run_program able to take std... values
#       February 4, 2012        bar     remove q "ks" echo in a comment
#       May 27, 2012            bar     doxygen namespace
#       June 17, 2012           bar     typo in commend
#       March 28, 2015          bar     copyfile, movefile deltree
#       April 2, 2015           bar     path_splitext
#       --eodstamps--
##      \file
#       \namespace              tzpython.tzsh
#
#
#       Expose a gob of routines and variables to someone who does a "from tzsh.py import *" or has a shebang of "#!tzsh.py"
#       and wants to replace shell scripts and .bat files with python scripts.
#
#       Note:
#           env:    PYTHONSTARTUP   has startup .py file name for interactive python (not for python programs run normally)
#
#
#

__doc__ = """
This module makes writing Python-ish shell-script/.bat-files replacements easier.

Your script has this at the top:

    from    %s  import  *

or it can have a shebang somewhat like:

    #!%s.py

    And you can simply run your script if it's chmod'd executable.
    Note: If you directly use python to run by your script (overriding the shebang),
          it will need the 'from %s import *'.
    Under Windows give your script a unique file name extention (e.g. .%s) and tell
    the OS to use %s.py to handle files of that extension.
        Note: It appears that the only way to do this is to hand-edit the registry
              with something like the following
              (change the py.ico, python.exe and tzsh.py paths):

            Windows Registry Editor Version 5.00
            [HKEY_CLASSES_ROOT\.tzsh]
            @="tzsh.file"
            "Content Type"="text/plain"

            [HKEY_CLASSES_ROOT\tzsh.file]
            @="tzsh file"
            "EditFlags"=dword:00000000

            [HKEY_CLASSES_ROOT\tzsh.file\DefaultIcon]
            @="C:\\Python26\\DLLs\\py.ico"

            [HKEY_CLASSES_ROOT\tzsh.file\shell]

            [HKEY_CLASSES_ROOT\tzsh.file\shell\open]
            @="Open"

            [HKEY_CLASSES_ROOT\tzsh.file\shell\open\command]
            @="\"C:\\Python26\\python.exe\" \"C:\\tzpython\\tzsh.py\" \"%%1\" %%*"


And your script can run programs by "calling" a program on the PATH like this Unix fer-instance:
    ls('-la *.py')

And your script can call all sorts of routines from os, os.path, sys, shutil, glob, random and others
without importing the modules.

And your script can access the command line args as A0 A1 A2... and AA for them all in a string.

And your script has the following handy, dandy routines available to it:

    elapsed_time()          # returns clock time as opposed to time-of-day
    find_files()            # returns array of file names as Unix 'find . -iname "ambiguous_name"' or WinDOS 'dir /b /s ambiguous_name' would do
    run_program()           # runs a program returning the exit code and a combo of stdout/stderr output in a string

"""
import  os
_tzsh_name  = os.path.splitext(os.path.basename(__file__))[0]
__doc__     = __doc__ % ( _tzsh_name, _tzsh_name, _tzsh_name, _tzsh_name, _tzsh_name, )


import  copy
import  functools
import  getpass
import  glob
import  hashlib
import  hmac
import  math
import  multiprocessing
import  platform
import  pprint
import  random
import  re
import  shutil
import  subprocess
import  sys
import  tempfile
import  time

try :
    import          tzlib
except ImportError  :
    try             :
        import      tzl.tzlib   as tzlib
    except ImportError :
        try         :
            import  tzlib.tzlib as tzlib
        except ImportError :
            tzlib   = None
        pass
    pass


TZSH_VERSION    = 1



sys             = sys
argv            = sys.argv
byteorder       = sys.byteorder
exit            = sys.exit
maxint          = sys.maxint
maxsize         = sys.maxsize
maxunicode      = sys.maxunicode
stdin           = sys.stdin
stdout          = sys.stdout
stderr          = sys.stderr


glob            = glob
dir_files       = glob.glob


os              = os
system          = os.system
chdir           = os.chdir
change_dir      = os.chdir
cwd             = os.getcwd
current_dir     = os.getcwd
chmod           = os.chmod
stat            = os.stat
lstat           = os.lstat
mkdir           = os.mkdir
make_dir        = os.mkdir
makedirs        = os.makedirs
make_dirs       = os.makedirs

def safe_make_dir(  path, *args, **kwargs) :
    """
        Make the full directory path, including intermediate directories
        if the directory does not exist.
    """

    if  not os.path.exists(path) :
        os.makedirs(path, *args, **kwargs)
    pass

rmdir           = os.rmdir
del_dir         = os.rmdir
del_dirs        = os.removedirs
rename          = os.rename
rename_file     = os.rename
renames         = os.renames
rename_dirs     = os.renames
dir_walk        = os.walk
environ         = os.environ
environment     = os.environ
getenv          = os.getenv
get_env         = os.getenv
putenv          = os.putenv
put_env         = os.putenv

del_file        = os.remove

def safe_del_files(path) :
    """
        Delete the given ambiguously named file(s) if they exist.
    """

    path        = os.path.expanduser(os.path.expandvars(path))
    fls         = glob.glob(path)
    for fn in fls :
        os.remove(fn)
    pass


def make_temp_file_name(ext = None) :
    ext     = ext or ".tmp"
    try     :
        nm  = os.urandom(16)
    except NotImplementedError :
        nm  = [ chr(random.randint(0, 255)) for i in xrange(16) ]
    nm      = "".join([ "%02x" % ord(nm[i]) for i in xrange(len(nm)) ]) + ext
    tmp     = os.path.expanduser(os.path.expandvars(environ.get('TEMP', '')))
    if  (not tmp) or (not os.path.isdir(tmp)) :
        tmp = os.path.expanduser(os.path.expandvars(environ.get('TMP',  '')))
        if  (not tmp) or (not os.path.isdir(tmp)) :
            tmp = os.path.expanduser(os.path.expandvars('~/tmp'))
            if  (not tmp) or (not os.path.isdir(tmp)) :
                tmp = os.path.expanduser(os.path.expandvars('~/temp'))
                if  (not tmp) or (not os.path.isdir(tmp)) :
                    tmp = '/tmp'
                    if  (not tmp) or (not os.path.isdir(tmp)) :
                        tmp = '/temp'
                        if  (not tmp) or (not os.path.isdir(tmp)) :
                            tmp = ''
                        pass
                    pass
                pass
            pass
        pass
    return(os.path.abspath(os.path.join(tmp, nm)))

tempfile        = tempfile
new_temp_file   = tempfile.NamedTemporaryFile
new_temp_dir    = tempfile.mkdtemp
the_temp_dir    = tempfile.gettempdir()


abspath     = os.path.abspath
basename    = os.path.basename
dirname     = os.path.dirname
exists      = os.path.exists
lexists     = os.path.lexists
expanduser  = os.path.expanduser
expandvars  = os.path.expandvars
def expand_user_vars(pth) :
    return(os.path.expanduser(os.path.expandvars(pth)))
getatime    = os.path.getatime
getmtime    = os.path.getmtime
getctime    = os.path.getctime
file_time   = os.path.getmtime
getsize     = os.path.getsize
file_size   = os.path.getsize
isfile      = os.path.isfile
is_file     = os.path.isfile
isdir       = os.path.isdir
is_dir      = os.path.isdir
islink      = os.path.islink
is_link     = os.path.islink
joinpath    = os.path.join
pathjoin    = os.path.join
join_path   = os.path.join
path_join   = os.path.join
splitpath   = os.path.split
pathsplit   = os.path.split
split_path  = os.path.split
path_split  = os.path.split
splitext    = os.path.splitext
extsplit    = os.path.splitext
split_ext   = os.path.splitext
ext_split   = os.path.splitext
pathsplitext    = os.path.splitext
pathextsplit    = os.path.splitext
path_split_ext  = os.path.splitext
path_splitext   = os.path.splitext
path_ext_split  = os.path.splitext
normpath    = os.path.normpath
norm_path   = os.path.normpath
realpath    = os.path.realpath
real_path   = os.path.realpath
abspath     = os.path.abspath
abs_path    = os.path.abspath


def find_files(ambiguous_name, do_sub_dirs = True) :
    """
        Return an array with the names of the files that match the given ambiguous file name.
    """

    abn     = os.path.expanduser(os.path.expandvars(ambiguous_name))
    files   = glob.glob(abn)

    if  do_sub_dirs :
        (dir_name, amb_name) = os.path.split(abn)
        if  not len(dir_name) :
            dir_name         = "./"

        for fn in os.listdir(dir_name) :
            ffn = os.path.join(os.path.normpath(dir_name), fn)
            if  os.path.isdir(ffn) :
                files  += find_files(os.path.join(ffn, amb_name), do_sub_dirs)
            pass
        pass

    return(files)


shutil      = shutil
copyfile    = shutil.copy2
copy_file   = shutil.copy2
copytree    = shutil.copytree
copy_tree   = shutil.copytree
movefile    = shutil.move
move_file   = shutil.move
deltree     = shutil.rmtree
del_tree    = shutil.rmtree


random      = random
randnum     = random.random
randint     = random.randint
shuffle     = random.shuffle


time        = time
unix_time   = time.time
def mdy_hms(t = None) :
    if  t  == None :
        t   = time.time()
    tm      = time.localtime(t)
    return(tm.tm_mon, tm.tm_mday, tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec)
def gmt_mdy_hms(t = None) :
    if  t  == None :
        t   = time.time()
    tm      = time.gmtime(t)
    return(tm.tm_mon, tm.tm_mday, tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec)

ascii_time          = time.asctime
sleep               = time.sleep

def elapsed_time() :
    if  sys.platform == 'win32' :
        return(time.clock())
    return(os.times()[4])
elapsed_time        = tzlib.elapsed_time    if tzlib else   elapsed_time


subprocess          = subprocess
def run_program(cmd, stdin = None, stdout = None, stderr = None) :
    """
        Run the given command line program (full path needed for the executable's name).
        Return the exit code int and the stdout/stderr output stripped and CRLFCRLF...LFLF... -> LF converted.
        This is a blocking function and does not return until the program finishes. So you can't monitor the output as it runs.
    """

    try             :
        stdin       = stdin  or subprocess.PIPE
        stdout      = stdout or subprocess.PIPE
        stderr      = stderr or subprocess.PIPE

        if  sys.platform == 'win32' :
            p       = subprocess.Popen(cmd, shell = True, stdin = stdin, stdout = stdout, stderr = stderr)
        else        :
            p       = subprocess.Popen(cmd, shell = True, stdin = stdin, stdout = stdout, stderr = stderr, close_fds = True)
        ( rs, se )  = p.communicate()
        r           = p.returncode
        rs          = rs.strip()
        rs          = re.sub(r"(?:\r?\n)+", "\n", rs).strip() + "\n"
    except          :
        raise

    return(r, rs)


use_os_system       = True
ignore_errors       = False         # default values
exception_on_error  = False

def run_a_program(program_name, *kargs, **kwargs) :
    uos         = use_os_system
    if  'use_os_system' in kwargs :
        del(kwargs['use_os_system'])
        uos     = True

    ie          = ignore_errors
    if  'ignore_errors' in kwargs :
        del(kwargs['ignore_errors'])
        ie      = True

    xe          = exception_on_error
    if  'exception_on_error' in kwargs :
        del(kwargs['exception_on_error'])
        xe      = True

    ka          = [ '"%s"' % ( a.replace('"', '\\"') ) if re.search(r"\s", a) else a for a in kargs ]
    cmd         = program_name + " " + " ".join([ '"%s=%s"' % ( k.replace('"', '\\"'), v.replace('"', '\\"') ) for k, v in kwargs.items() ]) + " " + " ".join(ka)
    if  uos         :
        if  len(kargs) <= 1 :
            cmd = program_name + " " + " ".join([ '"%s=%s"' % ( k.replace('"', '\\"'), v.replace('"', '\\"') ) for k, v in kwargs.items() ]) + " " + " ".join(kargs)
        r       = os.system(cmd)
        return(r)

    ( r, rs )   = run_program(cmd)
    if  (not ie) and r  :
        if  xe          :
            raise   ValueError([ r, rs ])
        print rs
        sys.exit(r)

    return(r, rs)


copy            = copy
deepcopy        = copy.deepcopy
deep_copy       = copy.deepcopy

getpass         = getpass
getuser         = getpass.getuser
get_user_name   = getpass.getuser
get_password    = getpass.getpass

hashlib         = hashlib
hmac            = hmac
math            = math
re              = re
multiprocessing = multiprocessing
platform        = platform

pprint          = pprint
pretty_print    = pprint.pprint
def print_list(a) :
    for i in a :
        print i
    return(a)




def set_args() :
    """ Make global names of the command line parameters. """

    for ai, arg in  enumerate(sys.argv) :
        globals()['A%u' % ai]   = arg               # define the A# variables for args (note: python won't allow _0 _1, etc) (A0 is the script file name A1 is the first command line arg, etc.)
    for ai in xrange(len(sys.argv), 20) :
        globals()['A%u' % ai]   = ""
    globals()['argc']   = str(len(sys.argv))
    globals()['AA']     = " ".join([ arg      if len(arg) and (not re.search(r'[\s"]', arg)) else    '"' + arg.replace('"', '\\"') + '"'     for arg in sys.argv[1:] ])


all_possible_programs   = {}

def forget_all_possible_programs() :
    global  all_possible_programs

    for p in all_possible_programs.values() :
        if  p in globals() :
            del(globals()[p])
        pass
    all_possible_programs   = {}


windows_exe_exts    = {
                        ".exe"  : 1,
                        ".bat"  : 1,
                        ".msi"  : 1,
                      }


if  type(__builtins__) == type({}) :
    builtin_names   = __builtins__
else :
    builtin_names   = {}
    for bi in dir(__builtins__) :
        builtin_names[bi]   = True
    pass

def set_programs() :
    """ Make global function names of all programs on the PATH whose names don't collide with known, global names. """

    global  all_possible_programs

    forget_all_possible_programs()
    pth     = os.getenv('PATH')
    if  pth :
        da          = pth.split(os.pathsep)
        if  sys.platform == 'win32' :
            da.append('.')
        for d in da :
            if  d   :
                pa  = []
                fls = glob.glob(os.path.join(d, "*"))
                if  sys.platform == 'win32' :
                    pa  = [ fn for fn in fls if os.path.splitext(fn)[1].lower() in windows_exe_exts ]
                else :
                    pa  = [ fn for fn in fls if os.access(fn, os.X_OK) ]
                for fn in pa :
                    if  fn not in all_possible_programs :
                        pn      = os.path.basename(fn)
                        if  sys.platform == 'win32' :
                            pn  = os.path.splitext(pn)[0]
                            pn  = pn.lower()
                        pn      = re.sub(r"[^a-zA-Z0-9_]", '_', pn)             # convert characters that aren't allowed in Python names to underscores
                        if  re.match(r"^[a-zA-Z_]", pn) :
                            if  (pn not in globals()) and (pn not in builtin_names) :
                                all_possible_programs[fn]   = pn
                                globals()[pn]               = functools.partial(run_a_program, fn)
                            pass
                        pass
                    pass
                pass
            pass
        pass
    pass

set_programs()
set_args()

if  __name__   == '__main__' :
    if  len(sys.argv) <= 1 :
        print __doc__
        sys.exit(254)

    for a in sys.argv[1:]  :
        if  a in [ '-h', '--help', '-?', '--?', '/H', '/h', '/?', ] :
            print __doc__
            sys.exit(254)

        if  os.path.isfile(a) :
            execfile(a)
        else :
            exec(a)                 # note: Use tzcl.py for this sort of thing.
        pass
    pass
pass


#
#
#
# eof
