pmacs3/application.py

815 lines
30 KiB
Python
Raw Normal View History

2007-03-06 10:05:38 -05:00
#!/usr/bin/env python
import curses, curses.ascii, getpass, os, re, string, sys, termios, time
import traceback
import buffer, bufferlist, color, completer, keyinput, method, minibuffer
import mode, point, sets, util, window
# modes
import mode, mode_c, mode_mini, mode_python, mode_nasm, mode_perl, mode_search
import mode_replace, mode_xml, mode_console, mode_sh, mode_text, mode_which
import mode_mutt, mode_sql, mode_javascript, mode_diff, mode_blame, mode_tt
def run(buffers, jump_to_line=None, init_mode=None):
# save terminal state so we can restore it when the program exits
attr = termios.tcgetattr(sys.stdin)
keyinput.disable_control_chars()
retval = 1
try:
retval = curses.wrapper(run_app, buffers, jump_to_line, init_mode)
except:
traceback.print_exc()
# restore terminal state
termios.tcsetattr(sys.stdin, termios.TCSANOW, attr)
return retval
def run_app(stdscr, buffers, jump_to_line=None, init_mode=None):
a = Application(stdscr, buffers, jump_to_line, init_mode)
a.run()
KILL_RING_LIMIT = 128
WORD_LETTERS = list(string.letters + string.digits)
ERROR_TIMEOUT = -1
#ERROR_TIMEOUT = 2
#DARK_BACKGROUND = False
DARK_BACKGROUND = True
class Application:
def __init__(self, stdscr, buffers=[], jump_to_line=None, init_mode=None):
# initalize curses primitives
self.stdscr = stdscr
self.y, self.x = self.stdscr.getmaxyx()
# initialize some basic stuff
self.margins_visible = False
#self.margins = [(80, 'blue'), (90, 'red')]
self.margins = [(80, 'blue'), ]
# each highlighted_range contains three things: [window, start_p, end_p]
self.highlighted_ranges = []
self.mini_active = False
self.mini_buffer = None
self.mini_prompt = ""
self.error_string = ""
self.error_timestamp = None
self.input = keyinput.Handler()
# initialize our colors
if curses.has_colors():
curses.start_color()
try:
curses.use_default_colors()
color.default_color = True
except:
# guess we weren't on 2.4
color.default_color = False
color.init()
# this is how we can change color settings
if curses.can_change_color():
#curses.init_color(curses.COLOR_BLUE, 750, 400, 0)
pass
else:
self.set_error("Dynamic color not available")
# initialize our modes
self.modes = {
'blame': mode_blame.Blame,
'c': mode_c.C,
'console': mode_console.Console,
'diff': mode_diff.Diff,
'fundamental': mode.Fundamental,
'mini': mode_mini.Mini,
'nasm': mode_nasm.Nasm,
'perl': mode_perl.Perl,
'python': mode_python.Python,
'replace': mode_replace.Replace,
'search': mode_search.Search,
'sh': mode_sh.Sh,
'text': mode_text.Text,
'which': mode_which.Which,
'xml': mode_xml.XML,
'mutt': mode_mutt.Mutt,
'sql': mode_sql.Sql,
'javascript': mode_javascript.Javascript,
'template': mode_tt.Template,
}
# these are used in this order to determine which mode to open certain
# kinds of files
self.mode_paths = {
'/etc/profile': 'sh',
}
self.mode_basenames = {
'.bashrc': 'sh',
'.bash_profile': 'sh',
'.profile': 'sh',
}
self.mode_extensions = {
'.py': 'python',
'.pl': 'perl',
'.pm': 'perl',
'.t': 'perl',
'.c': 'c',
'.txt': 'text',
'.s': 'nasm',
'.sh': 'sh',
'.bash': 'sh',
'.xml': 'xml',
'.xml.in': 'xml',
'.html': 'xml',
'.htm': 'xml',
'.sql': 'sql',
'.js': 'javascript',
'.tt': 'template'
}
self.mode_detection = {
'python': 'python',
'perl': 'perl',
'sh': 'sh',
'bash': 'sh',
}
# initialize our methods
self.methods = {}
for name in dir(method):
cls = eval("method.%s" % name)
if hasattr(cls, '_is_method') and cls._is_method:
self.methods[cls._name()] = cls()
# create all the insert methods for the character ranges we like
for c in string.letters + string.digits + string.punctuation:
## closing tags are handled differently
#if c == ')' or c == ']' or c == '}':
# continue
obj = method.InsertString(c)
self.methods[obj.name] = obj
# window/slot height/width
height = self.y - 2
width = self.x - 1
# initialize our buffers
# note that the first buffer in buffers will be initially visible
buffers.append(buffer.ScratchBuffer())
buffers.append(buffer.ConsoleBuffer())
self.bufferlist = bufferlist.BufferList(height, width)
self.active_slot = 0
self.resize_slots()
# build windows for our buffers
for b in buffers:
window.Window(b, self, height, width, slot=self.active_slot,
mode_name=init_mode)
self.bufferlist.add_buffer(b)
self.resize_windows()
# see if the user has requested that we go to a particular line
if jump_to_line:
name = buffers[0].name()
b = self.bufferlist.get_buffer_by_name(name)
w = b.get_window(self.active_slot)
method.GotoLine().execute(w, lineno=jump_to_line)
# initialize our kill ring and last action
self.kill_ring = []
self.kill_commands = ['kill', 'kill-region']
self.last_action = None
self.last_search = None
self.last_replace_before = None
self.last_replace_after = None
# initialize tab handlers
method.DATATYPES['path'] = completer.FileCompleter()
method.DATATYPES['buffer'] = completer.BufferCompleter(self)
method.DATATYPES['command'] = completer.CommandCompleter()
method.DATATYPES['shell'] = completer.ShellCompleter()
method.DATATYPES['method'] = completer.MethodCompleter()
method.DATATYPES['mode'] = completer.ModeCompleter()
method.DATATYPES['perlfunction'] = completer.PerlFunctionCompleter()
# set up curses
self.win = curses.newwin(self.y, self.x, 0, 0)
self.win.leaveok(1)
curses.meta(1)
curses.halfdelay(1)
self.hide_cursor()
def globals(self):
return globals()
def locals(self):
return locals()
def add_slot(self):
b = self.bufferlist.slots[self.active_slot].buffer
n = self.bufferlist.add_slot(0, 0, 0, b)
self.resize_slots()
self.add_window_to_buffer(b, n)
self.resize_windows()
def remove_slot(self, slotname):
assert len(self.bufferlist.slots) > 1, "oh no you didn't!"
assert slotname >= 0 and slotname < len(self.bufferlist.slots), \
"invalid slot: %r (%r)" % (slotname, len(self.bufferlist.slots))
b = self.bufferlist.slots[slotname].buffer
self.bufferlist.remove_slot(slotname)
if self.active_slot > slotname:
self.active_slot = max(0, self.active_slot - 1)
self.resize_slots()
self.resize_windows()
def single_slot(self):
while len(self.bufferlist.slots) > 1:
if self.active_slot == 0:
self.remove_slot(1)
else:
self.remove_slot(0)
def get_window_height_width(self, slotname):
assert slotname >= 0 and slotname < len(self.bufferlist.slots), \
"invalid slot: %r" % slotname
slot = self.bufferlist.slots[slotname]
return (slot.height, slot.width)
# mini buffer handling
def get_mini_buffer(self):
return self.mini_buffer
def mini_buffer_is_open(self):
return self.mini_buffer is not None
def open_mini_buffer(self, prompt, callback, method=None, tabber=None,
modename=None):
if self.mini_buffer_is_open():
self.close_mini_buffer()
self.mini_prompt = prompt
self.mini_buffer = minibuffer.MiniBuffer(callback, method, tabber,
modename)
window.Window(self.mini_buffer, self, height=1,
width=self.x-1-len(self.mini_prompt)-1, slot='mini')
self.mini_active = True
def exec_mini_buffer(self):
self.mini_buffer.callback(self.mini_buffer.make_string())
self.close_mini_buffer()
def close_mini_buffer(self):
if self.mini_buffer_is_open():
self.mini_buffer.close()
self.mini_buffer = None
self.mini_prompt = ""
self.mini_active = False
def get_mini_buffer_prompt(self):
return self.mini_prompt
def set_mini_buffer_prompt(self, p):
self.mini_prompt = p
# window handling
def toggle_window(self):
assert 0 <= self.active_slot and self.active_slot < len(self.bufferlist.slots)
self.active_slot = (self.active_slot + 1) % len(self.bufferlist.slots)
def window(self):
slotname = self.active_slot
return self.bufferlist.slots[slotname].buffer.get_window(slotname)
def active_window(self):
if self.mini_active:
return self.mini_buffer.get_window('mini')
else:
assert 0 <= self.active_slot and self.active_slot < len(self.bufferlist.slots), \
"0 <= %d < %d" % (self.active_slot, len(self.bufferlist.slots))
slotname = self.active_slot
return self.bufferlist.slots[slotname].buffer.get_window(slotname)
# buffer handling
def file_buffer(self, path, data, switch_to=True):
assert not self.has_buffer_name(path), 'oh no! %r is already open' % path
assert not os.path.exists(path), 'oh no! %r already exists in fs' % path
f = open(path, 'w')
f.write(data)
f.close()
b = buffer.FileBuffer(path)
b.open()
self.add_window_to_buffer(b, self.active_slot)
self.add_buffer(b)
if switch_to:
self.switch_buffer(b)
def data_buffer(self, name, data, switch_to=True, modename=None):
if self.has_buffer_name(name):
b = self.bufferlist.buffer_names[name]
self.remove_buffer(b)
b = buffer.DataBuffer(name, data)
if modename is not None:
b.modename = modename
self.add_window_to_buffer(b, self.active_slot)
self.add_buffer(b)
if switch_to:
self.switch_buffer(b)
def get_buffer_by_path(self, path):
return self.bufferlist.get_buffer_by_path(path)
def has_buffer_name(self, name):
return self.bufferlist.has_buffer_name(name)
def get_buffer_by_name(self, name):
return self.bufferlist.get_buffer_by_name(name)
def has_buffer(self, b):
return self.bufferlist.has_buffer(b)
def add_buffer(self, b):
self.bufferlist.add_buffer(b)
def remove_buffer(self, b):
assert b.name() is not "*Scratch*", "can't kill the scratch"
assert self.bufferlist.has_buffer(b), "can't kill what's not there"
assert len(self.bufferlist.buffers) > 1, "can't kill with no other buffers"
self.bufferlist.remove_buffer(b)
b.close()
if self.bufferlist.empty_slot(self.active_slot):
b2 = self.bufferlist.hidden_buffers[0]
self.bufferlist.set_slot(self.active_slot, b2)
def switch_buffer(self, b):
assert self.has_buffer_name(b.name()), "buffer %s does not exist" % (b.name())
assert 0 <= self.active_slot and self.active_slot < len(self.bufferlist.slots)
self.add_window_to_buffer(b, self.active_slot)
self.bufferlist.set_slot(self.active_slot, b)
def add_window_to_buffer(self, b, slotname):
if not b.has_window(slotname):
slot = self.bufferlist.slots[slotname]
window.Window(b, self, height=slot.height, width=slot.width, slot=slotname)
# error string handling
def set_error(self, s):
self.error_string = s
self.error_timestamp = time.time()
def clear_error(self):
self.error_string = ""
self.error_timestamp = None
def resize_event(self):
self.y, self.x = self.stdscr.getmaxyx()
self.resize_slots()
self.resize_windows()
def resize_slots(self):
n = len(self.bufferlist.slots)
x = self.x - 1
y_sum = self.y - 1 - n
y_pool = y_sum
y_offset = 0
for i in range(0, n - 1):
slot = self.bufferlist.slots[i]
y = y_sum / n
slot.resize(y, x, y_offset)
y_pool -= y
y_offset += y + 1
slot = self.bufferlist.slots[n-1].resize(y_pool, x, y_offset)
def resize_windows(self):
for b in self.bufferlist.buffers:
keys = b.windows.keys()
for name in keys:
try:
(height, width) = self.get_window_height_width(name)
b.windows[name].set_size(width, height)
except:
w = b.windows[name]
del b.windows[name]
# kill w now
# hide the curses cursor
def hide_cursor(self):
self.win.move(self.y-2, 0)
try:
curses.curs_set(0)
except:
pass
# exit
def exit(self):
self.done = True
# kill stack manipulation
def push_kill(self, s):
if s is not None:
if self.last_action in self.kill_commands and \
len(self.kill_ring):
self.kill_ring[-1] = self.kill_ring[-1] + s
else:
self.kill_ring.append(s)
if len(self.kill_ring) > KILL_RING_LIMIT:
self.kill_ring.pop(0)
def pop_kill(self):
return self.kill_ring.pop(-1)
def has_kill(self, i=-1):
return len(self.kill_ring) >= abs(i)
def get_kill(self, i=-1):
return self.kill_ring[i]
# undo/redo
def undo(self):
try:
self.window().buffer.undo()
except Exception, e:
self.set_error("%s" % (e))
def redo(self):
try:
self.window().buffer.redo()
except Exception, e:
self.set_error("%s" % (e))
# action creating methods
def make_insert_action(self, c):
return lambda: self.window().insert_string(c)
def make_window_action(self, methodname):
f = getattr(self.window(), methodname)
f()
# we are evil
def eval(self, s):
return eval(s)
# the might run-loop!
def run(self):
self.done = False
#keycodes = []
while not self.done:
i = self.win.getch()
#if i > 0:
# if len(keycodes) >= 6:
# keycodes.pop(0)
# keycodes.append(str(i))
#self.set_error('keycodes: %s' % repr(keycodes))
if i == curses.KEY_RESIZE:
while i == curses.KEY_RESIZE:
i = self.win.getch()
self.resize_event()
err = ''
try:
self.input.parse(i)
except Exception, e:
err = str(e)
while len(self.input.tokens):
t = self.input.tokens.pop(0)
self.active_window().mode.handle_token(t)
self.draw()
if err:
self.set_error(err)
if self.error_timestamp is not None and \
ERROR_TIMEOUT > 0 and \
time.time() - self.error_timestamp > ERROR_TIMEOUT:
self.clear_error()
return
# highlighting
# each highlighted_range contains three things: [window, start_p, end_p]
def add_highlighted_range(self, w, start_p, end_p):
self.highlighted_ranges.append([w, start_p, end_p])
def clear_highlighted_ranges(self):
self.highlighted_ranges = []
# full screen drawer
def draw(self):
self.hide_cursor()
self.draw_slots()
self.draw_input_bar()
self.hide_cursor()
self.win.noutrefresh()
self.hide_cursor()
curses.doupdate()
# debugging
def dump(self):
w = self.window()
ll = len(w.buffer.lines)
pl = len(w.get_physical_lines())
vl = len(w.visible_lines())
first = w.first
last = w.last
cursor = w.logical_cursor()
vcursor = w.visible_cursor()
s = ""
s += "width: %d\n" % (w.width)
s += "height: %d\n" % (w.height)
s += "len logical lines: %d\n" % (ll)
s += "logical first: %s\n" % (first)
s += "logical last: %s\n" % (last)
s += "logical cursor: %s\n" % (cursor)
s += "len physical lines: %d\n" % (pl)
s += "physical first: %s\n" % (w.physical_point(first))
s += "physical last: %s\n" % (w.physical_point(last))
s += "physical cursor: %s\n" % (w.physical_point(cursor))
s += "len visible lines: %d\n" % (vl)
s += "visible first: %s\n" % ("n/a")
s += "visible last: %s\n" % ("n/a")
s += "visible cursor: %s\n" % (vcursor)
return s
# sub-drawing methods
def draw_slots(self):
self.win.erase()
for i in range(0, len(self.bufferlist.slots)):
slot = self.bufferlist.slots[i]
self.draw_slot(i)
self.draw_status_bar(i)
def draw_slot(self, slotname):
slot = self.bufferlist.slots[slotname]
if not slot.buffer.has_window(slotname):
return
w = slot.buffer.get_window(slotname)
lines = w.visible_lines()
regions = w.mode.visible_regions()
## FIXME: why isn't this always the same????
assert (len(lines) == len(regions) or
len(lines) == len(regions) - 1), "%d,%d" % (len(lines),
len(regions)-1)
assert len(lines) > 0, "no lines... why?"
m = min(len(lines), slot.height)
assert m > 0
x = slot.width
y = slot.height
y_offset = slot.offset
assert x > 0
assert y > 0
red_attr = color.build_attr(color.pairs('red', 'default'))
for i in range(0, m):
j = 0
line = lines[i]
for r in regions[i]:
try:
# start, end, attr, value, ttype = r
assert 0 <= r.start, "0 <= %d" % (r.start)
assert r.start <= r.end, "%d <= %d" % (r.start, r.end)
assert r.end <= len(line), "%d <= %d" % (r.end, len(line))
except Exception, e:
s = "\n".join([repr(x) for x in regions])
raise Exception, "%s\n%s\n\n%s\n\n%s\n\n%s\n\n%d" % \
(e, s, regions[i], r, repr(line), len(line))
assert line[r.start:r.end] == r.value, \
"%r != %r" % (line[r.start:r.end], r.value)
try:
if DARK_BACKGROUND:
attr = r.attr | curses.A_BOLD
else:
attr = r.attr
self.win.addnstr(i + y_offset, r.start, r.value, r.end - r.start, attr)
except Exception, e:
raise Exception, "%s\n%s %s %s %s" % \
(e, repr(i), repr(r.start), repr(r.value), repr(r.end - r.start))
j = r.end
if j < len(line):
# this is cheating... FIXME
self.win.addnstr(i + y_offset, j, line[j:], len(line) - j)
j += len(line) - j
if j < x:
self.win.addnstr(i + y_offset, j, ' ' * (x-j), (x-j))
if w.continued_visible_line(i):
self.win.addch(i + y_offset, x, '\\', red_attr)
else:
self.win.addch(i + y_offset, x, ' ')
for i in range(m, y):
self.win.addnstr(i + y_offset, 0, '~' + ' ' * (x), x + 1, red_attr)
for (high_w, lp1, lp2) in self.highlighted_ranges:
if lp1.y != lp2.y:
# this region is incoherent, so skip it, or die, whatever
#raise Exception, "haddock %d != %d" % (lp1.y, lp2.y)
pass
elif w is not high_w:
# this region isn't in the current window so skip it
pass
else:
(pp1, pp2) = (w.physical_point(lp1), w.physical_point(lp2))
vo = w.visible_offset()
(vp1, vp2) = (pp1.offset(0, -vo), pp2.offset(0, -vo))
if vp2.y < 0 or vp1.y > w.height:
# this region is not visible, so skip it
pass
else:
# first let's fix our points so we're sure they're visible
if vp1.y < 0:
vp1 = point.Point(0,0)
if vp2.y > w.height:
vp2 = point.Point(len(lines[-1]), w.height-1)
if vp1.y == vp2.y:
# our region physically fits on one line; this is easy
b = lines[vp1.y][vp1.x:vp2.x]
self.win.addstr(vp1.y + y_offset, vp1.x, b, curses.A_REVERSE)
else:
# our region spans multiple physical lines, so deal
b1 = lines[vp1.y][vp1.x:]
self.win.addstr(vp1.y + y_offset, vp1.x, b1, curses.A_REVERSE)
for i in range(vp1.y + 1, vp2.y):
b = lines[i]
self.wind.addstr(i + y_offset, 0, b, curses.A_REVERSE)
b2 = lines[vp2.y][:vp2.x]
self.win.addstr(vp2.y + y_offset, 0, b2, curses.A_REVERSE)
if self.margins_visible:
for (limit, shade) in self.margins:
if self.x > limit:
for i in range(0, y):
# the actual character is the lower 8 bits, and the
# attribute is the upper 8 bits; we will ignore the
# attribute and just get the character
char = self.win.inch(i + y_offset, limit) & 255
attr = color.build('default', shade, 'bold')
self.win.addch(i + y_offset, limit, char, attr)
if self.mini_active is False and self.active_slot == slotname:
if w.active_point is not None and w.point_is_visible(w.active_point):
pa = w.physical_point(w.active_point)
va = pa.offset(0, -w.visible_offset())
if len(lines[va.y]):
a = lines[va.y][va.x]
else:
a = ' '
self.win.addch(va.y + y_offset, va.x, a, curses.A_REVERSE)
else:
cursor = w.visible_cursor()
cx, cy = (cursor.x, cursor.y)
if cy >= len(lines):
self.set_error('in main1: cursor error; %d >= %d' %
(cy, len(lines)))
return
elif cx == len(lines[cy]):
c = ' '
elif cx > len(lines[cy]):
self.set_error('why? %r %r' % (cursor, len(lines[cy])))
return
else:
c = lines[cy][cx]
self.win.addch(cy + y_offset, cx, c, curses.A_REVERSE)
def draw_status_bar(self, slotname):
slot = self.bufferlist.slots[slotname]
if not slot.buffer.has_window(slotname):
return
w = slot.buffer.get_window(slotname)
b = w.buffer
cursor = w.logical_cursor()
pcursor = w.physical_cursor()
first = w.first
last = w.last
if b.readonly():
if b.changed():
modflag = '%*'
else:
modflag = '%%'
else:
if b.changed():
modflag = '**'
else:
modflag = '--'
if w.mark:
mark = w.mark
else:
mark = point.Point(-1, -1)
name = b.name()
if w.first_is_visible():
perc = "Top"
elif w.last_is_visible():
perc = "Bot"
else:
perc = "%2d%%" % (first.y*100 / len(b.lines))
# XYZ: we should actually use more of the 'state' variables
format = "----:%s-Fl %-18s (%s)--L%d--C%d--%s"
status = format % (modflag, name, w.mode.name(), cursor.y+1, cursor.x+1, perc)
status = status[:slot.width + 1]
status += "-" * (slot.width - len(status) + 1)
self.win.addnstr(slot.height + slot.offset, 0, status, slot.width + 1,
curses.A_REVERSE)
# input bar drawing
def draw_input_bar(self):
if self.error_string:
self.draw_error()
elif self.mini_buffer_is_open():
self.draw_mini_buffer()
else:
self.draw_nothing()
try:
# fucking python, fucking curses, fucking fuck
self.win.addch(self.y-1, self.x-1, ' ')
except:
pass
def draw_error(self):
l = self.x - 1
s1 = self.error_string
s2 = util.cleanse(util.padtrunc(s1, l))
self.win.addnstr(self.y-1, 0, s2, l)
def draw_mini_buffer(self):
l = self.x - 1
w = self.mini_buffer.get_window('mini')
lines = w.visible_lines()
s1 = self.mini_prompt + lines[0]
s2 = util.padtrunc(s1, l)
self.win.addnstr(self.y-1, 0, s2, l)
if self.mini_active:
cursor = w.visible_cursor()
cx, cy = (cursor.x, cursor.y)
if cy >= len(lines):
#self.set_error('in main2: cursor error; %d >= %d' %
# (cy, len(lines)))
self.set_error('in main2: %r, %r [f:%r,l:%r] {h:%r,w:%r} %r' %
(len(lines), cursor, w.first, w.last,
w.height, w.width, len(lines[0])))
return
elif cx == len(lines[cy]):
c = ' '
else:
c = lines[cy][cx]
self.win.addch(self.y-1, cx + len(self.mini_prompt), c,
curses.A_REVERSE)
def draw_nothing(self):
l = self.x - 1
self.win.addnstr(self.y-1, 0, util.pad('', l), l)
def open_aes_file(path, nl, name=None):
p = getpass.getpass("Please enter the AES password: ")
b = buffer.AesBuffer(path, p, nl, name)
return b
def open_plain_file(path, nl, name=None):
b = buffer.FileBuffer(path, nl, name)
return b
if __name__ == "__main__":
ciphers = { 'none': open_plain_file,
'aes': open_aes_file }
linetypes = { 'win': '\r\n',
'mac': '\r',
'unix': '\n' }
import optparse
parser = optparse.OptionParser()
parser.set_defaults(debug=False)
parser.set_defaults(goto=None)
parser.set_defaults(mode=None)
parser.set_defaults(cipher='none')
parser.set_defaults(linetype='unix')
parser.add_option('-d', '--debug', dest='debug', action='store_true',
help='run in debug mode')
parser.add_option('-e', '--encrypt', dest='cipher', metavar='CIPHER',
help='decrypt and encrypt with CIPHER (default: none)')
parser.add_option('-g', '--goto', dest='goto', metavar='NUM', type='int',
help='jump to line NUM of the first argument')
parser.add_option('-l', '--line-end', dest='linetype', metavar='TYPE',
help='use TYPE (win,mac,unix) line endings (default: unix)')
parser.add_option('-m', '--mode', dest='mode', metavar='MODE',
help='open arguments in MODE')
(opts, args) = parser.parse_args()
# if debugging, disable error handling to produce backtraces
if opts.debug:
mode.DEBUG = True
# we will support using +19 as the first argument to indicate opening the
# first file on line 19 (same as -g 19 or --goto 19)
if len(sys.argv) > 1 and args[0].startswith('+'):
opts.goto = int(args[0][1:])
args = args[1:]
# figure out which kind of line types we're using
if opts.linetype not in linetypes:
sys.stderr.write('invalid linetype: %r' % opts.linetype)
sys.exit(1)
nl = linetypes[opts.linetype]
# figure out what kind of file open function to use
if opts.cipher not in ciphers:
sys.stderr.write('invalid cipher: %r' % opts.cipher)
sys.exit(2)
f = ciphers[opts.cipher]
# open each path using our callback to get a buffer, open that buffer, etc.
buffers = []
names = sets.Set()
paths = sets.Set()
for path in args:
path = os.path.abspath(os.path.realpath(util.expand_tilde(path)))
if path in paths:
continue
name = os.path.basename(path)
if name in names:
i = 1
auxname = '%s/%d' % (name, i)
while auxname in names:
i += 1
auxname = '%s/%d' % (name, i)
name = auxname
b = f(path, nl, name)
b.open()
buffers.append(b)
paths.add(path)
names.add(name)
# ok, now run our app
run(buffers, opts.goto, opts.mode)