mirror of
https://github.com/erkin/ponysay.git
synced 2024-11-22 20:38:00 +01:00
fba2fbf862
Signed-off-by: Mattias Andrée <maandree@operamail.com>
1250 lines
53 KiB
Python
Executable file
1250 lines
53 KiB
Python
Executable file
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
|
||
'''
|
||
ponysay - Ponysay, cowsay reimplementation for ponies
|
||
|
||
Copyright (C) 2012, 2013 Erkin Batu Altunbaş et al.
|
||
|
||
|
||
This program is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
|
||
If you intend to redistribute ponysay or a fork of it commercially,
|
||
it contains aggregated images, some of which may not be commercially
|
||
redistribute, you would be required to remove those. To determine
|
||
whether or not you may commercially redistribute an image make use
|
||
that line ‘FREE: yes’, is included inside the image between two ‘$$$’
|
||
lines and the ‘FREE’ is and upper case and directly followed by
|
||
the colon.
|
||
'''
|
||
|
||
import os
|
||
import sys
|
||
from subprocess import Popen, PIPE
|
||
|
||
from argparser import *
|
||
from ponysay import *
|
||
from metadata import *
|
||
|
||
|
||
'''
|
||
The version of ponysay
|
||
'''
|
||
VERSION = 'dev' # this line should not be edited, it is fixed by the build system
|
||
|
||
|
||
|
||
'''
|
||
Hack to enforce UTF-8 in output (in the future, if you see anypony not using utf-8 in
|
||
programs by default, report them to Princess Celestia so she can banish them to the moon)
|
||
|
||
@param text:str The text to print (empty string is default)
|
||
@param end:str The appendix to the text to print (line breaking is default)
|
||
'''
|
||
def print(text = '', end = '\n'):
|
||
sys.stdout.buffer.write((str(text) + end).encode('utf-8'))
|
||
|
||
'''
|
||
stderr equivalent to print()
|
||
|
||
@param text:str The text to print (empty string is default)
|
||
@param end:str The appendix to the text to print (line breaking is default)
|
||
'''
|
||
def printerr(text = '', end = '\n'):
|
||
sys.stderr.buffer.write((str(text) + end).encode('utf-8'))
|
||
|
||
|
||
|
||
'''
|
||
This is the mane class of ponysay-tool
|
||
'''
|
||
class PonysayTool():
|
||
'''
|
||
Starts the part of the program the arguments indicate
|
||
|
||
@param args:ArgParser Parsed command line arguments
|
||
'''
|
||
def __init__(self, args):
|
||
if args.argcount == 0:
|
||
args.help()
|
||
exit(255)
|
||
return
|
||
|
||
opts = args.opts
|
||
|
||
if unrecognised or (opts['-h'] is not None):
|
||
args.help()
|
||
if unrecognised:
|
||
exit(254)
|
||
|
||
elif opts['-v'] is not None:
|
||
print('%s %s' % ('ponysay-tool', VERSION))
|
||
|
||
elif opts['--kms'] is not None:
|
||
self.generateKMS()
|
||
|
||
elif (opts['--dimensions'] is not None) and (len(opts['--dimensions']) == 1):
|
||
self.generateDimensions(opts['--dimensions'][0], args.files)
|
||
|
||
elif (opts['--metadata'] is not None) and (len(opts['--metadata']) == 1):
|
||
self.generateMetadata(opts['--metadata'][0], args.files)
|
||
|
||
elif (opts['-b'] is not None) and (len(opts['-b']) == 1):
|
||
try:
|
||
if opts['--no-term-init'] is None:
|
||
print('\033[?1049h', end='') # initialise terminal
|
||
cmd = 'stty %s < %s > /dev/null 2> /dev/null'
|
||
cmd %= ('-echo -icanon -echo -isig -ixoff -ixon', os.path.realpath('/dev/stdout'))
|
||
Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait()
|
||
print('\033[?25l', end='') # hide cursor
|
||
dir = opts['-b'][0]
|
||
if not dir.endswith(os.sep):
|
||
dir += os.sep
|
||
self.browse(dir, opts['-r'])
|
||
finally:
|
||
print('\033[?25h', end='') # show cursor
|
||
cmd = 'stty %s < %s > /dev/null 2> /dev/null'
|
||
cmd %= ('echo icanon echo isig ixoff ixon', os.path.realpath('/dev/stdout'))
|
||
Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait()
|
||
if opts['--no-term-init'] is None:
|
||
print('\033[?1049l', end='') # terminate terminal
|
||
|
||
elif (opts['--edit'] is not None) and (len(opts['--edit']) == 1):
|
||
pony = opts['--edit'][0]
|
||
if not os.path.isfile(pony):
|
||
printerr('%s is not an existing regular file' % pony)
|
||
exit(252)
|
||
linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux')
|
||
try:
|
||
if opts['--no-term-init'] is None:
|
||
print('\033[?1049h', end='') # initialise terminal
|
||
if linuxvt: print('\033[?8c', end='') # use full block for cursor (_ is used by default in linux vt)
|
||
cmd = 'stty %s < %s > /dev/null 2> /dev/null'
|
||
cmd %= ('-echo -icanon -echo -isig -ixoff -ixon', os.path.realpath('/dev/stdout'))
|
||
Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait()
|
||
self.editmeta(pony)
|
||
finally:
|
||
cmd = 'stty %s < %s > /dev/null 2> /dev/null'
|
||
cmd %= ('echo icanon echo isig ixoff ixon', os.path.realpath('/dev/stdout'))
|
||
Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait()
|
||
if linuxvt: print('\033[?0c', end='') # restore cursor
|
||
if opts['--no-term-init'] is None:
|
||
print('\033[?1049l', end='') # terminate terminal
|
||
|
||
elif (opts['--edit-rm'] is not None) and (len(opts['--edit-rm']) == 1):
|
||
ponyfile = opts['--edit-rm'][0]
|
||
pony = None
|
||
with open(ponyfile, 'rb') as file:
|
||
pony = file.read().decode('utf8', 'replace')
|
||
if pony.startswith('$$$\n'):
|
||
pony = pony[3:]
|
||
pony = pony[pony.index('\n$$$\n') + 5:]
|
||
with open(ponyfile, 'wb') as file:
|
||
file.write(pony.encode('utf8'))
|
||
|
||
elif (opts['--edit-stash'] is not None) and (len(opts['--edit-stash']) == 1):
|
||
ponyfile = opts['--edit-stash'][0]
|
||
pony = None
|
||
with open(ponyfile, 'rb') as file:
|
||
pony = file.read().decode('utf8', 'replace')
|
||
if pony.startswith('$$$\n'):
|
||
pony = pony[3:]
|
||
pony = pony[:pony.index('\n$$$\n')]
|
||
print('$$$' + pony + '\n$$$\n', end='')
|
||
else:
|
||
print('$$$\n$$$\n', end='')
|
||
|
||
elif (opts['--edit-apply'] is not None) and (len(opts['--edit-apply']) == 1):
|
||
data = ''
|
||
while True:
|
||
line = input()
|
||
if data == '':
|
||
if line != '$$$':
|
||
printerr('Bad stash')
|
||
exit(251)
|
||
data += '$$$\n'
|
||
else:
|
||
data += line + '\n'
|
||
if line == '$$$':
|
||
break
|
||
ponyfile = opts['--edit-apply'][0]
|
||
pony = None
|
||
with open(ponyfile, 'rb') as file:
|
||
pony = file.read().decode('utf8', 'replace')
|
||
if pony.startswith('$$$\n'):
|
||
pony = pony[3:]
|
||
pony = pony[pony.index('\n$$$\n') + 5:]
|
||
with open(ponyfile, 'wb') as file:
|
||
file.write((data + pony).encode('utf8'))
|
||
|
||
else:
|
||
args.help()
|
||
exit(253)
|
||
|
||
|
||
'''
|
||
Execute ponysay!
|
||
|
||
@param args Arguments
|
||
@param message Message
|
||
'''
|
||
def execPonysay(self, args, message = ''):
|
||
class PhonyArgParser():
|
||
def __init__(self, args, message):
|
||
self.argcount = len(args) + (0 if message is None else 1)
|
||
for key in args:
|
||
self.argcount += len(args[key]) if (args[key] is not None) and isinstance(args[key], list) else 1
|
||
self.message = message
|
||
self.opts = self
|
||
def __getitem__(self, key):
|
||
if key in args:
|
||
return args[key] if (args[key] is not None) and isinstance(args[key], list) else [args[key]]
|
||
return None
|
||
def __contains__(self, key):
|
||
return key in args;
|
||
|
||
stdout = sys.stdout
|
||
class StringInputStream():
|
||
def __init__(self):
|
||
self.buf = ''
|
||
class Buffer():
|
||
def __init__(self, parent):
|
||
self.parent = parent
|
||
def write(self, data):
|
||
self.parent.buf += data.decode('utf8', 'replace')
|
||
def flush(self):
|
||
pass
|
||
self.buffer = Buffer(self)
|
||
def flush(self):
|
||
pass
|
||
def isatty(self):
|
||
return True
|
||
sys.stdout = StringInputStream()
|
||
ponysay = Ponysay()
|
||
ponysay.run(PhonyArgParser(args, message))
|
||
out = sys.stdout.buf[:-1]
|
||
sys.stdout = stdout
|
||
return out
|
||
|
||
|
||
'''
|
||
Browse ponies
|
||
|
||
@param ponydir:str The pony directory to browse
|
||
@param restriction:list<str> Restrictions on listed ponies, may be None
|
||
'''
|
||
def browse(self, ponydir, restriction):
|
||
## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses
|
||
termsize = None
|
||
for channel in (sys.stdout, sys.stdin, sys.stderr):
|
||
termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0]
|
||
if len(termsize) > 0:
|
||
termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n
|
||
termsize = [int(item) for item in termsize]
|
||
break
|
||
(termh, termw) = termsize
|
||
|
||
ponies = set()
|
||
for ponyfile in os.listdir(ponydir):
|
||
if endswith(ponyfile, '.pony'):
|
||
ponyfile = ponyfile[:-5]
|
||
if ponyfile not in ponies:
|
||
ponies.add(ponyfile)
|
||
if restriction is not None:
|
||
oldponies = ponies
|
||
logic = Metadata.makeRestrictionLogic(restriction)
|
||
ponies = set()
|
||
for pony in Metadata.restrictedPonies(ponydir, logic):
|
||
if (pony not in ponies) and (pony in oldponies):
|
||
ponies.add(pony)
|
||
oldponies = ponies
|
||
ponies = list(ponies)
|
||
ponies.sort()
|
||
|
||
if len(ponies) == 0:
|
||
print('\033[1;31m%s\033[21m;39m' % 'No ponies... press Enter to exit.')
|
||
input()
|
||
|
||
panelw = Backend.len(max(ponies, key = Backend.len))
|
||
panely = 0
|
||
panelx = termw - panelw
|
||
|
||
(x, y) = (0, 0)
|
||
(oldx, oldy) = (None, None)
|
||
(quotes, info) = (False, False)
|
||
(ponyindex, oldpony) = (0, None)
|
||
(pony, ponywidth, ponyheight) = (None, None, None)
|
||
|
||
stored = None
|
||
while True:
|
||
printpanel = -2 if ponyindex != oldpony else oldpony
|
||
if (ponyindex != oldpony):
|
||
ponyindex %= len(ponies)
|
||
if ponyindex < 0:
|
||
ponyindex += len(ponies)
|
||
oldpony = ponyindex
|
||
|
||
ponyfile = (ponydir + '/' + ponies[ponyindex] + '.pony').replace('//', '/')
|
||
pony = self.execPonysay({'-f' : ponyfile, '-W' : 'none', '-o' : None}).split('\n')
|
||
|
||
preprint = '\033[H\033[2J'
|
||
if pony[0].startswith(preprint):
|
||
pony[0] = pony[0][len(preprint):]
|
||
ponyheight = len(pony)
|
||
ponywidth = Backend.len(max(pony, key = Backend.len))
|
||
|
||
AUTO_PUSH = '\033[01010~'
|
||
AUTO_POP = '\033[10101~'
|
||
pony = '\n'.join(pony).replace('\n', AUTO_PUSH + '\n' + AUTO_POP)
|
||
colourstack = ColourStack(AUTO_PUSH, AUTO_POP)
|
||
buf = ''
|
||
for c in pony:
|
||
buf += c + colourstack.feed(c)
|
||
pony = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n')
|
||
|
||
if (oldx != x) or (oldy != y):
|
||
(oldx, oldy) = (x, y)
|
||
print('\033[H\033[2J', end='')
|
||
|
||
def getprint(pony, ponywidth, ponyheight, termw, termh, px, py):
|
||
ponyprint = pony
|
||
if py < 0:
|
||
ponyprint = [] if -py > len(ponyprint) else ponyprint[-py:]
|
||
elif py > 0:
|
||
ponyprint = py * [''] + ponyprint
|
||
ponyprint = ponyprint[:len(ponyprint) if len(ponyprint) < termh else termh]
|
||
def findcolumn(line, column):
|
||
if Backend.len(line) >= column:
|
||
return len(line)
|
||
pos = len(line)
|
||
while Backend.len(line[:pos]) != column:
|
||
pos -= 1
|
||
return pos
|
||
if px < 0:
|
||
ponyprint = [('' if -px > Backend.len(line) else line[findcolumn(line, -px):]) for line in ponyprint]
|
||
elif px > 0:
|
||
ponyprint = [px * ' ' + line for line in ponyprint]
|
||
ponyprint = [(line if Backend.len(line) <= termw else line[:findcolumn(line, termw)]) for line in ponyprint]
|
||
ponyprint = ['\033[21;39;49;0m%s\033[21;39;49;0m' % line for line in ponyprint]
|
||
return '\n'.join(ponyprint)
|
||
|
||
if quotes:
|
||
ponyquotes = None # TODO
|
||
quotesheight = len(ponyquotes)
|
||
quoteswidth = Backend.len(max(ponyquotes, key = Backend.len))
|
||
print(getprint(ponyquotes, quoteswidth, quotesheight, termw, termh, x, y), end='')
|
||
elif info:
|
||
ponyfile = (ponydir + '/' + ponies[ponyindex] + '.pony').replace('//', '/')
|
||
ponyinfo = self.execPonysay({'-f' : ponyfile, '-W' : 'none', '-i' : None}).split('\n')
|
||
infoheight = len(ponyinfo)
|
||
infowidth = Backend.len(max(ponyinfo, key = Backend.len))
|
||
print(getprint(ponyinfo, infowidth, infoheight, termw, termh, x, y), end='')
|
||
else:
|
||
print(getprint(pony, ponywidth, ponyheight, panelx, termh, x + (panelx - ponywidth) // 2, y + (termh - ponyheight) // 2), end='')
|
||
printpanel = -1
|
||
|
||
if printpanel == -1:
|
||
cury = 0
|
||
for line in ponies[panely:]:
|
||
cury += 1
|
||
if os.path.islink((ponydir + '/' + line + '.pony').replace('//', '/')):
|
||
line = '\033[34m%s\033[39m' % ((line + ' ' * panelw)[:panelw])
|
||
else:
|
||
line = (line + ' ' * panelw)[:panelw]
|
||
print('\033[%i;%iH\033[%im%s\033[0m' % (cury, panelx + 1, 1 if panely + cury - 1 == ponyindex else 0, line), end='')
|
||
elif printpanel >= 0:
|
||
for index in (printpanel, ponyindex):
|
||
cury = index - panely
|
||
if (0 <= cury) and (cury < termh):
|
||
line = ponies[cury + panely]
|
||
if os.path.islink((ponydir + '/' + line + '.pony').replace('//', '/')):
|
||
line = '\033[34m%s\033[39m' % ((line + ' ' * panelw)[:panelw])
|
||
else:
|
||
line = (line + ' ' * panelw)[:panelw]
|
||
print('\033[%i;%iH\033[%im%s\033[0m' % (cury, panelx + 1, 1 if panely + cury - 1 == ponyindex else 0, line), end='')
|
||
|
||
sys.stdout.buffer.flush()
|
||
if stored is None:
|
||
d = sys.stdin.read(1)
|
||
else:
|
||
d = stored
|
||
stored = None
|
||
|
||
recenter = False
|
||
if (d == 'w') or (d == 'W') or (d == '<') or (d == 'ä') or (d == 'Ä'): # pad ↑
|
||
y -= 1
|
||
elif (d == 's') or (d == 'S') or (d == 'o') or (d == 'O'): # pad ↓
|
||
y += 1
|
||
elif (d == 'd') or (d == 'D') or (d == 'e') or (d == 'E'): # pad →
|
||
x += 1
|
||
elif (d == 'a') or (d == 'A'): # pad ←
|
||
x -= 1
|
||
elif (d == 'q') or (d == 'Q'): # toggle quotes
|
||
quotes = False if info else not quotes
|
||
recenter = True
|
||
elif (d == 'i') or (d == 'I'): # toggle metadata
|
||
info = False if quotes else not info
|
||
recenter = True
|
||
elif ord(d) == ord('L') - ord('@'): # recenter
|
||
recenter = True
|
||
elif ord(d) == ord('P') - ord('@'): # previous
|
||
ponyindex -= 1
|
||
recenter = True
|
||
elif ord(d) == ord('N') - ord('@'): # next
|
||
ponyindex += 1
|
||
recenter = True
|
||
elif ord(d) == ord('Q') - ord('@'):
|
||
break
|
||
elif ord(d) == ord('X') - ord('@'):
|
||
if ord(sys.stdin.read(1)) == ord('C') - ord('@'):
|
||
break
|
||
elif d == '\033':
|
||
d = sys.stdin.read(1)
|
||
if d == '[':
|
||
d = sys.stdin.read(1)
|
||
if d == 'A': stored = chr(ord('P') - ord('@')) if (not quotes) and (not info) else 'W'
|
||
elif d == 'B': stored = chr(ord('N') - ord('@')) if (not quotes) and (not info) else 'S'
|
||
elif d == 'C': stored = chr(ord('N') - ord('@')) if (not quotes) and (not info) else 'D'
|
||
elif d == 'D': stored = chr(ord('P') - ord('@')) if (not quotes) and (not info) else 'A'
|
||
elif d == '1':
|
||
if sys.stdin.read(1) == ';':
|
||
if sys.stdin.read(1) == '5':
|
||
d = sys.stdin.read(1)
|
||
if d == 'A': stored = 'W'
|
||
elif d == 'B': stored = 'S'
|
||
elif d == 'C': stored = 'D'
|
||
elif d == 'D': stored = 'A'
|
||
if recenter:
|
||
(oldx, oldy) = (None, None)
|
||
(x, y) = (0, 0)
|
||
|
||
|
||
'''
|
||
Generate all kmsponies for the current TTY palette
|
||
'''
|
||
def generateKMS(self):
|
||
class PhonyArgParser():
|
||
def __init__(self, key, value):
|
||
self.argcount = 3
|
||
self.message = ''
|
||
self.opts = self
|
||
self.key = key
|
||
self.value = value
|
||
def __getitem__(self, key):
|
||
return [self.value] if key == self.key else None
|
||
def __contains__(self, key):
|
||
return key == self.key;
|
||
|
||
class StringInputStream():
|
||
def __init__(self):
|
||
self.buf = ''
|
||
class Buffer():
|
||
def __init__(self, parent):
|
||
self.parent = parent
|
||
def write(self, data):
|
||
self.parent.buf += data.decode('utf8', 'replace')
|
||
def flush(self):
|
||
pass
|
||
self.buffer = Buffer(self)
|
||
def flush(self):
|
||
pass
|
||
def isatty(self):
|
||
return True
|
||
|
||
stdout = sys.stdout
|
||
term = os.environ['TERM']
|
||
os.environ['TERM'] = 'linux'
|
||
|
||
sys.stdout = StringInputStream()
|
||
ponysay = Ponysay()
|
||
ponysay.run(PhonyArgParser('--onelist', None))
|
||
stdponies = sys.stdout.buf[:-1].split('\n')
|
||
|
||
sys.stdout = StringInputStream()
|
||
ponysay = Ponysay()
|
||
ponysay.run(PhonyArgParser('++onelist', None))
|
||
extraponies = sys.stdout.buf[:-1].split('\n')
|
||
|
||
for pony in stdponies:
|
||
printerr('Genering standard kmspony: %s' % pony)
|
||
sys.stderr.buffer.flush();
|
||
sys.stdout = StringInputStream()
|
||
ponysay = Ponysay()
|
||
ponysay.run(PhonyArgParser('--pony', pony))
|
||
|
||
for pony in extraponies:
|
||
printerr('Genering extra kmspony: %s' % pony)
|
||
sys.stderr.buffer.flush();
|
||
sys.stdout = StringInputStream()
|
||
ponysay = Ponysay()
|
||
ponysay.run(PhonyArgParser('++pony', pony))
|
||
|
||
os.environ['TERM'] = term
|
||
sys.stdout = stdout
|
||
|
||
|
||
'''
|
||
Generate pony dimension file for a directory
|
||
|
||
@param ponydir:str The directory
|
||
@param ponies:itr<str>? Ponies to which to limit
|
||
'''
|
||
def generateDimensions(self, ponydir, ponies = None):
|
||
dimensions = []
|
||
ponyset = None if (ponies is None) or (len(ponies) == 0) else set(ponies)
|
||
for ponyfile in os.listdir(ponydir):
|
||
if (ponyset is not None) and (ponyfile not in ponyset):
|
||
continue
|
||
if ponyfile.endswith('.pony') and (ponyfile != '.pony'):
|
||
class PhonyArgParser():
|
||
def __init__(self, balloon):
|
||
self.argcount = 5
|
||
self.message = ''
|
||
self.pony = (ponydir + '/' + ponyfile).replace('//', '/')
|
||
self.balloon = balloon
|
||
self.opts = self
|
||
def __getitem__(self, key):
|
||
if key == '-f':
|
||
return [self.pony]
|
||
if key == ('-W' if self.balloon else '-b'):
|
||
return [('none' if self.balloon else None)]
|
||
return None
|
||
def __contains__(self, key):
|
||
return key in ('-f', '-W', '-b');
|
||
stdout = sys.stdout
|
||
class StringInputStream():
|
||
def __init__(self):
|
||
self.buf = ''
|
||
class Buffer():
|
||
def __init__(self, parent):
|
||
self.parent = parent
|
||
def write(self, data):
|
||
self.parent.buf += data.decode('utf8', 'replace')
|
||
def flush(self):
|
||
pass
|
||
self.buffer = Buffer(self)
|
||
def flush(self):
|
||
pass
|
||
def isatty(self):
|
||
return True
|
||
sys.stdout = StringInputStream()
|
||
ponysay = Ponysay()
|
||
ponysay.run(PhonyArgParser(True))
|
||
printpony = sys.stdout.buf[:-1].split('\n')
|
||
ponyheight = len(printpony) - 2 # using fallback balloon
|
||
ponywidth = Backend.len(max(printpony, key = Backend.len))
|
||
ponysay = Ponysay()
|
||
ponysay.run(PhonyArgParser(False))
|
||
printpony = sys.stdout.buf[:-1].split('\n')
|
||
ponyonlyheight = len(printpony)
|
||
sys.stdout = stdout
|
||
dimensions.append((ponywidth, ponyheight, ponyonlyheight, ponyfile[:-5]))
|
||
(widths, heights, onlyheights) = ([], [], [])
|
||
for item in dimensions:
|
||
widths .append((item[0], item[3]))
|
||
heights .append((item[1], item[3]))
|
||
onlyheights.append((item[2], item[3]))
|
||
for items in (widths, heights, onlyheights):
|
||
sorted(items, key = lambda item : item[0])
|
||
for pair in ((widths, 'widths'), (heights, 'heights'), (onlyheights, 'onlyheights')):
|
||
(items, dimfile) = pair
|
||
dimfile = (ponydir + '/' + dimfile).replace('//', '/')
|
||
ponies = [item[1] for item in items]
|
||
dims = []
|
||
last = -1
|
||
index = 0
|
||
for item in items:
|
||
cur = item[0]
|
||
if cur != last:
|
||
if last >= 0:
|
||
dims.append((last, index))
|
||
last = cur
|
||
index += 1
|
||
if last >= 0:
|
||
dims.append((last, index))
|
||
dims = ''.join([('%i/%i/' % (dim[0], len('/'.join(ponies[:dim[1]])))) for dim in dims])
|
||
data = '/' + str(len(dims)) + '/' + dims + '/'.join(ponies) + '/'
|
||
with open(dimfile, 'wb') as file:
|
||
file.write(data.encode('utf8'))
|
||
file.flush()
|
||
|
||
|
||
'''
|
||
Generate pony metadata collection file for a directory
|
||
|
||
@param ponydir:str The directory
|
||
@param ponies:itr<str>? Ponies to which to limit
|
||
'''
|
||
def generateMetadata(self, ponydir, ponies = None):
|
||
if not ponydir.endswith('/'):
|
||
ponydir += '/'
|
||
def makeset(value):
|
||
rc = set()
|
||
bracket = 0
|
||
esc = False
|
||
buf = ''
|
||
for c in value:
|
||
if esc:
|
||
if bracket == 0:
|
||
if c not in (',', '\\', '(', ')'):
|
||
buf += '\\'
|
||
buf += c
|
||
esc = False
|
||
elif c == '(':
|
||
bracket += 1
|
||
elif c == ')':
|
||
if bracket == 0:
|
||
raise Exception('Bracket mismatch')
|
||
bracket -= 1
|
||
elif c == '\\':
|
||
esc = True
|
||
elif bracket == 0:
|
||
if c == ',':
|
||
buf = buf.strip()
|
||
if len(buf) > 0:
|
||
rc.add(buf)
|
||
buf = ''
|
||
else:
|
||
buf += c
|
||
if bracket > 0:
|
||
raise Exception('Bracket mismatch')
|
||
buf = buf.strip()
|
||
if len(buf) > 0:
|
||
rc.add(buf)
|
||
return rc
|
||
everything = []
|
||
ponyset = None if (ponies is None) or (len(ponies) == 0) else set(ponies)
|
||
for ponyfile in os.listdir(ponydir):
|
||
if (ponyset is not None) and (ponyfile not in ponyset):
|
||
continue
|
||
if ponyfile.endswith('.pony') and (ponyfile != '.pony'):
|
||
with open(ponydir + ponyfile, 'rb') as file:
|
||
data = file.read().decode('utf8', 'replace')
|
||
data = [line.replace('\n', '') for line in data.split('\n')]
|
||
if data[0] != '$$$':
|
||
meta = []
|
||
else:
|
||
sep = 1
|
||
while data[sep] != '$$$':
|
||
sep += 1
|
||
meta = data[1 : sep]
|
||
data = {}
|
||
for line in meta:
|
||
if ':' in line:
|
||
key = line[:line.find(':')].strip()
|
||
value = line[line.find(':') + 1:]
|
||
test = key
|
||
for c in 'ABCDEFGHIJKLMN OPQRSTUVWXYZ':
|
||
test = test.replace(c, '')
|
||
if (len(test) == 0) and (len(key) > 0):
|
||
vals = makeset(value.replace(' ', ''))
|
||
if key not in data:
|
||
data[key] = vals
|
||
else:
|
||
dset = data[key]
|
||
for val in vals:
|
||
dset.add(val)
|
||
everything.append((ponyfile[:-5], data))
|
||
import pickle
|
||
with open((ponydir + '/metadata').replace('//', '/'), 'wb') as file:
|
||
pickle.dump(everything, file, -1)
|
||
file.flush()
|
||
|
||
|
||
'''
|
||
Edit a pony file's metadata
|
||
|
||
@param ponyfile:str A pony file to edit
|
||
'''
|
||
def editmeta(self, ponyfile):
|
||
(data, meta, image) = 3 * [None]
|
||
|
||
with open(ponyfile, 'rb') as file:
|
||
data = file.read().decode('utf8', 'replace')
|
||
data = [line.replace('\n', '') for line in data.split('\n')]
|
||
|
||
if data[0] != '$$$':
|
||
image = data
|
||
meta = []
|
||
else:
|
||
sep = 1
|
||
while data[sep] != '$$$':
|
||
sep += 1
|
||
meta = data[1 : sep]
|
||
image = data[sep + 1:]
|
||
|
||
|
||
class PhonyArgParser():
|
||
def __init__(self):
|
||
self.argcount = 5
|
||
self.message = ponyfile
|
||
self.opts = self
|
||
def __getitem__(self, key):
|
||
if key == '-f': return [ponyfile]
|
||
if key == '-W': return ['n']
|
||
return None
|
||
def __contains__(self, key):
|
||
return key in ('-f', '-W');
|
||
|
||
|
||
data = {}
|
||
comment = []
|
||
for line in meta:
|
||
if ': ' in line.replace('\t', ' '):
|
||
key = line.replace('\t', ' ')
|
||
key = key[:key.find(': ')]
|
||
test = key
|
||
for c in 'ABCDEFGHIJKLMN OPQRSTUVWXYZ':
|
||
test = test.replace(c, '')
|
||
if (len(test) == 0) and (len(key.replace(' ', '')) > 0):
|
||
key = key.strip(' ')
|
||
value = line.replace('\t', ' ')
|
||
value = value[value.find(': ') + 2:]
|
||
if key not in data:
|
||
data[key] = value.strip(' ')
|
||
else:
|
||
data[key] += '\n' + value.strip(' ')
|
||
else:
|
||
comment.append(line)
|
||
else:
|
||
comment.append(line)
|
||
|
||
cut = 0
|
||
while (len(comment) > cut) and (len(comment[cut]) == 0):
|
||
cut += 1
|
||
comment = comment[cut:]
|
||
|
||
|
||
stdout = sys.stdout
|
||
class StringInputStream():
|
||
def __init__(self):
|
||
self.buf = ''
|
||
class Buffer():
|
||
def __init__(self, parent):
|
||
self.parent = parent
|
||
def write(self, data):
|
||
self.parent.buf += data.decode('utf8', 'replace')
|
||
def flush(self):
|
||
pass
|
||
self.buffer = Buffer(self)
|
||
def flush(self):
|
||
pass
|
||
def isatty(self):
|
||
return True
|
||
sys.stdout = StringInputStream()
|
||
ponysay = Ponysay()
|
||
ponysay.run(PhonyArgParser())
|
||
printpony = sys.stdout.buf[:-1].split('\n')
|
||
sys.stdout = stdout
|
||
|
||
preprint = '\033[H\033[2J'
|
||
if printpony[0].startswith(preprint):
|
||
printpony[0] = printpony[0][len(preprint):]
|
||
ponyheight = len(printpony) - len(ponyfile.split('\n')) + 1 - 2 # using fallback balloon
|
||
ponywidth = Backend.len(max(printpony, key = Backend.len))
|
||
|
||
## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses
|
||
termsize = None
|
||
for channel in (sys.stdout, sys.stdin, sys.stderr):
|
||
termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0]
|
||
if len(termsize) > 0:
|
||
termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n
|
||
termsize = [int(item) for item in termsize]
|
||
break
|
||
|
||
AUTO_PUSH = '\033[01010~'
|
||
AUTO_POP = '\033[10101~'
|
||
modprintpony = '\n'.join(printpony).replace('\n', AUTO_PUSH + '\n' + AUTO_POP)
|
||
colourstack = ColourStack(AUTO_PUSH, AUTO_POP)
|
||
buf = ''
|
||
for c in modprintpony:
|
||
buf += c + colourstack.feed(c)
|
||
modprintpony = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '')
|
||
|
||
printpony = [('\033[21;39;49;0m%s%s\033[21;39;49;0m' % (' ' * (termsize[1] - ponywidth), line)) for line in modprintpony.split('\n')]
|
||
|
||
|
||
print(preprint, end='')
|
||
print('\n'.join(printpony), end='')
|
||
print('\033[H', end='')
|
||
print('Please see the info manual for details on how to fill out this form')
|
||
print()
|
||
|
||
|
||
if 'WIDTH' in data: del data['WIDTH']
|
||
if 'HEIGHT' in data: del data['HEIGHT']
|
||
data['comment'] = '\n'.join(comment)
|
||
fields = [key for key in data]
|
||
fields.sort()
|
||
standardfields = ['GROUP NAME', 'NAME', 'OTHER NAMES', 'APPEARANCE', 'KIND',
|
||
'GROUP', 'BALLOON', 'LINK', 'LINK ON', 'COAT', 'MANE', 'EYE',
|
||
'AURA', 'DISPLAY', 'BALLOON TOP', 'BALLOON BOTTOM', 'MASTER',
|
||
'POSE', 'BASED ON', 'SOURCE', 'MEDIA', 'LICENSE', 'FREE',
|
||
'comment']
|
||
for standard in standardfields:
|
||
if standard in fields:
|
||
del fields[fields.index(standard)]
|
||
if standard not in data:
|
||
data[standard] = ''
|
||
|
||
fields = standardfields[:-1] + fields + [standardfields[-1]]
|
||
|
||
def saver(ponyfile, ponyheight, ponywidth, data, image):
|
||
class Saver():
|
||
def __init__(self, ponyfile, ponyheight, ponywidth, data, image):
|
||
(self.ponyfile, self.ponyheight, self.ponywidth, self.data, self.image) = (ponyfile, ponyheight, ponywidth, data, image)
|
||
def __call__(self): # functor
|
||
comment = self.data['comment']
|
||
comment = ('\n' + comment + '\n').replace('\n$$$\n', '\n\\$$$\n')[:-1]
|
||
|
||
meta = []
|
||
keys = [key for key in data]
|
||
keys.sort()
|
||
for key in keys:
|
||
if self.data[key] is None:
|
||
continue
|
||
if (key == 'comment') or (len(self.data[key].strip()) == 0):
|
||
continue
|
||
values = self.data[key].strip()
|
||
for value in values.split('\n'):
|
||
meta.append(key + ': ' + value)
|
||
|
||
meta.append('WIDTH: ' + str(self.ponywidth))
|
||
meta.append('HEIGHT: ' + str(self.ponyheight))
|
||
# TODO auto fill in BALLOON {TOP,BOTTOM}
|
||
meta.append(comment)
|
||
meta = '\n'.join(meta)
|
||
ponydata = '$$$\n' + meta + '\n$$$\n' + '\n'.join(self.image)
|
||
|
||
with open(self.ponyfile, 'wb') as file:
|
||
file.write(ponydata.encode('utf8'))
|
||
file.flush()
|
||
return Saver(ponyfile, ponyheight, ponywidth, data, image)
|
||
|
||
textarea = TextArea(fields, data, 1, 3, termsize[1] - ponywidth, termsize[0] - 2, termsize)
|
||
textarea.run(saver(ponyfile, ponyheight, ponywidth, data, image))
|
||
|
||
|
||
|
||
'''
|
||
GNU Emacs alike text area
|
||
'''
|
||
class TextArea(): # TODO support small screens
|
||
'''
|
||
Constructor
|
||
|
||
@param fields:list<str> Field names
|
||
@param datamap:dist<str,str> Data map
|
||
@param left:int Left position of the component
|
||
@param top:int Top position of the component
|
||
@param width:int Width of the component
|
||
@param height:int Height of the component
|
||
@param termsize:(int,int) The height and width of the terminal
|
||
'''
|
||
def __init__(self, fields, datamap, left, top, width, height, termsize):
|
||
(self.fields, self.datamap, self.left, self.top, self.width, self.height, self.termsize) \
|
||
= (fields, datamap, left, top, width - 1, height, termsize)
|
||
|
||
|
||
'''
|
||
Execute text reading
|
||
|
||
@param saver Save method
|
||
'''
|
||
def run(self, saver):
|
||
innerleft = UCS.dispLen(max(self.fields, key = UCS.dispLen)) + self.left + 3
|
||
|
||
leftlines = []
|
||
datalines = []
|
||
|
||
for key in self.fields:
|
||
for line in self.datamap[key].split('\n'):
|
||
leftlines.append(key)
|
||
datalines.append(line)
|
||
|
||
(termh, termw) = self.termsize
|
||
(y, x) = (0, 0)
|
||
mark = None
|
||
|
||
KILL_MAX = 50
|
||
killring = []
|
||
killptr = None
|
||
|
||
def status(text):
|
||
print('\033[%i;%iH\033[7m%s\033[27m\033[%i;%iH' % (termh - 1, 1, ' (' + text + ') ' + '-' * (termw - len(' (' + text + ') ')), self.top + y, innerleft + x), end='')
|
||
|
||
status('unmodified')
|
||
|
||
print('\033[%i;%iH' % (self.top, innerleft), end='')
|
||
|
||
def alert(text):
|
||
if text is None:
|
||
alert('')
|
||
else:
|
||
print('\033[%i;%iH\033[2K%s\033[%i;%iH' % (termh, 1, text, self.top + y, innerleft + x), end='')
|
||
|
||
modified = False
|
||
override = False
|
||
|
||
(oldy, oldx, oldmark) = (y, x, mark)
|
||
stored = chr(ord('L') - ord('@'))
|
||
alerted = False
|
||
edited = False
|
||
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='')
|
||
while True:
|
||
if (oldmark is not None) and (oldmark >= 0):
|
||
if oldmark < oldx:
|
||
print('\033[%i;%iH\033[49m%s\033[%i;%iH' % (self.top + oldy, innerleft + oldmark, datalines[oldy][oldmark : oldx], self.top + y, innerleft + x), end='')
|
||
elif oldmark > oldx:
|
||
print('\033[%i;%iH\033[49m%s\033[%i;%iH' % (self.top + oldy, innerleft + oldx, datalines[oldy][oldx : oldmark], self.top + y, innerleft + x), end='')
|
||
if (mark is not None) and (mark >= 0):
|
||
if mark < x:
|
||
print('\033[%i;%iH\033[44;37m%s\033[49;39m\033[%i;%iH' % (self.top + y, innerleft + mark, datalines[y][mark : x], self.top + y, innerleft + x), end='')
|
||
elif mark > x:
|
||
print('\033[%i;%iH\033[44;37m%s\033[49;39m\033[%i;%iH' % (self.top + y, innerleft + x, datalines[y][x : mark], self.top + y, innerleft + x), end='')
|
||
if y != oldy:
|
||
if (oldy > 0) and (leftlines[oldy - 1] == leftlines[oldy]) and (leftlines[oldy] == leftlines[-1]):
|
||
print('\033[%i;%iH\033[34m%s\033[39m' % (self.top + oldy, self.left, '>'), end='')
|
||
else:
|
||
print('\033[%i;%iH\033[34m%s:\033[39m' % (self.top + oldy, self.left, leftlines[oldy]), end='')
|
||
if (y > 0) and (leftlines[y - 1] == leftlines[y]) and (leftlines[y] == leftlines[-1]):
|
||
print('\033[%i;%iH\033[1;34m%s\033[21;39m' % (self.top + y, self.left, '>'), end='')
|
||
else:
|
||
print('\033[%i;%iH\033[1;34m%s:\033[21;39m' % (self.top + y, self.left, leftlines[y]), end='')
|
||
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='')
|
||
(oldy, oldx, oldmark) = (y, x, mark)
|
||
if edited:
|
||
edited = False
|
||
if not modified:
|
||
modified = True
|
||
status('modified' + (' override' if override else ''))
|
||
sys.stdout.flush()
|
||
if stored is None:
|
||
d = sys.stdin.read(1)
|
||
else:
|
||
d = stored
|
||
stored = None
|
||
if alerted:
|
||
alerted = False
|
||
alert(None)
|
||
if ord(d) == ord('@') - ord('@'):
|
||
if mark is None:
|
||
mark = x
|
||
alert('Mark set')
|
||
elif mark == ~x:
|
||
mark = x
|
||
alert('Mark activated')
|
||
elif mark == x:
|
||
mark = ~x
|
||
alert('Mark deactivated')
|
||
else:
|
||
mark = x
|
||
alert('Mark set')
|
||
alerted = True
|
||
elif ord(d) == ord('K') - ord('@'):
|
||
if x == len(datalines[y]):
|
||
alert('At end')
|
||
alerted = True
|
||
else:
|
||
mark = len(datalines[y])
|
||
stored = chr(ord('W') - ord('@'))
|
||
elif ord(d) == ord('W') - ord('@'):
|
||
if (mark is not None) and (mark >= 0) and (mark != x):
|
||
selected = datalines[y][mark : x] if mark < x else datalines[y][x : mark]
|
||
killring.append(selected)
|
||
if len(killring) > KILL_MAX:
|
||
killring = killring[1:]
|
||
stored = chr(127)
|
||
else:
|
||
alert('No text is selected')
|
||
alerted = True
|
||
elif ord(d) == ord('Y') - ord('@'):
|
||
if len(killring) == 0:
|
||
alert('Killring is empty')
|
||
alerted = True
|
||
else:
|
||
mark = None
|
||
killptr = len(killring) - 1
|
||
yanked = killring[killptr]
|
||
print('\033[%i;%iH%s' % (self.top + y, innerleft + x, yanked + datalines[y][x:]), end='')
|
||
datalines[y] = datalines[y][:x] + yanked + datalines[y][x:]
|
||
x += len(yanked)
|
||
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='')
|
||
elif ord(d) == ord('X') - ord('@'):
|
||
alert('C-x')
|
||
alerted = True
|
||
sys.stdout.flush()
|
||
d = sys.stdin.read(1)
|
||
alert(str(ord(d)))
|
||
sys.stdout.flush()
|
||
if ord(d) == ord('X') - ord('@'):
|
||
if (mark is not None) and (mark >= 0):
|
||
x ^= mark; mark ^= x; x ^= mark
|
||
alert('Mark swapped')
|
||
else:
|
||
alert('No mark is activated')
|
||
elif ord(d) == ord('S') - ord('@'):
|
||
last = ''
|
||
for row in range(0, len(datalines)):
|
||
current = leftlines[row]
|
||
if len(datalines[row].strip()) == 0:
|
||
if current is not 'comment':
|
||
if current != last:
|
||
self.datamap[current] = None
|
||
continue
|
||
if current == last:
|
||
self.datamap[current] += '\n' + datalines[row]
|
||
else:
|
||
self.datamap[current] = datalines[row]
|
||
last = current
|
||
saver()
|
||
status('unmodified' + (' override' if override else ''))
|
||
alert('Saved')
|
||
elif ord(d) == ord('C') - ord('@'):
|
||
break
|
||
else:
|
||
stored = d
|
||
alerted = False
|
||
alert(None)
|
||
elif (ord(d) == 127) or (ord(d) == 8):
|
||
removed = 1
|
||
if (mark is not None) and (mark >= 0) and (mark != x):
|
||
if mark > x:
|
||
x ^= mark; mark ^= x; x ^= mark
|
||
removed = x - mark
|
||
if x == 0:
|
||
alert('At beginning')
|
||
alerted = True
|
||
continue
|
||
dataline = datalines[y]
|
||
datalines[y] = dataline = dataline[:x - removed] + dataline[x:]
|
||
x -= removed
|
||
mark = None
|
||
print('\033[%i;%iH%s%s\033[%i;%iH' % (self.top + y, innerleft, dataline, ' ' * removed, self.top + y, innerleft + x), end='')
|
||
edited = True
|
||
elif ord(d) < ord(' '):
|
||
if ord(d) == ord('P') - ord('@'):
|
||
if y == 0:
|
||
alert('At first line')
|
||
alerted = True
|
||
else:
|
||
y -= 1
|
||
mark = None
|
||
x = 0
|
||
elif ord(d) == ord('N') - ord('@'):
|
||
if y == len(datalines) - 1:
|
||
datalines.append('')
|
||
leftlines.append(leftlines[-1])
|
||
y += 1
|
||
mark = None
|
||
x = 0
|
||
elif ord(d) == ord('F') - ord('@'):
|
||
if x < len(datalines[y]):
|
||
x += 1
|
||
print('\033[C', end='')
|
||
else:
|
||
alert('At end')
|
||
alerted = True
|
||
elif ord(d) == ord('B') - ord('@'):
|
||
if x > 0:
|
||
x -= 1
|
||
print('\033[D', end='')
|
||
else:
|
||
alert('At beginning')
|
||
alerted = True
|
||
elif ord(d) == ord('O') - ord('@'):
|
||
leftlines[y : y] = [leftlines[y]]
|
||
datalines[y : y] = ['']
|
||
y += 1
|
||
mark = None
|
||
x = 0
|
||
stored = chr(ord('L') - ord('@'))
|
||
elif ord(d) == ord('L') - ord('@'):
|
||
empty = '\033[0m' + (' ' * self.width + '\n') * len(datalines)
|
||
print('\033[%i;%iH%s' % (self.top, self.left, empty), end='')
|
||
for row in range(0, len(leftlines)):
|
||
leftline = leftlines[row] + ':'
|
||
if (leftlines[row - 1] == leftlines[row]) and (leftlines[row] == leftlines[-1]):
|
||
leftline = '>'
|
||
print('\033[%i;%iH\033[%s34m%s\033[%s39m' % (self.top + row, self.left, '1;' if row == y else '', leftline, '21;' if row == y else ''), end='')
|
||
for row in range(0, len(datalines)):
|
||
print('\033[%i;%iH%s\033[49m' % (self.top + row, innerleft, datalines[row]), end='')
|
||
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='')
|
||
elif d == '\033':
|
||
d = sys.stdin.read(1)
|
||
if d == '[':
|
||
d = sys.stdin.read(1)
|
||
if d == 'A':
|
||
stored = chr(ord('P') - ord('@'))
|
||
elif d == 'B':
|
||
if y == len(datalines) - 1:
|
||
alert('At last line')
|
||
alerted = True
|
||
else:
|
||
stored = chr(ord('N') - ord('@'))
|
||
elif d == 'C':
|
||
stored = chr(ord('F') - ord('@'))
|
||
elif d == 'D':
|
||
stored = chr(ord('B') - ord('@'))
|
||
elif d == '2':
|
||
d = sys.stdin.read(1)
|
||
if d == '~':
|
||
override = not override
|
||
status(('modified' if modified else 'unmodified') + (' override' if override else ''))
|
||
elif d == '3':
|
||
d = sys.stdin.read(1)
|
||
if d == '~':
|
||
removed = 1
|
||
if (mark is not None) and (mark >= 0) and (mark != x):
|
||
if mark < x:
|
||
x ^= mark; mark ^= x; x ^= mark
|
||
removed = mark - x
|
||
dataline = datalines[y]
|
||
if x == len(dataline):
|
||
alert('At end')
|
||
alerted = True
|
||
continue
|
||
datalines[y] = dataline = dataline[:x] + dataline[x + removed:]
|
||
print('\033[%i;%iH%s%s\033[%i;%iH' % (self.top + y, innerleft, dataline, ' ' * removed, self.top + y, innerleft + x), end='')
|
||
mark = None
|
||
edited = True
|
||
else:
|
||
while True:
|
||
d = sys.stdin.read(1)
|
||
if (ord('a') <= ord(d)) and (ord(d) <= ord('z')): break
|
||
if (ord('A') <= ord(d)) and (ord(d) <= ord('Z')): break
|
||
if d == '~': break
|
||
elif (d == 'w') or (d == 'W'):
|
||
if (mark is not None) and (mark >= 0) and (mark != x):
|
||
selected = datalines[y][mark : x] if mark < x else datalines[y][x : mark]
|
||
killring.append(selected)
|
||
mark = None
|
||
if len(killring) > KILL_MAX:
|
||
killring = killring[1:]
|
||
else:
|
||
alert('No text is selected')
|
||
alerted = True
|
||
elif (d == 'y') or (d == 'Y'):
|
||
if killptr is not None:
|
||
yanked = killring[killptr]
|
||
dataline = datalines[y]
|
||
if (len(yanked) <= x) and (dataline[x - len(yanked) : x] == yanked):
|
||
killptr -= 1
|
||
if killptr < 0:
|
||
killptr += len(killring)
|
||
dataline = dataline[:x - len(yanked)] + killring[killptr] + dataline[x:]
|
||
additional = len(killring[killptr]) - len(yanked)
|
||
x += additional
|
||
datalines[y] = dataline
|
||
print('\033[%i;%iH%s%s\033[%i;%iH' % (self.top + y, innerleft, dataline, ' ' * max(0, -additional), self.top + y, innerleft + x), end='')
|
||
else:
|
||
stored = chr(ord('Y') - ord('@'))
|
||
else:
|
||
stored = chr(ord('Y') - ord('@'))
|
||
elif d == 'O':
|
||
d = sys.stdin.read(1)
|
||
if d == 'H':
|
||
x = 0
|
||
elif d == 'F':
|
||
x = len(datalines[y])
|
||
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='')
|
||
elif d == '\n':
|
||
stored = chr(ord('N') - ord('@'))
|
||
else:
|
||
insert = d
|
||
if len(insert) == 0:
|
||
continue
|
||
dataline = datalines[y]
|
||
if (not override) or (x == len(dataline)):
|
||
print(insert + dataline[x:], end='')
|
||
if len(dataline) - x > 0:
|
||
print('\033[%iD' % (len(dataline) - x), end='')
|
||
datalines[y] = dataline[:x] + insert + dataline[x:]
|
||
if (mark is not None) and (mark >= 0):
|
||
if mark >= x:
|
||
mark += len(insert)
|
||
else:
|
||
print(insert, end='')
|
||
datalines[y] = dataline[:x] + insert + dataline[x + 1:]
|
||
x += len(insert)
|
||
edited = True
|
||
|
||
|
||
|
||
'''
|
||
The user's home directory
|
||
'''
|
||
HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~')
|
||
|
||
'''
|
||
Whether stdin is piped
|
||
'''
|
||
pipelinein = not sys.stdin.isatty()
|
||
|
||
'''
|
||
Whether stdout is piped
|
||
'''
|
||
pipelineout = not sys.stdout.isatty()
|
||
|
||
'''
|
||
Whether stderr is piped
|
||
'''
|
||
pipelineerr = not sys.stderr.isatty()
|
||
|
||
|
||
usage_program = '\033[34;1mponysay-tool\033[21;39m'
|
||
|
||
usage = '\n'.join(['%s %s' % (usage_program, '(--help | --version | --kms)'),
|
||
'%s %s' % (usage_program, '(--edit | --edit-rm) \033[33mPONY-FILE\033[39m'),
|
||
'%s %s' % (usage_program, '--edit-stash \033[33mPONY-FILE\033[39m > \033[33mSTASH-FILE\033[39m'),
|
||
'%s %s' % (usage_program, '--edit-apply \033[33mPONY-FILE\033[39m < \033[33mSTASH-FILE\033[39m'),
|
||
'%s %s' % (usage_program, '(--dimensions | --metadata) \033[33mPONY-DIR\033[39m'),
|
||
'%s %s' % (usage_program, '--browse \033[33mPONY-DIR\033[39m [-r \033[33mRESTRICTION\033[39m]*'),
|
||
])
|
||
|
||
usage = usage.replace('\033[', '\0')
|
||
for sym in ('[', ']', '(', ')', '|', '...', '*'):
|
||
usage = usage.replace(sym, '\033[2m' + sym + '\033[22m')
|
||
usage = usage.replace('\0', '\033[')
|
||
|
||
'''
|
||
Argument parsing
|
||
'''
|
||
opts = ArgParser(program = 'ponysay-tool',
|
||
description = 'Tool chest for ponysay',
|
||
usage = usage,
|
||
longdescription = None)
|
||
|
||
opts.add_argumentless(['--no-term-init']) # for debugging
|
||
|
||
opts.add_argumentless(['-h', '--help'], help = 'Print this help message.')
|
||
opts.add_argumentless(['-v', '--version'], help = 'Print the version of the program.')
|
||
opts.add_argumentless(['--kms'], help = 'Generate all kmsponies for the current TTY palette')
|
||
opts.add_argumented( ['--dimensions'], arg = 'PONY-DIR', help = 'Generate pony dimension file for a directory')
|
||
opts.add_argumented( ['--metadata'], arg = 'PONY-DIR', help = 'Generate pony metadata collection file for a directory')
|
||
opts.add_argumented( ['-b', '--browse'], arg = 'PONY-DIR', help = 'Browse ponies in a directory')
|
||
opts.add_argumented( ['-r', '--restrict'], arg = 'RESTRICTION', help = 'Metadata based restriction for --browse')
|
||
opts.add_argumented( ['--edit'], arg = 'PONY-FILE', help = 'Edit a pony file\'s metadata')
|
||
opts.add_argumented( ['--edit-rm'], arg = 'PONY-FILE', help = 'Remove metadata from a pony file')
|
||
opts.add_argumented( ['--edit-apply'], arg = 'PONY-FILE', help = 'Apply metadata from stdin to a pony file')
|
||
opts.add_argumented( ['--edit-stash'], arg = 'PONY-FILE', help = 'Print applyable metadata from a pony file')
|
||
|
||
'''
|
||
Whether at least one unrecognised option was used
|
||
'''
|
||
unrecognised = not opts.parse()
|
||
|
||
PonysayTool(args = opts)
|
||
|