Compare commits

...

18 Commits
v1.0 ... master

Author SHA1 Message Date
ivan 8ab5a13cca exception handling when a session subprocess is interrupted (f.e. ctr+c on password entry) 2024-04-05 14:28:53 +03:00
ivan fc9640a491 Merge branch 'fix-escape-sequence' into 'master'
Fix escape sequence

See merge request zlax/sshch!2
2024-04-05 11:18:39 +00:00
Anton Samofal 8ee868ab69 Fix escape sequence 2024-04-05 11:18:39 +00:00
ivan 2c5c87f555 add curses texpad del, home, end handlers 2022-06-17 21:55:01 +03:00
ivan cd501bdc1e add curses texpad Esc handler 2022-06-14 20:36:15 +03:00
ivan 37b3926880 merging CMDConnect and CursesConnect into the single Connect function 2022-06-14 12:33:32 +03:00
ivan abb2fcfa4c remove ThreadConnect func and help fixes 2022-06-14 11:52:00 +03:00
ivan 7d7e2ff775 py2 curses utf8 display support 2022-06-12 16:20:59 +03:00
ivan eddfedb6c3 parallel command execution for aliases and groups 2022-06-11 11:51:19 +03:00
ivan e11978957f parallel command execution for aliases and groups 2022-06-11 11:46:47 +03:00
ivan 032b4b1ab0 Add w/s as navigation 2021-09-22 18:58:18 +03:00
ivan 7fa8bdb6f9 Fix -f cmd output 2021-09-22 18:19:17 +03:00
ivan 10ca6ad15b Add GroupChildAliases function 2021-09-22 17:39:54 +03:00
ivan 48d491312a Add group tree (nested groups) 2021-09-22 16:31:11 +03:00
ivan 0bbf06340a Update 'README.md' 2019-09-05 13:42:24 +00:00
ivan ed10aa8f03 add exceptions 2018-11-28 20:46:18 +03:00
ivan 0fd4ff9b74 issue 10 fix 2018-11-28 14:30:35 +03:00
ivan a39cd7ae68 interface fixes 2018-11-28 14:27:06 +03:00
3 changed files with 358 additions and 175 deletions

View File

@ -4,7 +4,8 @@ sshch is released under DWTWL 2.55 license
sshch compatible with pyhon2 and python3, no additional libraries are required
### Screenshot
![sshch](https://raw.githubusercontent.com/zlaxy/sshch/master/sshch_screenshot.png)
![sshch](https://dev.ussr.win/zlax/sshch/raw/branch/master/sshch_screenshot.png)
### Installing
**You can install a release version from pip:**
```bash

View File

@ -8,15 +8,15 @@ def main():
setup(name='sshch',
author='zlaxy',
author_email='zlaxyi@gmail.com',
url='https://github.com/zlaxy/sshch/',
url='https://gitlab.com/zlax/sshch',
description='Ssh connection and aliases manager',
long_description='SSH connection and aliases manager with curses and command line interface',
long_description_content_type='text/x-rst',
license='DWTWL 2.55',
version='1.0',
version='1.09.7',
py_modules=['sshch'],
scripts=['sshch/sshch'],
keywords='sshch ssh aliases manager',
keywords='sshch ssh aliases curses manager',
python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4',
# http://pypi.python.org/pypi?%3Aaction=list_classifiers

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division
@ -7,7 +7,8 @@ try:
import configparser
except ImportError:
import ConfigParser as configparser # Python 2.x import
import locale
locale.setlocale(locale.LC_ALL, '')
from os import path
from sys import argv
from math import ceil
@ -18,13 +19,24 @@ import base64
import time
import curses
from curses import textpad, panel
from threading import Thread
# https://github.com/zlaxy/sshch
version = "1.0"
# path to conf file, default: ~/.config/sshch.conf
conf_file = path.expanduser("~") + '/.config/sshch.conf'
# https://gitlab.com/zlax/sshch
version = "1.09.7"
# expand groups by default
expand_default = True
# path to conf dir and file, default: ~/.config/sshch.conf
conf_dir = path.expanduser("~") + '/.config'
conf_file = conf_dir + '/' + 'sshch.conf'
class GroupTree(object):
"""Group object with relatives information"""
def __init__(self, group):
self.group = group
self.aliases = []
self.children = []
self.parent = []
def AddNewAlias(alias):
@ -64,17 +76,74 @@ def RemoveAliases(aliases):
conf.write(open(conf_file, "w"))
def ConnectAlias(alias, command=False):
def ConvertPassword(password):
password_string = "'"
for char in password:
if char == "'":
password_string += "'"+'"'+"'"+'"'+"'"
elif char == '"':
password_string += "''"+'"'+"''"
elif char == ';':
password_string += "'"+r'\;'+"'"
elif char == "\\":
password_string += "'"+'"'+"\\"+"\\"+'"'+"'"
else:
password_string += char
password_string += "'"
return password_string
def Connect(aliases, command=False, threading=False, screen=False):
if screen:
curses.endwin()
groups = []
connectaliases = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
for alias in aliases:
if alias in groups:
group_aliases = GroupChildAliases(alias)
for ga in group_aliases:
connectaliases.append(ga)
else:
connectaliases.append(alias)
connectaliases = setSeq(connectaliases)
threads = {}
for alias in connectaliases:
if conf.has_section(alias):
print("Connecting to " + alias + "...")
if threading:
threads[alias] = Thread(target=ConnectAlias, args=(alias,
command, True))
threads[alias].start()
else:
ConnectAlias(alias, command)
if not threading:
print("... " + alias + " session finished.")
else:
print("error: '" + alias + "' alias does not exists")
if screen:
print("Press 'enter' to continue.")
screen.getch()
def ConnectAlias(alias, command=False, threading=False):
exec_string = ""
if conf.has_option(alias, "password"):
password = base64.b32decode(base64.b16decode(
base64.b64decode(conf.get(alias, "password"))))
exec_string = 'sshpass -p "' + password.decode('utf-8') + '" '
exec_string = "sshpass -p " + ConvertPassword(password.decode('utf-8')) + " "
if conf.has_option(alias, "exec_string"):
exec_string = exec_string + conf.get(alias, "exec_string")
if command:
exec_string = exec_string + " " + command
subprocess.Popen(exec_string, shell=True).communicate()[0]
try:
subprocess.Popen(exec_string, shell=True).communicate()[0]
except:
pass
if threading:
print ("... "+alias+" session output finished.")
def HoldConnection(alias):
@ -124,8 +193,8 @@ def CMDGroup(group):
def CMDEdit(alias):
if conf.has_section(alias):
if conf.has_option(alias, "exec_string"):
prompt_edit = ("".join(["Enter connection string for existing alias ",
"(example: ssh user@somehost.com):\n"]))
prompt_edit = ("".join(["Enter connection string for existing ",
"alias (example: ssh user@somehost.com):\n"]))
string = ""
while string == "":
string = input(prompt_edit)
@ -153,7 +222,8 @@ def CMDPassword(alias):
print("Can't set password for group.")
else:
if conf.has_section(alias):
prompt_pass = ("[UNSAFE] Enter password for sshpass (Ctrl+C - cancel, blank - clear password):\n")
prompt_pass = ("[UNSAFE] Enter password for sshpass (Ctrl+C" +
" - cancel, blank - clear password):\n")
string = ""
string = getpass(prompt_pass)
SetPassword(alias, string)
@ -163,7 +233,8 @@ def CMDPassword(alias):
def CMDRemove(alias):
if conf.has_section(alias):
prompt_remove = ("Type 'yes' if you sure to remove '" + alias + "' alias or group: ")
prompt_remove = ("Type 'yes' if you sure to remove '" + alias +
"' alias or group: ")
string = input(prompt_remove)
if string == "yes":
RemoveAliases([alias])
@ -173,102 +244,113 @@ def CMDRemove(alias):
print("error: '" + alias + "' alias or group does not exists.")
def CMDConnect(aliases, command=False):
groups = []
connectaliases = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
for alias in aliases:
if alias in groups:
group_aliases = conf.get(alias, "group").split()
for ga in group_aliases:
if not ga in connectaliases:
connectaliases.append(ga)
else:
if not alias in connectaliases:
connectaliases.append(alias)
for alias in connectaliases:
if conf.has_section(alias):
print("Connecting to " + alias + "...")
ConnectAlias(alias, command)
print("... " + alias + " session finished.")
else:
print("error: '" + alias + "' alias does not exists")
def CMDList(option, opt, value, parser):
print(' '.join(str(p) for p in conf.sections()))
def GetTreeList(strings=True, expandlist=True):
def setSeq(seq):
# py2&3 fastest way to remove duplicates from a list:
# https://www.rupython.com/x432-4-496.html
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
def GroupChildRecursion(group, childaliases, treelist):
if treelist[group].children:
for a in treelist[group].children:
GroupChildRecursion(a, childaliases, treelist)
if treelist[group].aliases:
for a in treelist[group].aliases:
childaliases.append(a)
return childaliases
def GroupChildAliases(group):
aliases = []
groups = []
resultalias = []
resultstring = []
rootgroups = []
treelist = {}
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
elif conf.has_option(a, "exec_string"):
aliases.append(a)
rootaliases = list(aliases)
for g in groups:
resultalias.append(g)
resultstring.append(">> "+g)
treelist[g] = GroupTree(g)
group_aliases = conf.get(g, "group").split()
for ga in group_aliases:
if expandlist == False:
pass
elif expandlist == True or g in expandlist:
if conf.has_option(ga, "exec_string"):
resultalias.append(ga+" "+g)
result = "".join([" ", str(ga), " (", (conf.get(ga, "exec_string") if
conf.has_option(ga, "exec_string") else ""), ")",
(" [password]" if conf.has_option(ga, "password") else "")])
resultstring.append(result)
try:
aliases.remove(ga)
except ValueError:
pass
for a in aliases:
if ga in groups:
treelist[g].children.append(ga)
elif ga in aliases:
treelist[g].aliases.append(ga)
try:
rootaliases.remove(ga)
except ValueError:
pass
for g in groups:
if treelist[g].children:
for child in treelist[g].children:
treelist[child].parent.append(g)
for g in groups:
if not treelist[g].parent:
rootgroups.append(g)
if group == False:
return aliases, groups, rootaliases, rootgroups, treelist
else:
childaliases = GroupChildRecursion(group, [], treelist)
return setSeq(childaliases)
def GroupTreeRecursion(level, group, treelist, resultalias, resultstring,
expandlist, previousgroups):
if group in previousgroups:
return resultalias, resultstring
previousgroups.append(group)
resultalias.append(group)
if expandlist == True or group in expandlist:
resultstring.append(' '*(level-1)+">> "+group)
for g in treelist[group].children:
resultalias, resultstring = GroupTreeRecursion(level+1, g,
treelist, resultalias, resultstring, expandlist, previousgroups)
for ga in treelist[group].aliases:
if conf.has_option(ga, "exec_string"):
resultalias.append(ga)
result = "".join([' '*(level-1)+" ~ ", str(ga), " (",
(conf.get(ga, "exec_string") if conf.has_option(ga,
"exec_string") else ""), ")",
(" [password]" if conf.has_option(ga, "password") else "")])
resultstring.append(result)
else:
resultstring.append(' '*(level-1)+"<> "+group)
return resultalias, resultstring
def GetTreeList(strings=True, expandlist=True):
resultalias = []
resultstring = []
aliases, groups, rootaliases, rootgroups, treelist = GroupChildAliases(False)
for g in rootgroups:
resultalias, resultstring = GroupTreeRecursion(1, g, treelist,
resultalias, resultstring, expandlist, [])
for a in rootaliases:
resultalias.append(a)
result = "".join([str(a), " (", (conf.get(a, "exec_string") if
conf.has_option(a, "exec_string") else ""), ")",
(" [password]" if conf.has_option(a, "password") else "")])
resultstring.append(result)
if strings:
return resultstring;
return resultstring
else:
return resultalias;
return resultalias
def CMDFullList(option, opt, value, parser):
for p in GetTreeList():
print (p)
def CursesConnect(screen, aliases, command=False):
curses.endwin()
groups = []
connectaliases = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
for alias in aliases:
if alias in groups:
group_aliases = conf.get(alias, "group").split()
for ga in group_aliases:
if not ga in connectaliases:
connectaliases.append(ga)
else:
if not alias in connectaliases:
connectaliases.append(alias)
for alias in connectaliases:
print("Connecting to " + alias + "...")
ConnectAlias(alias, command)
print("... " + alias + " session finished.")
print("Press 'enter' to continue.")
screen.getch()
def CursesExit(error=False):
curses.endwin()
if error:
@ -276,9 +358,21 @@ def CursesExit(error=False):
exit()
class CursesTextpadEsc(Exception):
"ESC key has been pressed"
def CursesTextpadConfirm(value):
if value == 10:
if value == 10: # Enter
value = 7
elif value == 27: # Esc
raise CursesTextpadEsc()
elif value == curses.KEY_DC: # Del
value = curses.ascii.EOT
elif value == curses.KEY_HOME: # Home
value = curses.ascii.SOH
elif value == curses.KEY_END: # End
value = curses.ascii.ENQ
return value
@ -351,19 +445,20 @@ def CMDOptions():
def format_epilog(self, formatter):
return self.epilog
usage = "usage: %prog [options] [aliases]"
progname = path.basename(__file__)
epilog = ("".join(["Examples:\n ",
progname, " existingalias\n ",
progname, " -a newremoteserver\n ",
progname, " --edit=newremoteserver -p newremoteserver\n ",
progname, ' -c "ls -l" newremoteserver\n ',
progname, " -c reboot existingalias newremoteserver\n",
"Examples of connection string:\n ",
"ssh user@somehost.com\n ",
"ssh gates@8.8.8.8 -p 667\n ",
"ssh root@somehost.com -t tmux a\n",
"Also, you can edit config file manually: ", conf_file, "\n"]))
progname, " existingalias\n ",
progname, " -a newremoteserver\n ",
progname, " --edit=newremoteserver -p newremoteserver\n ",
progname, ' -c "ls -l" newremoteserver\n ',
progname, " -c reboot existingalias newremoteserver\n",
"Examples of connection string:\n ",
"ssh user@somehost.com\n ",
"ssh gates@8.8.8.8 -p 667\n ",
"ssh root@somehost.com -t tmux a\n",
"Also, you can edit the config file manually: ", conf_file, "\n"]))
opts = FormatedParser(usage=usage, version="%prog " + version,
epilog=epilog)
opts.add_option('-l', '--list', action="callback",
@ -379,7 +474,11 @@ def CMDOptions():
help="add new group for aliases")
opts.add_option('-c', '--command', action="store", type="string",
dest="command", metavar="command", default=False,
help="execute command for aliases or group")
help="execute command for aliases and groups")
opts.add_option('-t', '--thread', action="store", type="string",
dest="thread", metavar="command", default=False,
help="parallel command execution for aliases and groups " +
"(ssh key authentication or set password required)" )
opts.add_option('-k', '--keep', action="store", type="string",
dest="keep", metavar="alias", default=False,
help="hold connection with specified alias")
@ -405,40 +504,44 @@ def CMDOptions():
CMDRemove(options.remove)
if options.keep:
HoldConnection(options.keep)
if alias:
CMDConnect(alias, options.command)
if options.thread:
Connect(alias, options.thread, True)
elif alias:
Connect(alias, options.command)
# curses template from: https://stackoverflow.com/a/30828805/6224462
def CursesMain():
help_screen = ("".join([" Press:\n",
" 'z'/'x' or arrows - navigation\n",
" 'a'/'F2' - add new alias (without spaces)\n",
" 'g'/'F5' - add new group (spaces will be stripped)\n",
" 'e'/'F4' - edit existing alias/group\n",
" 'p'/'F6' - set alias's password for sshpass [UNSAFE]\n",
" 'space'/'insert' - select\n",
" 'r'/'F8' - remove selected alias/aliases\n",
" 'c'/'F3' - execute specific command with selected alias/aliases\n",
" 'k'/'F7' - hold connection with selected alias\n",
" 'enter'/'F9' - connect to selected alias/aliases,\n",
" expand/collapse group\n",
" 'q'/'F10' - quit\n",
" Run program with '--help' option to view command line help.\n",
" Also, you can edit config file manually:\n",
" ", conf_file]))
" 'z'/'x', 'w'/'s' or arrows - navigation\n",
" 'a'/'F2' - add new alias (without spaces)\n",
" 'g'/'F5' - add new group (spaces will be stripped)\n",
" 'e'/'F4' - edit existing alias/group\n",
" 'p'/'F6' - set alias's password for sshpass [UNSAFE]\n",
" 'space'/'insert' - select\n",
" 'r'/'F8' - remove selected alias/aliases\n",
" 'c'/'F3' - command execution for alias (group/aliases - in turn)\n",
" 't'/'F11' - parallel command execution for aliases and groups\n",
" (ssh key authentication or set password required)\n",
" 'k'/'F7' - hold connection with selected alias\n",
" 'enter'/'F9' - connect to selected alias/aliases,\n",
" expand/collapse group\n",
" 'q'/'F10' - quit\n",
" Run program with '--help' option to view command line help.\n",
" Also, you can edit the config file manually:\n",
" ", conf_file]))
if expand_default == True:
groups = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
expanded = groups
expanded = list(groups)
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
elif expand_default == False:
groups = []
expanded = groups
expanded = []
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings)
@ -490,23 +593,31 @@ def CursesMain():
'A') or key_pressed == curses.KEY_F2:
curses.curs_set(1)
new_alias_textpad = CursesTextpad(screen, 1, width - 8,
(height // 2) - 1, 4, "Enter new alias:", "",
normal_text, highlight_text)
add_alias = new_alias_textpad.edit(CursesTextpadConfirm)
add_alias = add_alias.split()[0].strip()
(height // 2) - 1, 4, "Enter new alias:", "",
normal_text, highlight_text)
try:
add_alias = new_alias_textpad.edit(CursesTextpadConfirm)
except CursesTextpadEsc:
add_alias = ""
if not add_alias == "":
add_alias = add_alias.split()[0].strip()
if not add_alias == "":
add_result = AddNewAlias(add_alias)
if add_result == True:
add_string = ""
while add_string == "":
string_textpad = CursesTextpad(screen, 3,
width - 8, (height // 2) - 1, 4,
"Enter full execution string:",
"ssh ", normal_text, highlight_text)
add_string = string_textpad.edit(
CursesTextpadConfirm)
SetAliasString(add_alias,
add_string.replace("\n", "").rstrip())
try:
while add_string == "":
string_textpad = CursesTextpad(screen, 3,
width - 8, (height // 2) - 1, 4,
"Enter full execution string:",
"ssh ", normal_text, highlight_text)
add_string = string_textpad.edit(
CursesTextpadConfirm)
SetAliasString(add_alias,
add_string.replace("\n", "").rstrip())
except CursesTextpadEsc:
RemoveAliases([add_alias])
add_alias = ""
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings)
@ -523,24 +634,32 @@ def CursesMain():
'G') or key_pressed == curses.KEY_F5:
curses.curs_set(1)
new_group_textpad = CursesTextpad(screen, 1, width - 8,
(height // 2) - 1, 4, "Enter group name (without spaces):", "",
normal_text, highlight_text)
add_group = new_group_textpad.edit(CursesTextpadConfirm)
add_group = add_group.split()[0].strip()
(height // 2) - 1, 4, "Enter group name (without spaces):", "",
normal_text, highlight_text)
try:
add_group = new_group_textpad.edit(CursesTextpadConfirm)
except CursesTextpadEsc:
add_group = ""
if not add_group == "":
add_group = add_group.split()[0].strip()
if not add_group == "":
add_result = AddNewAlias(add_group)
if add_result == True:
add_string = ""
while add_string.rstrip() == "":
string_textpad = CursesTextpad(screen, 3,
width - 8, (height // 2) - 1, 4,
"Enter aliases for new group (example: alias1 alias2):",
"", normal_text, highlight_text)
add_string = string_textpad.edit(
CursesTextpadConfirm)
SetGroupString(add_group,
add_string.replace("\n", "").rstrip())
expanded.append(add_group)
try:
while add_string.rstrip() == "":
string_textpad = CursesTextpad(screen, 3,
width - 8, (height // 2) - 1, 4,
"Enter aliases for new group (example: alias1 alias2):",
"", normal_text, highlight_text)
add_string = string_textpad.edit(
CursesTextpadConfirm)
SetGroupString(add_group,
add_string.replace("\n", "").rstrip())
expanded.append(add_group)
except CursesTextpadEsc:
RemoveAliases([add_group])
add_group = ""
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings)
@ -564,12 +683,17 @@ def CursesMain():
if strings[position - 1] in groups:
while edit_string.rstrip() == "":
string_textpad = CursesTextpad(screen, 3, width - 8,
(height // 2) - 1, 4, "Enter new aliases for existing group:",
(conf.get(strings[position - 1],
"group") if conf.has_option(strings[position - 1],
"group") else ""),
normal_text, highlight_text)
edit_string = string_textpad.edit(CursesTextpadConfirm)
(height // 2) - 1, 4, "Enter new aliases for existing group:",
(conf.get(strings[position - 1],
"group") if conf.has_option(strings[position - 1],
"group") else ""),
normal_text, highlight_text)
try:
edit_string = string_textpad.edit(CursesTextpadConfirm)
except CursesTextpadEsc:
edit_string = conf.get(strings[position - 1],
"group") if conf.has_option(strings[position - 1],
"group") else ""
SetGroupString(strings[position - 1],
edit_string.replace("\n", "").rstrip())
strings = GetTreeList(False, expanded)
@ -581,12 +705,17 @@ def CursesMain():
else:
while edit_string.rstrip() == "":
string_textpad = CursesTextpad(screen, 3, width - 8,
(height // 2) - 1, 4, "Enter new execution string:",
(conf.get(strings[position - 1].split()[0].strip(),
"exec_string") if conf.has_option(strings[position - 1].split()[0].strip(),
"exec_string") else ""),
normal_text, highlight_text)
edit_string = string_textpad.edit(CursesTextpadConfirm)
(height // 2) - 1, 4, "Enter new execution string:",
(conf.get(strings[position - 1].split()[0].strip(),
"exec_string") if conf.has_option(strings[position - 1].split()[0].strip(),
"exec_string") else ""),
normal_text, highlight_text)
try:
edit_string = string_textpad.edit(CursesTextpadConfirm)
except CursesTextpadEsc:
edit_string = conf.get(strings[position - 1].split()[0].strip(),
"exec_string") if conf.has_option(strings[position - 1].split()[0].strip(),
"exec_string") else ""
SetAliasString(strings[position - 1].split()[0].strip(),
edit_string.replace("\n", "").rstrip())
strings = GetTreeList(False, expanded)
@ -601,9 +730,9 @@ def CursesMain():
if not strings[position - 1].split()[0].strip() in groups:
set_password = ""
set_password = CursesPanel(screen, 4, width - 6,
(height // 2) - 1, 3,
" Enter user password for sshpass and press 'enter':\n>",
normal_text, highlight_text, "password")
(height // 2) - 1, 3,
" Enter user password for sshpass and press 'enter':\n>",
normal_text, highlight_text, "password")
SetPassword(strings[position - 1].split()[0].strip(), set_password)
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
@ -620,11 +749,11 @@ def CursesMain():
str(len(selected)), " selected aliases? (y/N)"]))
else:
remove_confirm = ("".join(["Are you sure to remove '",
strings[position - 1].split()[0].strip(), "' alias? (y/N)"]))
strings[position - 1].split()[0].strip(), "' alias? (y/N)"]))
selected.append(strings[position - 1].split()[0].strip())
remove_result = CursesPanel(screen, 4, width - 6,
(height // 2) - 1, 3, remove_confirm, normal_text,
highlight_text, "remove")
(height // 2) - 1, 3, remove_confirm, normal_text,
highlight_text, "remove")
if remove_result == "confirm":
RemoveAliases(selected)
strings = GetTreeList(False, expanded)
@ -645,14 +774,39 @@ def CursesMain():
selected.append(strings[position - 1].split()[0].strip())
curses.curs_set(1)
command_textpad = CursesTextpad(screen, 3, width - 8,
(height // 2) - 1, 4,
"".join([
"Enter specific command to execute with selected ",
"alias/aliases:"]
), "", normal_text, highlight_text)
command_string = command_textpad.edit(CursesTextpadConfirm)
CursesConnect(screen, selected,
command_string.replace("\n", "").rstrip())
(height // 2) - 1, 4,
"".join([
"Enter specific command to execute with selected ",
"alias/aliases:"]
), "", normal_text, highlight_text)
try:
command_string = command_textpad.edit(CursesTextpadConfirm)
Connect(selected, command_string.replace("\n", "").rstrip(),
False, screen)
except CursesTextpadEsc:
command_string = ""
curses.curs_set(0)
if (key_pressed == ord('t') or key_pressed == ord(
'T') or key_pressed == curses.KEY_F11) and row_num != 0:
selected = []
for i in range(1, row_num + 1):
if selected_strings[i] == "*":
selected.append(strings[i - 1])
if not len(selected) > 0:
selected.append(strings[position - 1].split()[0].strip())
curses.curs_set(1)
command_textpad = CursesTextpad(screen, 3, width - 8,
(height // 2) - 1, 4,
"".join([
"Enter specific command to execute with selected ",
"alias/aliases:"]
), "", normal_text, highlight_text)
try:
command_string = command_textpad.edit(CursesTextpadConfirm)
Connect(selected, command_string.replace("\n", "").rstrip(),
True, screen)
except CursesTextpadEsc:
command_string = ""
curses.curs_set(0)
if (key_pressed == ord('k') or key_pressed == ord('K') or
key_pressed == (curses.KEY_F7)) and row_num != 0:
@ -687,7 +841,7 @@ def CursesMain():
selected.append(strings[i - 1])
if not len(selected) > 0:
selected.append(strings[position - 1].split()[0].strip())
CursesConnect(screen, selected)
Connect(selected, False, False, screen)
selected_strings = [" " for i in range(0, row_num + 1)]
if (key_pressed == 32 or key_pressed == (
curses.KEY_IC)) and row_num != 0:
@ -712,7 +866,8 @@ def CursesMain():
page = page + 1
position = 1 + (max_row * (page - 1))
if key_pressed == curses.KEY_DOWN or key_pressed == ord(
'x') or key_pressed == ord('X'):
'x') or key_pressed == ord('X') or key_pressed == ord(
's') or key_pressed == ord('S'):
if page == 1:
if position < i:
position = position + 1
@ -730,7 +885,8 @@ def CursesMain():
page = page + 1
position = 1 + (max_row * (page - 1))
if key_pressed == curses.KEY_UP or key_pressed == ord(
'z') or key_pressed == ord('Z'):
'z') or key_pressed == ord('Z') or key_pressed == ord(
'w') or key_pressed == ord('W'):
if page == 1:
if position > 1:
position = position - 1
@ -786,10 +942,29 @@ if __name__ == "__main__":
except NameError:
pass
if not path.exists(conf_dir):
try:
from os import makedirs
makedirs(conf_dir)
except:
print("Can't make dir " + conf_dir)
exit()
conf = configparser.RawConfigParser()
if not path.exists(conf_file):
open(conf_file, 'w')
conf.read(conf_file)
try:
open(conf_file, 'w')
except:
print("Can't make file at " + conf_dir)
exit()
try:
conf.read(conf_file)
except:
print("Error: can't read config file " + conf_file)
exit()
if len(argv) > 1:
try:
CMDOptions()
@ -798,6 +973,10 @@ if __name__ == "__main__":
except configparser.Error:
print("Error: can't parse your config file, please check it manually or make new one")
exit()
except IOError:
print("Error: can't use your config file, please check permissionss of " + conf_file)
exit()
else:
try:
CursesMain()
@ -809,3 +988,6 @@ if __name__ == "__main__":
except curses.error:
CursesExit("".join(["Error: can't show some curses element, maybe ",
"your terminal is too small"]))
except IOError:
CursesExit("".join(["Error: can't use your config file, please ",
"check permissionss of ", conf_file]))