forked from sloumdrone/chalk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchalk
executable file
·238 lines (209 loc) · 8.09 KB
/
chalk
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
#!/usr/bin/env python3
#
# Chalk: A line mode text editor
# by Brian Evans
# You may copy or modify at will for profit or not so long
# as any usage is attributed and it released with these same
# provisions
#_
import sys
import os
import subprocess
import re
import readline
class c:
black = ''
red = '\033[0;31m'
b_red = '\033[1;31m'
yellow = '\033[1;33m'
green = '\033[0;32m'
b_green = '\033[1;32m'
cyan = '\033[0;36m'
b_cyan = '\033[1;36m'
purple = '\033[1;35m'
blue = '\033[0;34m'
b_blue = '\033[1;34m'
white = '\033[1;37m'
end = '\033[0m'
def input_editable(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
def validate_filename(filename):
if re.match(r'^[\w.\-]{1,100}', filename):
return True
return False
def validate_path(path):
if not path[0] in ['.','~','/']:
path = './{}'.format(path)
temp = path.split('/')
temp.pop()
temp = '/'.join(temp)
loc = subprocess.run(['ls',temp], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if loc.returncode > 0:
return False
if temp[-1] == '/':
temp = temp[:-1]
if filepath:
temp = temp + '/' + path.split('/')[-1]
return temp
def chalk(path):
fn = path.split('/')[-1]
valid_fn = validate_filename(fn)
if not valid_fn:
print('Invalid filename')
sys.exit(2)
valid_path = validate_path(path)
if not valid_path:
print('Invalid path')
sys.exit(2)
header = "{:7} 5 10 15 20 25 30 35 40 45 50 55 60 65\n{:7} ....|....|....|....|....|....|....|....|....|....|....|....|....|".format(' ',' ')
helptext = [
"",
"{}Commands are entered as the only entry for their row:{}".format(c.yellow, c.end),
" {}.{} - Finish writing/exit, will prompt for save".format(c.b_green, c.end),
" {}!?{} - Print this help message".format(c.b_green, c.end),
" {}!d{} - Display the contents of the file".format(c.b_green, c.end),
" {}!v{} - View lines, prompt will request a range of lines".format(c.b_green, c.end),
" {}!#{} - Rewrite a line, # is replaced by line number e.g. !27".format(c.b_green, c.end),
" {}!x{} - Delete line(s), prompt will request line or range".format(c.b_green, c.end),
" {}!i{} - Insert line(s), prompt will request start point and optional quantity".format(c.b_green, c.end),
" {}!g{} - Print the ruler/guide".format(c.b_green, c.end),
""
]
content = []
if os.path.exists(valid_path):
with open(valid_path,'r') as f:
content = f.read().split('\n')
if content[-1] == '':
content.pop()
edited_file = False
print('\n Chalk 0.8 by sloum')
print('\n{} Writing:{} {}{}'.format(c.yellow, c.white, fn, c.end))
print(" For a command list, enter {}!?\n{}".format(c.green, c.end))
print(header)
while True:
ln = input('{:6} {}>{} '.format(len(content), c.yellow, c.end))
if ln == '.':
break
elif ln == '!?':
print('')
for x in helptext:
print('{:8} {}'.format(' ',x))
print('')
print(header)
elif ln == '!g':
print('')
print(header)
elif ln == '!d':
print('')
for i, x in enumerate(content):
print('{:6} - {}{}{}'.format(i, c.green, x, c.end))
print('')
print(header)
elif ln == '!x':
print('\n{:8} {}Enter line to delete, for a range enter the start and\n{:8} end separated by a space. To cancel, enter -1.\n{:8} Deletion cannot be undone. Be careful.{}'.format(' ', c.cyan, ' ', ' ', c.end))
delete = input('{:6} {}>{} '.format(' ', c.b_red, c.end))
entry = delete.split(' ')
try:
beg = int(entry[0])
end = beg + 1
if len(entry) > 1:
end = int(entry[1]) + 1
if beg < 0:
continue
content = content[:beg] + content[end:]
edited_file = True
except:
print('{}{:8} Invalid entry{}'.format(c.red, ' ', c.end))
print('')
elif ln == '!i':
print('\n{:8} {}Enter the line number that you want the new line(s) to\n{:8} appear after.To insert multiple lines enter the starting\n{:8} point and the number of lines separated by a space.\n{:8}To cancel, enter -1{}'.format(' ', c.cyan,' ', ' ', ' ', c.end))
insert = input('{:6} {}>{} '.format(' ', c.b_green, c.end))
entry = insert.split(' ')
try:
beg = int(entry[0]) + 1
count = 1
if len(entry) > 1:
count = int(entry[1])
if beg < 0 or count < 1:
continue
while count > 0:
content.insert(beg,'')
count -= 1
edited_file = True
except:
print('{}{:8} Invalid entry{}'.format(c.red, ' ', c.end))
print('')
elif ln == '!v':
print('\n{:8} {}Enter the start & end, separated by a space, to indicate\n{:8} the range you wish to view. To cancel, enter: -1{}'.format(' ', c.cyan, ' ', c.end))
view = input('{:6} {}>{}'.format(' ', c.yellow, c.end))
entry = view.split(' ')
try:
beg = int(entry[0])
if beg < 0:
continue
if len(entry) < 2 or beg > int(entry[1]):
print('{}{:8} Invalid entry, must provide two numbers separated by a space{}'.format(c.red, ' ', c.end))
continue
end = int(entry[1])
end = end if end < len(content) else len(content) - 1
if beg > end:
print('{}{:8} Invalid entry, start position must be less than end position.{}'.format(c.red, ' ', c.end))
continue
else:
for x in range(beg, end + 1):
print('{:6} - {}{}{}'.format(x, c.green, content[x], c.end))
print('')
print(header)
except:
print('{}{:8} Invalid entry{}'.format(c.red, ' ', c.end))
elif re.match(r'^\!\d+$',ln):
try:
row = int(ln[1:])
newln = input_editable('{:6} {}>{} '.format(row, c.b_blue, c.end), content[row])
if newln == '!c':
print('{:8} Cancelled...'.format(' '))
else:
content[row] = newln
edited_file = True
except:
print('{}{:8} Invalid entry!{}'.format(c.b_red, ' ', c.end))
print('')
print(header)
else:
edited_file = True
content.append(ln)
edited_file = True
if not edited_file:
print('{}exiting...{}\n'.format(c.white, c.end))
sys.exit(0)
confirmation = ''
while confirmation.lower() not in ['y','yes','n','no']:
confirmation = input('{}Save {}?{} (Y/n) '.format(c.b_green, fn, c.end))
if not len(confirmation):
continue
if confirmation.lower()[0] == 'y':
print('{}saving...{}'.format(c.white, c.end))
text = '\n'.join(content)
text += '\n'
try:
with open(valid_path, 'w') as f:
f.write(text)
print('Done.\n')
sys.exit(0)
except:
print('{} You do not have permission to write to this file.{}'.format(c.red, c.end))
sys.exit(1)
else:
print('{}exiting...{}\n'.format(c.white, c.end))
sys.exit(0)
if __name__ == '__main__':
args = sys.argv
if len(args) < 2:
print('Incorrect number of arguments.')
sys.exit(1)
filepath = args[1]
chalk(filepath)