mirror of
https://github.com/erkin/ponysay.git
synced 2024-11-29 23:48:00 +01:00
ec73c4dd1d
Signed-off-by: Mattias Andrée <maandree@operamail.com>
1204 lines
51 KiB
Python
Executable file
1204 lines
51 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
'''
|
|
ponysay - Ponysay, cowsay reimplementation for ponies
|
|
Copyright (C) 2012 Erkin Batu Altunbaş et al.
|
|
|
|
This program is free software. It comes without any warranty, to
|
|
the extent permitted by applicable law. You can redistribute it
|
|
and/or modify it under the terms of the Do What The Fuck You Want
|
|
To Public License, Version 2, as published by Sam Hocevar. See
|
|
http://sam.zoy.org/wtfpl/COPYING for more details.
|
|
'''
|
|
|
|
import os
|
|
import sys
|
|
from subprocess import Popen, PIPE
|
|
|
|
from ponysay 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])
|
|
|
|
elif (opts['--metadata'] is not None) and (len(opts['--metadata']) == 1):
|
|
self.generateMetadata(opts['--metadata'][0])
|
|
|
|
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
|
|
self.browse(opts['-b'][0], 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
|
|
|
|
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 = Ponysay.makeRestrictionLogic(restriction)
|
|
ponies = set()
|
|
for pony in Ponysay.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 = ponyfile
|
|
self.opts = self
|
|
self.key = key
|
|
self.value = value
|
|
def __getitem__(self, key):
|
|
return [self.value] if key == self.key else None
|
|
|
|
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
|
|
|
|
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.stdout = StringInputStream()
|
|
ponysay = Ponysay()
|
|
ponysay.run(PhonyArgParser('--pony', pony))
|
|
|
|
for pony in extraponies:
|
|
printerr('Genering extra kmspony: %s' % pony)
|
|
sys.stdout = StringInputStream()
|
|
ponysay = Ponysay()
|
|
ponysay.run(PhonyArgParser('++pony', pony))
|
|
|
|
sys.stdout = stdout
|
|
|
|
|
|
'''
|
|
Generate pony dimension file for a directory
|
|
|
|
@param ponydir The directory
|
|
'''
|
|
def generateDimensions(self, ponydir):
|
|
dimensions = []
|
|
for ponyfile in os.listdir(ponydir):
|
|
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
|
|
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):
|
|
sort(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 The directory
|
|
'''
|
|
def generateMetadata(self, ponydir):
|
|
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 = []
|
|
for ponyfile in os.listdir(ponydir):
|
|
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):
|
|
data.append((key, makeset(value.replace(' ', ''))))
|
|
everything.append(ponyfile[:-5], data)
|
|
import cPickle
|
|
with open(ponydir + 'metadata', 'wb') as file:
|
|
cPickle.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
|
|
|
|
|
|
data = {}
|
|
comment = []
|
|
for line in meta:
|
|
if ': ' in line:
|
|
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
|
|
|
|
|
|
|
|
'''
|
|
Start the program from ponysay.__init__ if this is the executed file
|
|
'''
|
|
if __name__ == '__main__':
|
|
'''
|
|
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)
|
|
|