import sys import os import subprocess import configparser import magic import json import datetime mime = magic.Magic(mime=True) # supported values: fish, bash, zsh if ( 'DO_WHAT_SHELL' in os.environ ): shell=os.environ['DO_WHAT_SHELL'] else: print("echo Shell type not set. To set up the environment, add the following line to your shell\\\'s init file:") print("echo dowhat {shell} \| .") exit() # Defaults print_file="cat" pretty_print_file="less -R" edit_file="vim" list_directory="ls --color=auto" list_git_directory="ls --color=auto" change_dir="cd" print_dir="pwd" help_command="man" path_locator="which" open_file="xdg-open" # general function for setting a shell's environment variable def set_runtime_var(name, value, options=""): if ( shell == "fish" ): print("set "+name+" "+options+" \""+value+"\"") elif ( shell == "bash" or shell == "zsh" ): print("export "+name+"="+"\""+value+"\"") # checks if a file is a binary file or a plaintext file def is_binary_file(filepathname): textchars = bytearray([7,8,9,10,12,13,27]) + bytearray(range(0x20, 0x7f)) + bytearray(range(0x80, 0x100)) is_binary_string = lambda bytes: bool(bytes.translate(None, textchars)) if is_binary_string(open(filepathname, 'rb').read(1024)): return True else: return False # check if a given program is in the path environment using which def env_has(program): def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return True else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return True return None # get the number of lines in a file def file_len(fname): with open(fname) as f: for i, l in enumerate(f): pass try: i except NameError: return 0 else: return i + 1 # bootstrap the config ENV_HOME=os.environ['HOME'] subprocess.call(['mkdir', '-p', ENV_HOME+'/.config/do_what']) if ( os.path.isfile(ENV_HOME+'/.config/do_what/what.config') ): config = configparser.ConfigParser() config.read(ENV_HOME+'/.config/do_what/what.config') # TODO add else default configs if ( 'DEFAULT' in config ): if ( 'print_file' in config['DEFAULT'] ): print_file = config['DEFAULT']['print_file'] if ( 'pretty_print_file' in config['DEFAULT'] ): pretty_print_file = config['DEFAULT']['pretty_print_file'] if ( 'edit_file' in config['DEFAULT'] ): edit_file = config['DEFAULT']['edit_file'] if ( 'list_directory' in config['DEFAULT'] ): list_directory = config['DEFAULT']['list_directory'] if ( 'help_command' in config['DEFAULT'] ): help_command = config['DEFAULT']['help_command'] if ( 'path_locator' in config['DEFAULT'] ): path_locator = config['DEFAULT']['path_locator'] if ( 'list_git_directory' in config['DEFAULT'] ): list_git_directory = config['DEFAULT']['list_git_directory'] if ( 'open_file' in config['DEFAULT'] ): open_file = config['DEFAULT']['open_file'] else: config = configparser.ConfigParser() config['DEFAULT'] = { 'list_directory': list_directory, 'edit_file': edit_file, 'pretty_print_file': pretty_print_file, 'print_file': print_file, 'help_command': help_command, 'path_locator': path_locator, 'list_git_directory': list_git_directory, 'open_file': open_file } print("echo Default config loaded.") with open(ENV_HOME+'/.config/do_what/what.config', 'w') as configfile: config.write(configfile) configfile.close() # check if active mode is enabled argiter = 0 active = False for arg in sys.argv: if ( str(arg) == '-' ): active = True break argiter += 1 # absolutize the path for python if ( (active and len(sys.argv) >= 3) or (not active and len(sys.argv) >= 2) ): filearg = sys.argv[1] path = sys.argv[1] if ( path[0] != "/" and path[0] != "~" and path[0] != "." ): path = os.getcwd()+"/"+path elif ( path[0] == "." and len(path) > 1 and ( path[:2] == "./" ) ): path = os.getcwd()+path[1:] elif ( path[0] == "." and len(path) > 1 and ( path[:2] == ".." ) ): path = os.getcwd()+"/"+path elif ( path[0] == "." and len(path) == 1 ): path = os.getcwd() # CLIpboard (ha.) if ( active and len(sys.argv) > argiter+1 and sys.argv[argiter+1] == 'c' ): subprocess.call(['rm', '-rf', ENV_HOME+'/.config/do_what/clipboard']) subprocess.call(['touch', ENV_HOME+'/.config/do_what/clipboard']) if ( filearg == '-' ): path = os.getcwd() if ( os.path.isdir(path) or os.path.isfile(path) ): with open(ENV_HOME+"/.config/do_what/clipboard", "w") as cb: cbdata = { 'action': 'copy', 'path': path } json.dump(cbdata, cb) print("echo Copied: "+path) exit() else: print("echo Unknown file/directory.") exit() elif ( active and len(sys.argv) > argiter+1 and sys.argv[argiter+1] == 'x' ): subprocess.call(['rm', '-rf', ENV_HOME+'/.config/do_what/clipboard']) subprocess.call(['touch', ENV_HOME+'/.config/do_what/clipboard']) if ( filearg == '-' ): path = os.getcwd() if ( os.path.isdir(path) or os.path.isfile(path) ): with open(ENV_HOME+"/.config/do_what/clipboard", "w") as cb: cbdata = { 'action': 'cut', 'path': path } json.dump(cbdata, cb) print("echo Cut: "+path) exit() else: print("echo Unknown file/directory.") exit() elif ( active and len(sys.argv) > argiter+1 and sys.argv[argiter+1] == 'p' ): if ( os.path.isfile(ENV_HOME+"/.config/do_what/clipboard") ): with open(ENV_HOME+"/.config/do_what/clipboard", "r") as cb: try: cbdata = json.load(cb) except json.decoder.JSONDecodeError: print("echo Invalid clipboard file.") exit() print("echo \""+str(cbdata)+"\"") if ( cbdata['action'] == 'copy' ): file_op = 'cp' elif ( cbdata['action'] == 'cut' ): file_op = 'mv' if ( os.path.isdir( cbdata['path'] ) and filearg == '-' ): print("echo Pasted directory: "+cbdata['path']) print(file_op+" -r "+cbdata['path']+" .") elif ( os.path.isdir( cbdata['path'] ) and os.path.isdir( path ) ): print("echo Pasted directory "+cbdata['path']+" in "+path) print(file_op+" -r "+cbdata['path']+" "+path) elif ( os.path.isdir( cbdata['path'] ) and os.path.isfile( path ) ): print("echo Cannot paste directory into regular file.") print("echo \"("+cbdata['path']+" -> "+path+")\"") elif ( os.path.isfile( cbdata['path'] ) and filearg == '-' ): print("echo Pasted file: "+cbdata['path']) print(file_op+" "+cbdata['path']+" .") elif ( os.path.isfile( cbdata['path'] ) and os.path.isdir( path ) ): print("echo Pasted file "+cbdata['path']+" in directory "+path) print(file_op+" "+cbdata['path']+" "+path) elif ( os.path.isfile( cbdata['path'] ) and os.path.isfile( path ) ): print("echo Pasted file "+cbdata['path']+" over existing file "+path) print(file_op+" "+cbdata['path']+" "+path) elif ( os.path.isfile( cbdata['path'] ) and filearg != '-' ): print("echo Pasted file "+cbdata['path']+" as new file "+path) print(file_op+" "+cbdata['path']+" "+path) elif ( os.path.isdir( cbdata['path'] ) and filearg != '-' ): print("echo Pasted directory "+cbdata['path']+" as new directory "+path) print(file_op+" "+cbdata['path']+" "+path) else: print("echo Invalid clipboard. Use \\\'c\\\' directive to copy a file or directory.") else: print("echo Empty clipboard. Use \\\'c\\\' directive to copy a file or directory.") exit() # File/Directory backup creator # Capital B = Time versioned backup elif ( active and len(sys.argv) > argiter+1 and sys.argv[argiter+1] == 'B' ): if ( filearg == '-' ): print("No file/directory specified to backup.") elif ( os.path.isfile( path ) ): bakname = ".bak-"+datetime.datetime.now().strftime("%Y-%B-%d_%I:%M%p") print("cp "+path+" "+path+bakname) print("echo Created file backup: "+path+bakname) elif( os.path.isdir( path ) ): bakname = ".bak-"+datetime.datetime.now().strftime("%Y-%B-%d_%I:%M%p") print("cp -r "+path+" "+path+bakname) print("echo Created directory backup: "+path+bakname) exit() # Lowercase b = un-versioned backup elif ( active and len(sys.argv) > argiter+1 and sys.argv[argiter+1] == 'b' ): if ( filearg == '-' ): print("No file/directory specified to backup.") elif ( os.path.isfile( path ) ): bakname = ".bak" print("cp "+path+" "+path+bakname) print("echo Created file backup: "+path+bakname) elif( os.path.isdir( path ) ): bakname = ".bak" print("cp -r "+path+" "+path+bakname) print("echo Created directory backup: "+path+bakname) exit() # Lowercase r = restore the specified backup elif ( active and len(sys.argv) > argiter+1 and sys.argv[argiter+1] == 'r' ): if ( filearg == '-' ): print("No file/directory specified to restore.") elif ( os.path.isfile( path ) ): if ( ".bak" not in path ): print("echo "+path+" is not a valid backup file.") else: restorepath = path.split(".bak", 1)[0] print("mv "+path+" "+restorepath) print("echo Restored file: "+restorepath) elif ( os.path.isdir( path ) ): if ( ".bak" not in path ): print("echo "+path+" is not a valid backup directory.") else: restorepath = path.split(".bak", 1)[0] print("mv "+path+" "+restorepath) print("echo Restored directory: "+restorepath) exit() elif ( active and len(sys.argv) > argiter+1 ): print("echo Invalid directive: "+sys.argv[argiter+1]) exit() # File Operations if ( (not active and len(sys.argv) == 1) or (active and len(sys.argv) == 2) ): # list the contents of the current directory if ( not active ): if ( os.path.isdir('.git') ): print(list_git_directory) else: print(list_directory) # print the working directory (active) elif ( active ): print(print_dir) elif ( (not active and len(sys.argv) == 2) or (active and len(sys.argv) == 3) ): # if directory, list its contents if ( os.path.isdir(path) and not active ): if ( os.path.isdir(path+"/.git") ): print(list_git_directory+" "+path) else: print(list_directory+" "+path) # if directory, change into it (active) elif ( os.path.isdir(path) and active ): print(change_dir+" "+path) # if file, display its contents elif ( os.path.isfile(path) and not active ): # check if file is binary if ( is_binary_file(path) ): filemime = mime.from_file(path) print("echo Binary file: "+filemime) if ( filemime == "inode/symlink" ): print("echo Symlink location: "+os.path.realpath(path)) else: trows, tcolumns = os.popen('stty size', 'r').read().split() # if file is taller than the terminal, pipe it to a pretty-print display if ( int(trows) - 5 < int(file_len(path)) ): set_runtime_var('LESSOPEN', '| /usr/share/source-highlight/src-hilite-lesspipe.sh %s') set_runtime_var('LESS', ' -R ') print(pretty_print_file+" "+path) # if file is shorter than the terminal, print its contents else: print(print_file+" "+path) # if file, open in editor (active) elif ( os.path.isfile(path) and active ): if ( is_binary_file(path) ): print(open_file+" "+path) else: if ( os.access(path, os.W_OK) ): print(edit_file+" "+path) else: print("sudo "+edit_file+" "+path) else: # check if file is a path program, then list using which/man on active path_has = env_has(filearg) # print the location of the program if ( path_has and not active ): print(path_locator+" "+filearg) # display the help page for the program (active) elif ( path_has and active ): print(help_command+" "+filearg) else: print("echo Unknown file/directory.")