/
c
executable file
·307 lines (257 loc) · 8.71 KB
/
c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#!/usr/bin/env python3
# 2012 (c) Grigory Bakunov <thebobuk@ya.ru>
# Copying with bad intents prohibited by real patsans law
import sys
import os
import json
import itertools
import shutil
import platform
commands = ['c', 'v', 'del', 'p', 'R', 'h', ]
class C:
N = ''
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
@classmethod
def disable(cls):
cls.HEADER = ''
cls.OKBLUE = ''
cls.OKGREEN = ''
cls.WARNING = ''
cls.FAIL = ''
cls.ENDC = ''
class Storage:
def __init__(self, path=os.path.expanduser("~/.ctrl-buff")):
self.path = path
self.clean()
def clean(self):
self.files = []
self.dirs = []
return self
def load(self):
try:
with open(self.path, 'r') as fl:
(self.files, self.dirs) = json.loads(fl.read())
except IOError:
pass
return self
def save(self):
with open(self.path, 'w') as fl:
fl.write(json.dumps((
list(set(self.files)),
list(set(self.dirs)),
)))
return self
class normal_cmd:
"Dumb command"
AUTOLOAD = False
NEEDFILES = False
AUTOSAVE = False
AUTOCLEAN = False
def __init__(self):
self.args = []
self.quiet = False
self.canclean = True
if self.AUTOLOAD:
self.storage = Storage()
self.storage.load()
if self.NEEDFILES and not (self.storage.files or self.storage.dirs):
raise Exception("Please select some files or dirs with ctrl+c")
def output(self, mode, string):
if not self.quiet or (mode == C.FAIL):
sys.stdout.write(mode + string + C.ENDC + '\n')
def with_args(self, args):
if '-h' in args or '--help' in args:
self.output(C.N, 'Help: ' + self.__doc__)
sys.exit(-2)
if '-q' in args or '--quiet' in args:
self.quiet = True
for x in ['-q', '--quiet']:
if x in args:
args.remove(x)
if '-2' in args or '--not-clean' in args:
self.canclean = False
for x in ['-2', '--not-clean']:
if x in args:
args.remove(x)
self.args += args
def count(self):
try:
sf = (str(len(self.storage.files)) + ' files') if self.storage.files else ''
sd = (str(len(self.storage.dirs)) + ' directories') if self.storage.dirs else ''
if sf and sd:
return sf + ' and ' + sd
else:
return sf if sf else sd
except:
return ''
def clean(self):
if self.canclean:
self.storage.clean()
else:
self.output(C.WARNING, 'Buffer not wiped.')
def doit(self):
self.do()
if self.AUTOCLEAN:
self.clean()
if self.AUTOSAVE:
self.storage.save()
def process(self):
pass
def do(self):
raise Exception('Not Implemented')
class del_cmd(normal_cmd):
"""{ctrl+del} remove selected files to trash"""
AUTOLOAD = True
NEEDFILES = True
AUTOSAVE = True
AUTOCLEAN = True
def do(self):
if input("Are you sure to remove " + self.count() + ' files (y/n)') in ['y', 'Y', 'yes']:
try:
from send2trash import send2trash
except:
send2trash = shutil.rmtree
for item in itertools.chain(self.storage.files, self.storage.dirs):
self.output(C.OKGREEN, 'move to trash ' + item)
send2trash(item)
self.output(C.OKBLUE, 'Done')
else:
self.output(C.WARNING, '\tCanceled')
class R_cmd(normal_cmd):
"""{ctrl+R} reset list of selection"""
# We can't do just AUTOCLEAN/AUTOSAVE because I want to ignore -2 argument
def process(self):
self.storage = Storage()
def do(self):
self.storage.save()
class h_cmd(normal_cmd):
"""{ctrl+h} show list of commands"""
def process(self):
self.cmdlist = [eval(x + '_cmd').__doc__ for x in commands]
def do(self):
self.output(C.HEADER, 'List of commands:')
for cmd in self.cmdlist:
if sys.platform == 'darwin':
cmd = cmd.replace('{ctrl+', '{cmd+')
r = cmd.replace('{', C.OKGREEN).replace('}', C.ENDC)
self.output(C.N, '\t' + r)
class p_cmd(normal_cmd):
"""{ctrl+p} show a list of marked files"""
AUTOLOAD = True
def do(self):
for x in self.storage.dirs:
self.output(C.OKBLUE, 'D\t' + x)
for x in self.storage.files:
self.output(C.N, '.\t' + x)
class c_cmd(normal_cmd):
"""{ctrl+c} mark files and directories for copying or moving"""
AUTOLOAD = True
AUTOSAVE = True
def process(self):
self.files = []
self.dirs = []
if not self.args:
self.args.append(os.getcwd())
for x in self.args:
p = os.path.realpath(x)
if not os.path.exists(p):
raise Exception('File ' + p + ' not found')
if os.path.isfile(p):
self.files.append(p)
elif os.path.isdir(p):
self.dirs.append(p)
else:
raise Exception('Unknown filetype "' + p + '"')
def do(self):
for x in self.dirs:
self.output(C.OKBLUE, '+D ' + x)
self.storage.dirs += self.dirs
for x in self.files:
self.output(C.N, '+. ' + x)
self.storage.files += self.files
class vx_cmd_proto(normal_cmd):
AUTOLOAD = True
NEEDFILES = True
AUTOSAVE = True
AUTOCLEAN = True
def vname(self, x, topath=os.getcwd()):
name = os.path.basename(x)
overwrite = False
if os.path.exists(os.path.join(topath, name)):
self.output(C.WARNING, 'File ' + name + ' exists.')
try:
newname = input('Old name `' + C.OKGREEN + name + C.ENDC + "', new name?\n>> ")
except KeyboardInterrupt:
newname = False
if not newname:
raise Exception('\nAbort.')
if newname.startswith('*'):
newname = name + newname[1:]
elif newname.startswith('!'):
newname = name
overwrite = True
if os.path.exists(os.path.join(topath, newname)) and not overwrite:
raise Exception('Name `' + newname + "' exists.")
else:
newname = name
newname = os.path.join(topath, newname)
return (newname, overwrite)
class v_cmd(vx_cmd_proto):
"""{ctrl+v} copy files and other stuff to current directory"""
def process(self):
if not self.args:
self.args.append(os.getcwd())
def do(self):
for curpath in self.args:
self.output(C.HEADER, 'Copying to `' + curpath + "'")
for x in self.storage.dirs:
newname, overwrite = self.vname(x, curpath)
if not overwrite:
self.output(C.OKBLUE, 'D ' + x + ' -> ' + newname)
shutil.copytree(x, newname, symlinks=True)
else:
self.output(C.WARNING, 'D ' + x + ' -> ' + newname + ' overwrite')
suffix = '.bak.bak.bak'
shutil.copytree(x, newname + suffix, symlinks=True)
shutil.rmtree(newname)
for x in self.storage.files:
newname, overwrite = self.vname(x, curpath)
if overwrite:
self.output(C.WARNING, '. ' + x + ' -> ' + newname + ' overwrite')
shutil.copy2(x, newname)
else:
self.output(C.OKGREEN, '. ' + x + ' -> ' + newname)
shutil.copy2(x, newname)
def argparse_and_cmd(args=sys.argv[1:]):
if len(args) < 1:
raise Exception('First argument must be one of this: ' + ', '.join(commands))
cmd = eval(args[0] + '_cmd()')
cmd.with_args(args[1:])
cmd.process()
return cmd
def fetchname(name=sys.argv[0]):
name = os.path.basename(name)
if '+' in name:
return name.rsplit('+')[-1]
if '-' in name:
return name.rsplit('-')[-1]
return
if __name__ == '__main__':
try:
if platform.system().lower() == 'windows':
C.disable()
name = fetchname()
if name:
cmd = argparse_and_cmd([name] + sys.argv[1:])
else:
cmd = argparse_and_cmd()
if cmd:
cmd.doit()
except Exception as e:
sys.stderr.write(C.FAIL + str(e) + C.ENDC + '\n')
sys.exit(-1)