kuroko port

This commit is contained in:
K. Lange 2022-03-22 22:41:22 +09:00
parent 8a2c71416e
commit 2a32fed223
14 changed files with 592 additions and 531 deletions

1
.gitignore vendored
View file

@ -39,7 +39,6 @@ _/
/*ponies/onlyheights /*ponies/onlyheights
/*ponies/widths /*ponies/widths
/*ponies/metadata /*ponies/metadata
/ponysay
/bin/ /bin/
/obj/ /obj/

View file

@ -39,56 +39,57 @@ Authors:
Jan Alexander "heftig" Steffens: Major contributor of the first implementation Jan Alexander "heftig" Steffens: Major contributor of the first implementation
Kyah "L-four" Rindlisbacher: Patched the first implementation Kyah "L-four" Rindlisbacher: Patched the first implementation
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith
from argparser import * from ponysay.argparser import ArgParser
from ponysay import * from ponysay.ponysay import Ponysay
import os
import kuroko
''' '''
Start the program Start the program
''' '''
if __name__ == '__main__': if __name__ == '__main__':
istool = sys.argv[0] let istool = kuroko.argv[0]
if os.sep in istool: if os.sep in istool:
istool = istool[istool.rfind(os.sep) + 1:] istool = istool[istool.rfind(os.sep) + 1:]
if os.extsep in istool: if os.extsep in istool:
istool = istool[:istool.find(os.extsep)] istool = istool[:istool.find(os.extsep)]
istool = istool.endswith('-tool') istool = istool.endswith('-tool')
if istool: if istool:
from ponysaytool import * ## will start ponysay-tool import ponysaytool
exit(0) os.exit(0)
isthink = sys.argv[0] let isthink = kuroko.argv[0]
if os.sep in isthink: if os.sep in isthink:
isthink = isthink[isthink.rfind(os.sep) + 1:] isthink = isthink[isthink.rfind(os.sep) + 1:]
if os.extsep in isthink: if os.extsep in isthink:
isthink = isthink[:isthink.find(os.extsep)] isthink = isthink[:isthink.find(os.extsep)]
isthink = isthink.endswith('think') isthink = isthink.endswith('think')
usage_saythink = '\033[34;1m(ponysay | ponythink)\033[21;39m' let usage_saythink = '\033[34;1m(ponysay | ponythink)\033[22;39m'
usage_common = '[-c] [-W\033[33mCOLUMN\033[39m] [-b\033[33mSTYLE\033[39m]' let usage_common = '[-c] [-W\033[33mCOLUMN\033[39m] [-b\033[33mSTYLE\033[39m]'
usage_listhelp = '(-l | -L | -B | +l | +L | -A | + A | -v | -h)' let usage_listhelp = '(-l | -L | -B | +l | +L | -A | + A | -v | -h)'
usage_file = '[-f\033[33mPONY\033[39m]* [[--] \033[33mmessage\033[39m]' let usage_file = '[-f\033[33mPONY\033[39m]* [[--] \033[33mmessage\033[39m]'
usage_xfile = '(+f\033[33mPONY\033[39m)* [[--] \033[33mmessage\033[39m]' let usage_xfile = '(+f\033[33mPONY\033[39m)* [[--] \033[33mmessage\033[39m]'
usage_afile = '(-F\033[33mPONY\033[39m)* [[--] \033[33mmessage\033[39m]' let usage_afile = '(-F\033[33mPONY\033[39m)* [[--] \033[33mmessage\033[39m]'
usage_quote = '(-q\033[33mPONY\033[39m)*' let usage_quote = '(-q\033[33mPONY\033[39m)*'
usage = ('%s %s' + 4 * '\n%s %s %s') % (usage_saythink, usage_listhelp, let usage = ('{} {}' + 4 * '\n{} {} {}') .format (usage_saythink, usage_listhelp,
usage_saythink, usage_common, usage_file, usage_saythink, usage_common, usage_file,
usage_saythink, usage_common, usage_xfile, usage_saythink, usage_common, usage_xfile,
usage_saythink, usage_common, usage_afile, usage_saythink, usage_common, usage_afile,
usage_saythink, usage_common, usage_quote) usage_saythink, usage_common, usage_quote)
usage = usage.replace('\033[', '\0') usage = usage.replace('\033[', '')
for sym in ('[', ']', '(', ')', '|', '...', '*'): for sym in ('[', ']', '(', ')', '|', '...', '*'):
usage = usage.replace(sym, '\033[2m' + sym + '\033[22m') usage = usage.replace(sym, '\033[2m' + sym + '\033[22m')
usage = usage.replace('\0', '\033[') usage = usage.replace('', '\033[')
''' '''
Argument parsing Argument parsing
''' '''
opts = ArgParser(program = 'ponythink' if isthink else 'ponysay', let opts = ArgParser(program = 'ponythink' if isthink else 'ponysay',
description = 'cowsay reimplemention for ponies', description = 'cowsay reimplemention for ponies',
usage = usage, usage = usage,
longdescription = longdescription =
@ -117,8 +118,8 @@ run `man ponysay`. Ponysay has so much more to offer than described here.''')
opts.add_argumented( ['--colour-pony'], arg = 'COLOUR') opts.add_argumented( ['--colour-pony'], arg = 'COLOUR')
opts.add_argumented( ['--colour-wrap', '--colour-hyphen'], arg = 'COLOUR') opts.add_argumented( ['--colour-wrap', '--colour-hyphen'], arg = 'COLOUR')
_F = ['--any-file', '--anyfile', '--any-pony', '--anypony'] let _F = ['--any-file', '--anyfile', '--any-pony', '--anypony']
__F = [_.replace("pony", "ponie") + 's' for _ in _F] let __F = [_.replace("pony", "ponie") + 's' for _ in _F]
opts.add_argumentless(['-h', '--help'], help = 'Print this help message.') opts.add_argumentless(['-h', '--help'], help = 'Print this help message.')
opts.add_argumentless(['+h', '++help', '--help-colour'], help = 'Print this help message with colours even if piped.') opts.add_argumentless(['+h', '++help', '--help-colour'], help = 'Print this help message with colours even if piped.')
opts.add_argumentless(['-v', '--version'], help = 'Print the version of the program.') opts.add_argumentless(['-v', '--version'], help = 'Print the version of the program.')
@ -145,10 +146,10 @@ run `man ponysay`. Ponysay has so much more to offer than described here.''')
''' '''
Whether at least one unrecognised option was used Whether at least one unrecognised option was used
''' '''
unrecognised = not opts.parse() let unrecognised = not opts.parse()
## Start ## Start
ponysay = Ponysay() let ponysay = Ponysay()
ponysay.unrecognised = unrecognised ponysay.unrecognised = unrecognised
ponysay.run(opts) ponysay.run(opts)

View file

@ -29,21 +29,23 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith
import os
import kuroko
import fileio
let ARGUMENTLESS = 0
ARGUMENTLESS = 0
''' '''
Option takes no arguments Option takes no arguments
''' '''
ARGUMENTED = 1 let ARGUMENTED = 1
''' '''
Option takes one argument per instance Option takes one argument per instance
''' '''
VARIADIC = 2 let VARIADIC = 2
''' '''
Option consumes all following arguments Option consumes all following arguments
''' '''
@ -83,7 +85,7 @@ class ArgParser():
@param help:str Short description, use `None` to hide the option @param help:str Short description, use `None` to hide the option
''' '''
self.__arguments.append((ARGUMENTLESS, alternatives, None, help)) self.__arguments.append((ARGUMENTLESS, alternatives, None, help))
stdalt = alternatives[0] let stdalt = alternatives[0]
self.opts[stdalt] = None self.opts[stdalt] = None
for alt in alternatives: for alt in alternatives:
self.optmap[alt] = (stdalt, ARGUMENTLESS) self.optmap[alt] = (stdalt, ARGUMENTLESS)
@ -97,7 +99,7 @@ class ArgParser():
@param help:str Short description, use `None` to hide the option @param help:str Short description, use `None` to hide the option
''' '''
self.__arguments.append((ARGUMENTED, alternatives, arg, help)) self.__arguments.append((ARGUMENTED, alternatives, arg, help))
stdalt = alternatives[0] let stdalt = alternatives[0]
self.opts[stdalt] = None self.opts[stdalt] = None
for alt in alternatives: for alt in alternatives:
self.optmap[alt] = (stdalt, ARGUMENTED) self.optmap[alt] = (stdalt, ARGUMENTED)
@ -111,13 +113,13 @@ class ArgParser():
@param help:str Short description, use `None` to hide the option @param help:str Short description, use `None` to hide the option
''' '''
self.__arguments.append((VARIADIC, alternatives, arg, help)) self.__arguments.append((VARIADIC, alternatives, arg, help))
stdalt = alternatives[0] let stdalt = alternatives[0]
self.opts[stdalt] = None self.opts[stdalt] = None
for alt in alternatives: for alt in alternatives:
self.optmap[alt] = (stdalt, VARIADIC) self.optmap[alt] = (stdalt, VARIADIC)
def parse(self, argv = sys.argv): def parse(self, argv = kuroko.argv):
''' '''
Parse arguments Parse arguments
@ -127,23 +129,24 @@ class ArgParser():
self.argcount = len(argv) - 1 self.argcount = len(argv) - 1
self.files = [] self.files = []
argqueue = [] let arg, i, n, opt, varopt
optqueue = [] let argqueue = []
deque = [] let optqueue = []
let deque = []
for arg in argv[1:]: for arg in argv[1:]:
deque.append(arg) deque.append(arg)
dashed = False let dashed = False
tmpdashed = False let tmpdashed = False
get = 0 let get = 0
dontget = 0 let dontget = 0
self.rc = True self.rc = True
self.unrecognisedCount = 0 self.unrecognisedCount = 0
def unrecognised(arg): def unrecognised(arg):
self.unrecognisedCount += 1 self.unrecognisedCount += 1
if self.unrecognisedCount <= 5: if self.unrecognisedCount <= 5:
sys.stderr.write('%s: warning: unrecognised option %s\n' % (self.__program, arg)) fileio.stderr.write('{}: warning: unrecognised option {}\n' .format (self.__program, arg))
self.rc = False self.rc = False
while len(deque) != 0: while len(deque) != 0:
@ -184,11 +187,11 @@ class ArgParser():
else: else:
unrecognised(arg) unrecognised(arg)
else: else:
sign = arg[0] let sign = arg[0]
i = 1 let i = 1
n = len(arg) let n = len(arg)
while i < n: while i < n:
narg = sign + arg[i] let narg = sign + arg[i]
i += 1 i += 1
if (narg in self.optmap): if (narg in self.optmap):
if self.optmap[narg][1] == ARGUMENTLESS: if self.optmap[narg][1] == ARGUMENTLESS:
@ -196,7 +199,7 @@ class ArgParser():
argqueue.append(None) argqueue.append(None)
elif self.optmap[narg][1] == ARGUMENTED: elif self.optmap[narg][1] == ARGUMENTED:
optqueue.append(narg) optqueue.append(narg)
nargarg = arg[i:] let nargarg = arg[i:]
if len(nargarg) == 0: if len(nargarg) == 0:
get += 1 get += 1
else: else:
@ -204,7 +207,7 @@ class ArgParser():
break break
elif self.optmap[narg][1] == VARIADIC: elif self.optmap[narg][1] == VARIADIC:
optqueue.append(narg) optqueue.append(narg)
nargarg = arg[i:] let nargarg = arg[i:]
argqueue.append(nargarg if len(nargarg) > 0 else None) argqueue.append(nargarg if len(nargarg) > 0 else None)
dashed = True dashed = True
break break
@ -240,7 +243,7 @@ class ArgParser():
self.message = ' '.join(self.files) if len(self.files) > 0 else None self.message = ' '.join(self.files) if len(self.files) > 0 else None
if self.unrecognisedCount > 5: if self.unrecognisedCount > 5:
sys.stderr.write('%s: warning: %i more unrecognised %s\n' % (self.unrecognisedCount - 5, 'options' if self.unrecognisedCount == 6 else 'options')) fileio.stderr.write('{}: warning: {} more unrecognised {}\n' .format (self.unrecognisedCount - 5, 'options' if self.unrecognisedCount == 6 else 'options'))
return self.rc return self.rc
@ -252,94 +255,102 @@ class ArgParser():
@param use_colours:bool? Whether to use colours, `None` if stdout is not piped @param use_colours:bool? Whether to use colours, `None` if stdout is not piped
''' '''
if use_colours is None: if use_colours is None:
use_colours = sys.stdout.isatty() use_colours = True # os.isatty(1)
print(('\033[1m%s\033[21m %s %s' if use_colours else '%s %s %s') % (self.__program, '-' if self.linuxvt else '—', self.__description)) print(('\[[1m{}\[[22m {} {}' if use_colours else '{} {} {}').format(self.__program, '-' if self.linuxvt else '—', self.__description))
print() print()
if self.__longdescription is not None: if self.__longdescription is not None:
desc = self.__longdescription let desc = self.__longdescription
if not use_colours: if not use_colours:
while '\033' in desc: while '\[' in desc:
esc = desc.find('\033') esc = desc.find('\[')
desc = desc[:esc] + desc[desc.find('m', esc) + 1:] desc = desc[:esc] + desc[desc.find('m', esc) + 1:]
print(desc) print(desc)
print() print()
print('\033[1mUSAGE:\033[21m' if use_colours else 'USAGE:', end='') print('\[[1mUSAGE:\[[22m' if use_colours else 'USAGE:', end='')
first = True let first = True
for line in self.__usage.split('\n'): for line in self.__usage.split('\n'):
if first: if first:
first = False first = False
else: else:
print(' or', end='') print(' or', end='')
if not use_colours: if not use_colours:
while '\033' in line: while '\[' in line:
esc = line.find('\033') esc = line.find('\[')
line = line[:esc] + line[line.find('m', esc) + 1:] line = line[:esc] + line[line.find('m', esc) + 1:]
print('\t%s' % line) print('\t{}'.format(line))
print() print()
maxfirstlen = [] let maxfirstlen = []
for opt in self.__arguments: for opt in self.__arguments:
opt_alts = opt[1] let opt_alts = opt[1]
opt_help = opt[3] let opt_help = opt[3]
if opt_help is None: if opt_help is None:
continue continue
first = opt_alts[0] first = opt_alts[0]
last = opt_alts[-1] let last = opt_alts[-1]
if first is not last: if first is not last:
maxfirstlen.append(first) maxfirstlen.append(first)
maxfirstlen = len(max(maxfirstlen, key = len)) maxfirstlen = len(max(maxfirstlen, key = len))
print('\033[1mSYNOPSIS:\033[21m' if use_colours else 'SYNOPSIS') print('\[[1mSYNOPSIS:\[[22m' if use_colours else 'SYNOPSIS')
(lines, lens) = ([], []) let lines, lens = [], []
for opt in self.__arguments: for opt in self.__arguments:
opt_type = opt[0] let opt_type = opt[0]
opt_alts = opt[1] let opt_alts = opt[1]
opt_arg = opt[2] let opt_arg = opt[2]
opt_help = opt[3] let opt_help = opt[3]
if opt_help is None: if opt_help is None:
continue continue
(line, l) = ('', 0) let line, l = '', 0
first = opt_alts[0] first = opt_alts[0]
last = opt_alts[-1] let last = opt_alts[-1]
alts = ['', last] if first is last else [first, last] let alts = ['', last] if first is last else [first, last]
alts[0] += ' ' * (maxfirstlen - len(alts[0])) alts[0] += ' ' * (maxfirstlen - len(alts[0]))
for opt_alt in alts: for opt_alt in alts:
if opt_alt is alts[-1]: if opt_alt is alts[-1]:
line += '%colour%' + opt_alt line += '%colour%' + opt_alt
l += len(opt_alt) l += len(opt_alt)
if use_colours: if use_colours:
if opt_type == ARGUMENTED: line += ' \033[4m%s\033[24m' % (opt_arg); l += len(opt_arg) + 1 if opt_type == ARGUMENTED:
elif opt_type == VARIADIC: line += ' [\033[4m%s\033[24m...]' % (opt_arg); l += len(opt_arg) + 6 line += ' \[[4m{}\[[24m' .format (opt_arg)
l += len(opt_arg) + 1
elif opt_type == VARIADIC:
line += ' [\[[4m{}\[[24m...]' .format (opt_arg)
l += len(opt_arg) + 6
else: else:
if opt_type == ARGUMENTED: line += ' %s' % (opt_arg); l += len(opt_arg) + 1 if opt_type == ARGUMENTED:
elif opt_type == VARIADIC: line += ' [%s...]' % (opt_arg); l += len(opt_arg) + 6 line += ' {}' .format (opt_arg)
l += len(opt_arg) + 1
elif opt_type == VARIADIC:
line += ' [{}...]' .format (opt_arg)
l += len(opt_arg) + 6
else: else:
if use_colours: if use_colours:
line += ' \033[2m%s\033[22m ' % (opt_alt) line += ' \[[2m{}\[[22m ' .format (opt_alt)
else: else:
line += ' %s ' % (opt_alt) line += ' {} ' .format (opt_alt)
l += len(opt_alt) + 6 l += len(opt_alt) + 6
lines.append(line) lines.append(line)
lens.append(l) lens.append(l)
col = max(lens) let col = max(lens)
col += 8 - ((col - 4) & 7) col += 8 - ((col - 4) & 7)
index = 0 let index = 0
for opt in self.__arguments: for opt in self.__arguments:
opt_help = opt[3] let opt_help = opt[3]
if opt_help is None: if opt_help is None:
continue continue
first = True first = True
colour = ('36' if (index & 1) == 0 else '34') if use_colours else '' let colour = ('36' if (index & 1) == 0 else '34') if use_colours else ''
print(lines[index].replace('%colour%', ('\033[%s;1m' % colour) if use_colours else ''), end=' ' * (col - lens[index])) print(lines[index].replace('%colour%', ('\[[{};1m' .format (colour)) if use_colours else ''), end=' ' * (col - lens[index]))
for line in opt_help.split('\n'): for line in opt_help.split('\n'):
if first: if first:
first = False first = False
print('%s' % (line), end='\033[21;39m\n' if use_colours else '\n') print(line, end='\[[22;39m\n' if use_colours else '\n')
else: else:
print(('%s\033[%sm%s\033[39m' if use_colours else '%s%s%s') % (' ' * col, colour, line)) print(('{}\[[{}m{}\[[39m' if use_colours else '{}{}{}').format (' ' * col, colour, line))
index += 1 index += 1
print() print()

View file

@ -29,14 +29,14 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith
from balloon import * from ponysay.balloon import Balloon
from colourstack import * from ponysay.colourstack import ColourStack
from ucs import * from ponysay.ucs import UCS
import unicodedata
from wcwidth import wcwidth
import fileio
import os
class Backend(): class Backend():
''' '''
@ -89,6 +89,7 @@ class Backend():
if self.pony.startswith('$$$\n'): if self.pony.startswith('$$$\n'):
self.pony = self.pony[4:] self.pony = self.pony[4:]
let infoend, info
if self.pony.startswith('$$$\n'): if self.pony.startswith('$$$\n'):
infoend = 4 infoend = 4
info = '' info = ''
@ -104,21 +105,21 @@ class Backend():
else: else:
info = info.split('\n') info = info.split('\n')
for line in info: for line in info:
sep = line.find(':') let sep = line.find(':')
if sep > 0: if sep > 0:
key = line[:sep].strip() let key = line[:sep].strip()
if key == 'BALLOON TOP': if key == 'BALLOON TOP':
value = line[sep + 1:].strip() let value = line[sep + 1:].strip()
if len(value) > 0: if len(value) > 0:
self.balloontop = int(value) self.balloontop = int(value)
if key == 'BALLOON BOTTOM': if key == 'BALLOON BOTTOM':
value = line[sep + 1:].strip() let value = line[sep + 1:].strip()
if len(value) > 0: if len(value) > 0:
self.balloonbottom = int(value) self.balloonbottom = int(value)
printinfo(info) printinfo(info)
self.pony = self.pony[infoend:] self.pony = self.pony[infoend:]
elif self.infolevel == 2: elif self.infolevel == 2:
self.message = '\033[01;31mI am the mysterious mare...\033[21;39m' self.message = '\033[01;31mI am the mysterious mare...\033[22;39m'
elif self.infolevel == 1: elif self.infolevel == 1:
self.pony = 'There is not metadata for this pony file' self.pony = 'There is not metadata for this pony file'
self.pony = self.mode + self.pony self.pony = self.mode + self.pony
@ -149,7 +150,7 @@ class Backend():
test = test.replace(c, '') test = test.replace(c, '')
if (len(test) == 0) and (len(key.replace(' ', '')) > 0): if (len(test) == 0) and (len(key.replace(' ', '')) > 0):
value = line[sep + 1:].strip() value = line[sep + 1:].strip()
line = '\033[1m%s\033[21m: %s\n' % (key.strip(), value) line = '\033[1m{}\033[22m: {}\n' .format (key.strip(), value)
tags += line tags += line
continue continue
comment += '\n' + line comment += '\n' + line
@ -163,16 +164,16 @@ class Backend():
''' '''
Remove padding spaces fortune cookies are padded with whitespace (damn featherbrains) Remove padding spaces fortune cookies are padded with whitespace (damn featherbrains)
''' '''
lines = self.message.split('\n') let lines = self.message.split('\n')
for spaces in (128, 64, 32, 16, 8, 4, 2, 1): for spaces in (128, 64, 32, 16, 8, 4, 2, 1):
padded = True let padded = True
for line in lines: for line in lines:
if not line.startswith(' ' * spaces): if not line.startswith(' ' * spaces):
padded = False padded = False
break break
if padded: if padded:
for i in range(0, len(lines)): for i in range(0, len(lines)):
line = lines[i] let line = lines[i]
line = line[spaces:] line = line[spaces:]
lines[i] = line lines[i] = line
lines = [line.rstrip(' ') for line in lines] lines = [line.rstrip(' ') for line in lines]
@ -183,19 +184,19 @@ class Backend():
''' '''
Converts all tabs in the message to spaces by expanding Converts all tabs in the message to spaces by expanding
''' '''
lines = self.message.split('\n') let lines = self.message.split('\n')
buf = '' let buf = ''
for line in lines: for line in lines:
(i, n, x) = (0, len(line), 0) let i, n, x = (0, len(line), 0)
while i < n: while i < n:
c = line[i] let c = line[i]
i += 1 i += 1
if c == '\033': if c == '\033':
colour = Backend.getColour(line, i - 1) let colour = Backend.getColour(line, i - 1)
i += len(colour) - 1 i += len(colour) - 1
buf += colour buf += colour
elif c == '\t': elif c == '\t':
nx = 8 - (x & 7) let nx = 8 - (x & 7)
buf += ' ' * nx buf += ' ' * nx
x += nx x += nx
else: else:
@ -210,8 +211,8 @@ class Backend():
''' '''
Loads the pony file Loads the pony file
''' '''
with open(self.ponyfile, 'rb') as ponystream: with fileio.open(self.ponyfile, 'rb') as ponystream:
self.pony = ponystream.read().decode('utf8', 'replace') self.pony = ponystream.read().decode()
def __truncate(self): def __truncate(self):
@ -220,15 +221,15 @@ class Backend():
''' '''
if self.width is None: if self.width is None:
return return
lines = self.output.split('\n') let lines = self.output.split('\n')
self.output = '' self.output = ''
for line in lines: for line in lines:
(i, n, x) = (0, len(line), 0) let i, n, x = (0, len(line), 0)
while i < n: while i < n:
c = line[i] let c = line[i]
i += 1 i += 1
if c == '\033': if c == '\033':
colour = Backend.getColour(line, i - 1) let colour = Backend.getColour(line, i - 1)
i += len(colour) - 1 i += len(colour) - 1
self.output += colour self.output += colour
else: else:
@ -246,35 +247,36 @@ class Backend():
''' '''
self.output = '' self.output = ''
AUTO_PUSH = '\033[01010~' let AUTO_PUSH = '\033[01010~'
AUTO_POP = '\033[10101~' let AUTO_POP = '\033[10101~'
variables = {'' : '$'} let variables = {'' : '$'}
for key in self.link: for key in self.link:
variables[key] = AUTO_PUSH + self.link[key] + AUTO_POP variables[key] = AUTO_PUSH + self.link[key] + AUTO_POP
indent = 0 let indent = 0
dollar = None let dollar = None
balloonLines = None let balloonLines = None
colourstack = ColourStack(AUTO_PUSH, AUTO_POP) let balloonLine, balloonIndent
let colourstack = ColourStack(AUTO_PUSH, AUTO_POP)
(i, n, lineindex, skip, nonskip) = (0, len(self.pony), 0, 0, 0) let i, n, lineindex, skip, nonskip = (0, len(self.pony), 0, 0, 0)
while i < n: while i < n:
c = self.pony[i] let c = self.pony[i]
if c == '\t': if c == '\t':
n += 7 - (indent & 7) n += 7 - (indent & 7)
ed = ' ' * (8 - (indent & 7)) let ed = ' ' * (8 - (indent & 7))
c = ' ' c = ' '
self.pony = self.pony[:i] + ed + self.pony[i + 1:] self.pony = self.pony[:i] + ed + self.pony[i + 1:]
i += 1 i += 1
if c == '$': if c == '$':
if dollar is not None: if dollar is not None:
if '=' in dollar: if '=' in dollar:
name = dollar[:dollar.find('=')] let name = dollar[:dollar.find('=')]
value = dollar[dollar.find('=') + 1:] let value = dollar[dollar.find('=') + 1:]
variables[name] = value variables[name] = value
elif not dollar.startswith('balloon'): elif not dollar.startswith('balloon'):
data = variables[dollar].replace('$', '$$') let data = variables[dollar].replace('$', '$$')
if data == '$$': # if not handled specially we will get an infinity loop if data == '$$': # if not handled specially we will get an infinity loop
if (skip == 0) or (nonskip > 0): if (skip == 0) or (nonskip > 0):
if nonskip > 0: if nonskip > 0:
@ -287,8 +289,8 @@ class Backend():
n += len(data) n += len(data)
self.pony = self.pony[:i] + data + self.pony[i:] self.pony = self.pony[:i] + data + self.pony[i:]
elif self.balloon is not None: elif self.balloon is not None:
(w, h, x, justify) = ('0', 0, 0, None) let w, h, x, justify = ('0', 0, 0, None)
props = dollar[7:] let props = dollar[7:]
if len(props) > 0: if len(props) > 0:
if ',' in props: if ',' in props:
if props[0] != ',': if props[0] != ',':
@ -299,28 +301,28 @@ class Backend():
if 'l' in w: if 'l' in w:
(x, w) = (int(w[:w.find('l')]), int(w[w.find('l') + 1:])) (x, w) = (int(w[:w.find('l')]), int(w[w.find('l') + 1:]))
justify = 'l' justify = 'l'
w -= x; w -= x
elif 'c' in w: elif 'c' in w:
(x, w) = (int(w[:w.find('c')]), int(w[w.find('c') + 1:])) (x, w) = (int(w[:w.find('c')]), int(w[w.find('c') + 1:]))
justify = 'c' justify = 'c'
w -= x; w -= x
elif 'r' in w: elif 'r' in w:
(x, w) = (int(w[:w.find('r')]), int(w[w.find('r') + 1:])) (x, w) = (int(w[:w.find('r')]), int(w[w.find('r') + 1:]))
justify = 'r' justify = 'r'
w -= x; w -= x
else: else:
w = int(w) w = int(w)
balloon = self.__getBalloon(w, h, x, justify, indent) let balloon = self.__getBalloon(w, h, x, justify, indent)
balloon = balloon.split('\n') balloon = balloon.split('\n')
balloon = [AUTO_PUSH + self.ballooncolour + item + AUTO_POP for item in balloon] balloon = [AUTO_PUSH + self.ballooncolour + item + AUTO_POP for item in balloon]
for b in balloon[0]: for b in balloon[0]:
self.output += b + colourstack.feed(b) self.output += b + colourstack.feed(b)
if lineindex == 0: if lineindex == 0:
balloonpre = '\n' + (' ' * indent) let balloonpre = '\n' + (' ' * indent)
for line in balloon[1:]: for line in balloon[1:]:
self.output += balloonpre; self.output += balloonpre
for b in line: for b in line:
self.output += b + colourstack.feed(b); self.output += b + colourstack.feed(b)
indent = 0 indent = 0
elif len(balloon) > 1: elif len(balloon) > 1:
balloonLines = balloon balloonLines = balloon
@ -337,9 +339,9 @@ class Backend():
i += 1 i += 1
dollar += c dollar += c
elif c == '\033': elif c == '\033':
colour = Backend.getColour(self.pony, i - 1) let colour = Backend.getColour(self.pony, i - 1)
for b in colour: for b in colour:
self.output += b + colourstack.feed(b); self.output += b + colourstack.feed(b)
i += len(colour) - 1 i += len(colour) - 1
elif c == '\n': elif c == '\n':
self.output += c self.output += c
@ -364,7 +366,7 @@ class Backend():
if (skip == 0) or (nonskip > 0): if (skip == 0) or (nonskip > 0):
if nonskip > 0: if nonskip > 0:
nonskip -= 1 nonskip -= 1
self.output += c + colourstack.feed(c); self.output += c + colourstack.feed(c)
if not UCS.isCombining(c): if not UCS.isCombining(c):
indent += 1 indent += 1
else: else:
@ -374,7 +376,7 @@ class Backend():
for line in balloonLines[balloonLine:]: for line in balloonLines[balloonLine:]:
data = ' ' * (balloonIndent - indent) + line + '\n' data = ' ' * (balloonIndent - indent) + line + '\n'
for b in data: for b in data:
self.output += b + colourstack.feed(b); self.output += b + colourstack.feed(b)
indent = 0 indent = 0
self.output = self.output.replace(AUTO_PUSH, '').replace(AUTO_POP, '') self.output = self.output.replace(AUTO_PUSH, '').replace(AUTO_POP, '')
@ -395,11 +397,11 @@ class Backend():
@param offset:int The offset at where to start reading, a escape must begin here @param offset:int The offset at where to start reading, a escape must begin here
@return :str The escape sequence @return :str The escape sequence
''' '''
(i, n) = (offset, len(input)) let i, n = (offset, len(input))
rc = input[i] let rc = input[i]
i += 1 i += 1
if i == n: return rc if i == n: return rc
c = input[i] let c = input[i]
i += 1 i += 1
rc += c rc += c
@ -450,16 +452,16 @@ class Backend():
@param input:str The input buffer @param input:str The input buffer
@return :int The number of visible characters @return :int The number of visible characters
''' '''
(rc, i, n) = (0, 0, len(input)) let rc, i, n = 0, 0, len(input)
while i < n: while i < n:
c = input[i] let c = input[i]
if c == '\033': if c == '\033':
i += len(Backend.getColour(input, i)) i += len(Backend.getColour(input, i))
else: else:
i += 1 i += 1
if not UCS.isCombining(c): if not UCS.isCombining(c):
rc += 1 rc += 1
if unicodedata.east_asian_width(c) in ('F', 'W'): if wcwidth(ord(c)) in ('F', 'W'):
rc += 1 rc += 1
return rc return rc
@ -476,22 +478,22 @@ class Backend():
@param left:int The column where the balloon starts @param left:int The column where the balloon starts
@return :str The balloon the the message as a string @return :str The balloon the the message as a string
''' '''
wrap = None let wrap = None
if self.wrapcolumn is not None: if self.wrapcolumn is not None:
wrap = self.wrapcolumn - left wrap = self.wrapcolumn - left
if wrap < 8: if wrap < 8:
wrap = 8 wrap = 8
msg = self.message let msg = self.message
if wrap is not None: if wrap is not None:
msg = self.__wrapMessage(msg, wrap) msg = self.__wrapMessage(msg, wrap)
msg = msg.replace('\n', '\033[0m%s\n' % (self.ballooncolour)) + '\033[0m' + self.ballooncolour msg = msg.replace('\n', '\033[0m{}\n' .format (self.ballooncolour)) + '\033[0m' + self.ballooncolour
msg = msg.split('\n') msg = msg.split('\n')
extraleft = 0 let extraleft = 0
if justify is not None: if justify is not None:
msgwidth = self.len(max(msg, key = self.len)) + self.balloon.minwidth let msgwidth = self.len(max(msg, key = self.len)) + self.balloon.minwidth
extraleft = innerleft extraleft = innerleft
if msgwidth > width: if msgwidth > width:
if (justify == 'l') and (wrap is not None): if (justify == 'l') and (wrap is not None):
@ -507,7 +509,7 @@ class Backend():
if extraleft + msgwidth > wrap: if extraleft + msgwidth > wrap:
extraleft -= msgwidth - wrap extraleft -= msgwidth - wrap
rc = self.balloon.get(width, height, msg, Backend.len); let rc = self.balloon.get(width, height, msg, Backend.len)
if extraleft > 0: if extraleft > 0:
rc = ' ' * extraleft + rc.replace('\n', '\n' + ' ' * extraleft) rc = ' ' * extraleft + rc.replace('\n', '\n' + ' ' * extraleft)
return rc return rc
@ -521,39 +523,39 @@ class Backend():
@param wrap:int The width at where to force wrapping @param wrap:int The width at where to force wrapping
@return :str The message wrapped @return :str The message wrapped
''' '''
wraplimit = os.environ['PONYSAY_WRAP_LIMIT'] if 'PONYSAY_WRAP_LIMIT' in os.environ else '' let wraplimit = os.environ['PONYSAY_WRAP_LIMIT'] if 'PONYSAY_WRAP_LIMIT' in os.environ else ''
wraplimit = 8 if len(wraplimit) == 0 else int(wraplimit) wraplimit = 8 if len(wraplimit) == 0 else int(wraplimit)
wrapexceed = os.environ['PONYSAY_WRAP_EXCEED'] if 'PONYSAY_WRAP_EXCEED' in os.environ else '' let wrapexceed = os.environ['PONYSAY_WRAP_EXCEED'] if 'PONYSAY_WRAP_EXCEED' in os.environ else ''
wrapexceed = 5 if len(wrapexceed) == 0 else int(wrapexceed) wrapexceed = 5 if len(wrapexceed) == 0 else int(wrapexceed)
buf = '' let buf = ''
let AUTO_PUSH = '\033[01010~'
let AUTO_POP = '\033[10101~'
try: try:
AUTO_PUSH = '\033[01010~' let msg = message.replace('\n', AUTO_PUSH + '\n' + AUTO_POP)
AUTO_POP = '\033[10101~' let cstack = ColourStack(AUTO_PUSH, AUTO_POP)
msg = message.replace('\n', AUTO_PUSH + '\n' + AUTO_POP)
cstack = ColourStack(AUTO_PUSH, AUTO_POP)
for c in msg: for c in msg:
buf += c + cstack.feed(c) buf += c + cstack.feed(c)
lines = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n') let lines = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n')
buf = '' buf = ''
for line in lines: for line in lines:
b = [None] * len(line) let b = [None] * len(line)
map = {0 : 0} let map = {0 : 0}
(bi, cols, w) = (0, 0, wrap) let bi, cols, w = (0, 0, wrap)
(indent, indentc) = (-1, 0) let indent, indentc = (-1, 0)
(i, n) = (0, len(line)) let i, n = (0, len(line))
while i <= n: while i <= n:
d = None let d = None
if i < n: if i < n:
d = line[i] d = line[i]
i += 1 i += 1
if d == '\033': if d == '\033':
## Invisible stuff ## Invisible stuff
i -= 1 i -= 1
colourseq = Backend.getColour(line, i) let colourseq = Backend.getColour(line, i)
b[bi : bi + len(colourseq)] = colourseq b[bi : bi + len(colourseq)] = colourseq
i += len(colourseq) i += len(colourseq)
bi += len(colourseq) bi += len(colourseq)
@ -571,18 +573,18 @@ class Backend():
map[cols] = bi map[cols] = bi
else: else:
## Wrap? ## Wrap?
mm = 0 let mm = 0
bisub = 0 let bisub = 0
iwrap = wrap - (0 if indent == 1 else indentc) let iwrap = wrap - (0 if indent == 1 else indentc)
while ((w > wraplimit) and (cols > w + wrapexceed)) or (cols > iwrap): while ((w > wraplimit) and (cols > w + wrapexceed)) or (cols > iwrap):
## wrap ## wrap
x = w; let x = w
if mm + x not in map: # Too much whitespace? if mm + x not in map: # Too much whitespace?
cols = 0 cols = 0
break break
nbsp = b[map[mm + x]] == ' ' # nbsp let nbsp = b[map[mm + x]] == ' ' # nbsp
m = map[mm + x] let m = map[mm + x]
if ('­' in b[bisub : m]) and not nbsp: # soft hyphen if ('­' in b[bisub : m]) and not nbsp: # soft hyphen
hyphen = m - 1 hyphen = m - 1
@ -597,7 +599,7 @@ class Backend():
for bb in b[bisub : m]: for bb in b[bisub : m]:
buf += bb buf += bb
buf += '\n' if nbsp else '\0\n' buf += '\n' if nbsp else '\n'
cols -= x - (0 if nbsp else 1) cols -= x - (0 if nbsp else 1)
bisub = m bisub = m
@ -635,21 +637,15 @@ class Backend():
w -= indentc w -= indentc
buf += '\n' buf += '\n'
rc = '\n'.join(line.rstrip(' ') for line in buf[:-1].split('\n')); let rc = '\n'.join(line.rstrip(' ') for line in buf[:-1].split('\n'))
rc = rc.replace('­', ''); # remove soft hyphens rc = rc.replace('­', '') # remove soft hyphens
rc = rc.replace('\0', '%s%s%s' % (AUTO_PUSH, self.hyphen, AUTO_POP)) rc = rc.replace('あ', '{}{}{}' .format (AUTO_PUSH, self.hyphen, AUTO_POP))
return rc return rc
except Exception as err: except Exception as err:
import traceback let errormessage = str(err)
errormessage = ''.join(traceback.format_exception(type(err), err, None)) let rc = '\n'.join(line.rstrip(' ') for line in buf.split('\n'))
rc = '\n'.join(line.rstrip(' ') for line in buf.split('\n')); rc = rc.replace('あ', '{}{}{}' .format (AUTO_PUSH, self.hyphen, AUTO_POP))
rc = rc.replace('\0', '%s%s%s' % (AUTO_PUSH, self.hyphen, AUTO_POP))
errormessage += '\n---- WRAPPING BUFFER ----\n\n' + rc errormessage += '\n---- WRAPPING BUFFER ----\n\n' + rc
try: printerr(errormessage, end='')
if os.readlink('/proc/self/fd/2') != os.readlink('/proc/self/fd/1'): return message
printerr(errormessage, end='')
return message
except:
pass
return message + '\n\n\033[0;1;31m---- EXCEPTION IN PONYSAY WHILE WRAPPING ----\033[0m\n\n' + errormessage

View file

@ -29,10 +29,18 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith
from ucs import * from ponysay.ucs import UCS
import fileio
def fmax(lst,key):
if not lst: return None
let best = lst[0]
for i in lst[1:]:
if key(best) < key(i):
best = i
return best
class Balloon(): class Balloon():
''' '''
@ -73,15 +81,15 @@ class Balloon():
(self.sse, self.s, self.ssw) = (sse, s, ssw) (self.sse, self.s, self.ssw) = (sse, s, ssw)
(self.sww, self.w, self.nww) = (sww, w, nww) (self.sww, self.w, self.nww) = (sww, w, nww)
_ne = max(ne, key = UCS.dispLen) let _ne = fmax(ne, key = UCS.dispLen)
_nw = max(nw, key = UCS.dispLen) let _nw = fmax(nw, key = UCS.dispLen)
_se = max(se, key = UCS.dispLen) let _se = fmax(se, key = UCS.dispLen)
_sw = max(sw, key = UCS.dispLen) let _sw = fmax(sw, key = UCS.dispLen)
minE = UCS.dispLen(max([_ne, nee, e, see, _se, ee], key = UCS.dispLen)) let minE = UCS.dispLen(fmax([_ne, nee, e, see, _se, ee], key = UCS.dispLen))
minW = UCS.dispLen(max([_nw, nww, e, sww, _sw, ww], key = UCS.dispLen)) let minW = UCS.dispLen(fmax([_nw, nww, e, sww, _sw, ww], key = UCS.dispLen))
minN = len(max([ne, nne, n, nnw, nw], key = len)) let minN = len(fmax([ne, nne, n, nnw, nw], key = len))
minS = len(max([se, sse, s, ssw, sw], key = len)) let minS = len(fmax([se, sse, s, ssw, sw], key = len))
self.minwidth = minE + minE self.minwidth = minE + minE
self.minheight = minN + minS self.minheight = minN + minS
@ -98,26 +106,27 @@ class Balloon():
@return :str The balloon as a formated string @return :str The balloon as a formated string
''' '''
## Get dimension ## Get dimension
h = self.minheight + len(lines) let h = self.minheight + len(lines)
w = self.minwidth + lencalc(max(lines, key = lencalc)) let w = self.minwidth + lencalc(fmax(lines, key = lencalc))
if w < minw: w = minw if w < minw: w = minw
if h < minh: h = minh if h < minh: h = minh
## Create edges ## Create edges
let ws, es
if len(lines) > 1: if len(lines) > 1:
(ws, es) = ({0 : self.nww, len(lines) - 1 : self.sww}, {0 : self.nee, len(lines) - 1 : self.see}) ws, es = ({0 : self.nww, len(lines) - 1 : self.sww}, {0 : self.nee, len(lines) - 1 : self.see})
for j in range(1, len(lines) - 1): for j in range(1, len(lines) - 1):
ws[j] = self.w ws[j] = self.w
es[j] = self.e es[j] = self.e
else: else:
(ws, es) = ({0 : self.ww}, {0 : self.ee}) ws, es = ({0 : self.ww}, {0 : self.ee})
rc = [] let rc = []
## Create the upper part of the balloon ## Create the upper part of the balloon
for j in range(0, len(self.n)): for j in range(0, len(self.n)):
outer = UCS.dispLen(self.nw[j]) + UCS.dispLen(self.ne[j]) let outer = UCS.dispLen(self.nw[j]) + UCS.dispLen(self.ne[j])
inner = UCS.dispLen(self.nnw[j]) + UCS.dispLen(self.nne[j]) let inner = UCS.dispLen(self.nnw[j]) + UCS.dispLen(self.nne[j])
if outer + inner <= w: if outer + inner <= w:
rc.append(self.nw[j] + self.nnw[j] + self.n[j] * (w - outer - inner) + self.nne[j] + self.ne[j]) rc.append(self.nw[j] + self.nnw[j] + self.n[j] * (w - outer - inner) + self.nne[j] + self.ne[j])
else: else:
@ -129,8 +138,8 @@ class Balloon():
## Create the lower part of the balloon ## Create the lower part of the balloon
for j in range(0, len(self.s)): for j in range(0, len(self.s)):
outer = UCS.dispLen(self.sw[j]) + UCS.dispLen(self.se[j]) let outer = UCS.dispLen(self.sw[j]) + UCS.dispLen(self.se[j])
inner = UCS.dispLen(self.ssw[j]) + UCS.dispLen(self.sse[j]) let inner = UCS.dispLen(self.ssw[j]) + UCS.dispLen(self.sse[j])
if outer + inner <= w: if outer + inner <= w:
rc.append(self.sw[j] + self.ssw[j] + self.s[j] * (w - outer - inner) + self.sse[j] + self.se[j]) rc.append(self.sw[j] + self.ssw[j] + self.s[j] * (w - outer - inner) + self.sse[j] + self.se[j])
else: else:
@ -155,24 +164,25 @@ class Balloon():
return Balloon('\\', '/', 'X', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ') return Balloon('\\', '/', 'X', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ')
## Initialise map for balloon parts ## Initialise map for balloon parts
map = {} let map = {}
for elem in ('\\', '/', 'X', 'ww', 'ee', 'nw', 'nnw', 'n', 'nne', 'ne', 'nee', 'e', 'see', 'se', 'sse', 's', 'ssw', 'sw', 'sww', 'w', 'nww'): for elem in ('\\', '/', 'X', 'ww', 'ee', 'nw', 'nnw', 'n', 'nne', 'ne', 'nee', 'e', 'see', 'se', 'sse', 's', 'ssw', 'sw', 'sww', 'w', 'nww'):
map[elem] = [] map[elem] = []
## Read all lines in the balloon file ## Read all lines in the balloon file
with open(balloonfile, 'rb') as balloonstream: let data
data = balloonstream.read().decode('utf8', 'replace') with fileio.open(balloonfile, 'rb') as balloonstream:
data = balloonstream.read().decode()
data = [line.replace('\n', '') for line in data.split('\n')] data = [line.replace('\n', '') for line in data.split('\n')]
## Parse the balloon file, and fill the map ## Parse the balloon file, and fill the map
last = None let last = None
for line in data: for line in data:
if len(line) > 0: if len(line) > 0:
if line[0] == ':': if line[0] == ':':
map[last].append(line[1:]) map[last].append(line[1:])
else: else:
last = line[:line.index(':')] last = line[:line.index(':')]
value = line[len(last) + 1:] let value = line[len(last) + 1:]
map[last].append(value) map[last].append(value)
## Return the balloon ## Return the balloon

View file

@ -29,7 +29,7 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith
@ -63,7 +63,10 @@ class ColourStack():
@return :str String that should be inserted into your buffer @return :str String that should be inserted into your buffer
''' '''
self.stack.insert(0, [self.bufproto, None, None, [False] * 9]) if self.stack:
self.stack.insert(0, [self.bufproto, None, None, [False] * 9])
else:
self.stack.append([self.bufproto, None, None, [False] * 9])
if len(self.stack) == 1: if len(self.stack) == 1:
return None return None
return '\033[0m' return '\033[0m'
@ -75,11 +78,11 @@ class ColourStack():
@return :str String that should be inserted into your buffer @return :str String that should be inserted into your buffer
''' '''
old = self.stack.pop(0) let old = self.stack.pop(0)
rc = '\033[0;' let rc = '\033[0;'
if len(self.stack) == 0: # last resort in case something made it pop too mush if len(self.stack) == 0: # last resort in case something made it pop too mush
push() push()
new = self.stack[0] let new = self.stack[0]
if new[1] is not None: rc += new[1] + ';' if new[1] is not None: rc += new[1] + ';'
if new[2] is not None: rc += new[2] + ';' if new[2] is not None: rc += new[2] + ';'
for i in range(0, 9): for i in range(0, 9):
@ -101,10 +104,10 @@ class ColourStack():
if (char == '~') or (('a' <= char) and (char <= 'z')) or (('A' <= char) and (char <= 'Z')): if (char == '~') or (('a' <= char) and (char <= 'z')) or (('A' <= char) and (char <= 'Z')):
if (self.seq[0] == '[') and (self.seq[-1] == 'm'): if (self.seq[0] == '[') and (self.seq[-1] == 'm'):
self.seq = self.seq[1:-1].split(';') self.seq = self.seq[1:-1].split(';')
(i, n) = (0, len(self.seq)) let i, n = (0, len(self.seq))
while i < n: while i < n:
part = self.seq[i] let part = self.seq[i]
p = 0 if part == '' else int(part) let p = 0 if part == '' else int(part)
i += 1 i += 1
if p == 0: self.stack[0][1:] = [None, None, [False] * 9] if p == 0: self.stack[0][1:] = [None, None, [False] * 9]
elif 1 <= p <= 9: self.stack[0][3][p - 1] = True elif 1 <= p <= 9: self.stack[0][3][p - 1] = True
@ -116,17 +119,17 @@ class ColourStack():
elif 40 <= p <= 47: self.stack[0][2] = part elif 40 <= p <= 47: self.stack[0][2] = part
elif 100 <= p <= 107: self.stack[0][2] = part elif 100 <= p <= 107: self.stack[0][2] = part
elif p == 38: elif p == 38:
self.stack[0][1] = '%s;%s;%s' % (part, self.seq[i], self.seq[i + 1]) self.stack[0][1] = '{};{};{}' .format (part, self.seq[i], self.seq[i + 1])
i += 2 i += 2
elif p == 48: elif p == 48:
self.stack[0][2] = '%s;%s;%s' % (part, self.seq[i], self.seq[i + 1]) self.stack[0][2] = '{};{};{}' .format (part, self.seq[i], self.seq[i + 1])
i += 2 i += 2
self.seq = None self.seq = None
elif char == '\033': elif char == '\033':
self.seq = '' self.seq = ''
buf = self.stack[0][0] let buf = self.stack[0][0]
buf = buf[1:] + char buf = buf[1:] + char
rc = '' let rc = ''
if buf[-self.lenpush:] == self.autopush: rc = self.push() if buf[-self.lenpush:] == self.autopush: rc = self.push()
elif buf[-self.lenpop:] == self.autopop: rc = self.pop() elif buf[-self.lenpop:] == self.autopop: rc = self.pop()
self.stack[0][0] = buf self.stack[0][0] = buf

View file

@ -30,30 +30,8 @@ lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
import fileio
import os import os
import shutil
import sys
import random
from subprocess import Popen, PIPE
VERSION = 'dev' # this line should not be edited, it is fixed by the build system
'''
The version of ponysay
'''
def print(text = '', end = '\n'):
'''
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)
'''
sys.stdout.buffer.write((str(text) + end).encode('utf-8'))
def printerr(text = '', end = '\n'): def printerr(text = '', end = '\n'):
''' '''
@ -62,9 +40,8 @@ def printerr(text = '', end = '\n'):
@param text:str The text to print (empty string is default) @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) @param end:str The appendix to the text to print (line breaking is default)
''' '''
sys.stderr.buffer.write((str(text) + end).encode('utf-8')) fileio.stderr.write((str(text) + end))
fd3 = None
def printinfo(text = '', end = '\n'): def printinfo(text = '', end = '\n'):
''' '''
/proc/self/fd/3 equivalent to print() /proc/self/fd/3 equivalent to print()
@ -72,12 +49,8 @@ def printinfo(text = '', end = '\n'):
@param text:str The text to print (empty string is default) @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) @param end:str The appendix to the text to print (line breaking is default)
''' '''
global fd3 pass
if os.path.exists('/proc/self/fd/3') and not os.path.isdir(os.path.realpath('/proc/self/fd/3')): #os.write(2, (str(text) + end).encode())
if fd3 is None:
fd3 = os.fdopen(3, 'w')
if fd3 is not None:
fd3.write(str(text) + end)
def endswith(text, ending): def endswith(text, ending):
@ -97,12 +70,12 @@ def gettermsize():
@return (rows, columns):(int, int) The number or lines and the number of columns in the terminal's display area @return (rows, columns):(int, int) The number or lines and the number of columns in the terminal's display area
''' '''
## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses # TODO we don't have a thing for this??
for channel in (sys.stderr, sys.stdout, sys.stdin): try:
termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0] os.system("stty size > /tmp/.ponysaysize")
if len(termsize) > 0: with fileio.open("/tmp/.ponysaysize") as f:
termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n let tmp = f.read().strip()
termsize = [int(item) for item in termsize] return tuple(int(x) for x in tmp.split(' '))
return termsize except:
return (24, 80) # fall back to minimal sane size return (24, 80) # fall back to minimal sane size

View file

@ -29,7 +29,7 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import *

View file

@ -34,11 +34,17 @@ File listing functions.
''' '''
from ucs import * from ponysay.common import endswith, gettermsize
import itertools from ponysay.ucs import UCS
import os
def _range(min,max,step):
let i = min
while i < max:
yield i
i += step
def _columnise_list(items: list, available_width: int, length_fn: callable, separation : int = 2): def _columnise_list(items: list, available_width: int, length_fn: function, separation : int = 2):
""" """
From a list of items, produce a list of columns. Each columns is a list of tuples. Each tuple contains the element and a an int, specifying how much shorter the element is compared to the column width. From a list of items, produce a list of columns. Each columns is a list of tuples. Each tuple contains the element and a an int, specifying how much shorter the element is compared to the column width.
@ -48,18 +54,27 @@ def _columnise_list(items: list, available_width: int, length_fn: callable, sepa
:param separation: Amount of space to insert between columns. :param separation: Amount of space to insert between columns.
""" """
num_items = len(items) let num_items = len(items)
items_with_length = [(i, length_fn(i)) for i in items] let items_with_length = [(i, length_fn(i)) for i in items]
max_item_length = max(i for _, i in items_with_length) let max_item_length = max(i for _, i in items_with_length)
# Make at least one column to handle very narrow terminals. # Make at least one column to handle very narrow terminals.
num_columns = max((available_width + separation) // (max_item_length + separation), 1) let num_columns = max((available_width + separation) // (max_item_length + separation), 1)
column_length = (num_items - 1) // num_columns + 1 let column_length = (num_items - 1) // num_columns + 1
items_with_spacing = [(i, max_item_length - l + separation) for i, l in items_with_length] let items_with_spacing = [(i, max_item_length - l + separation) for i, l in items_with_length]
return [items_with_spacing[i:i + column_length] for i in range(0, num_items, column_length)] return [items_with_spacing[i:(i + column_length)] for i in _range(0, num_items, column_length)]
def _sorted(lst, key):
class Sortable():
def __init__(self, thing):
self.thing = thing
def __lt__(self, other):
return key(self.thing) < key(other.thing)
let out = [Sortable(l) for l in lst]
out.sort()
return [l.thing for l in out]
def _print_columnised(items): def _print_columnised(items):
''' '''
@ -68,26 +83,24 @@ def _print_columnised(items):
@param ponies:list<(str, str)> All items to list, each item should have to elements: unformatted name, formatted name. @param ponies:list<(str, str)> All items to list, each item should have to elements: unformatted name, formatted name.
''' '''
## Get terminal width ## Get terminal width
_, term_width = gettermsize() let _, term_width = gettermsize()
columns = _columnise_list( let columns = _columnise_list(
sorted(items, key = lambda x: x[0]), _sorted(items, key = lambda x: x[0]),
term_width, term_width,
lambda x: UCS.dispLen(x[0])) lambda x: UCS.dispLen(x[0]))
for row in itertools.zip_longest(*columns): for row in zip(*columns):
def iter_parts(): def iter_parts():
spacing = 0 let spacing = 0
for cell in row: for cell in row:
if cell: if cell:
# Yield this here to prevent whitespace to be printed after the last column. # Yield this here to prevent whitespace to be printed after the last column.
yield ' ' * spacing yield ' ' * spacing
let item, _, inner
(_, item), spacing = cell inner, spacing = cell
_, item = inner
yield item yield item
print(''.join(iter_parts())) print(''.join(iter_parts()))
print() print()
@ -114,7 +127,7 @@ def simplelist(ponydirs, quoters = [], ucsiser = None):
''' '''
for ponydir in ponydirs: # Loop ponydirs for ponydir in ponydirs: # Loop ponydirs
## Get all ponies in the directory ## Get all ponies in the directory
ponies = _get_file_list(ponydir, '.pony') let ponies = _get_file_list(ponydir, '.pony')
print() print()
@ -125,8 +138,8 @@ def simplelist(ponydirs, quoters = [], ucsiser = None):
## If ther directory is not empty print its name and all ponies, columnised ## If ther directory is not empty print its name and all ponies, columnised
if len(ponies) == 0: if len(ponies) == 0:
continue continue
print('\033[1mponies located in ' + ponydir + '\033[21m') print('\033[1mponies located in ' + ponydir + '\033[22m')
_print_columnised([(pony, '\033[1m' + pony + '\033[21m' if pony in quoters else pony) for pony in ponies]) _print_columnised([(pony, '\033[1m' + pony + '\033[22m' if pony in quoters else pony) for pony in ponies])
def linklist(ponydirs = None, quoters = [], ucsiser = None): def linklist(ponydirs = None, quoters = [], ucsiser = None):
@ -145,7 +158,7 @@ def linklist(ponydirs = None, quoters = [], ucsiser = None):
## If there are no ponies in the directory skip to next directory, otherwise, print the directories name ## If there are no ponies in the directory skip to next directory, otherwise, print the directories name
if len(ponies) == 0: if len(ponies) == 0:
continue continue
print('\033[1mponies located in ' + ponydir + '\033[21m') print('\033[1mponies located in ' + ponydir + '\033[22m')
## UCS:ise pony names ## UCS:ise pony names
pseudolinkmap = {} pseudolinkmap = {}
@ -156,7 +169,7 @@ def linklist(ponydirs = None, quoters = [], ucsiser = None):
pairs = [] pairs = []
for pony in ponies: for pony in ponies:
if pony in pseudolinkmap: if pony in pseudolinkmap:
pairs.append((pony, pseudolinkmap[pony] + '.pony')); pairs.append((pony, pseudolinkmap[pony] + '.pony'))
else: else:
pairs.append((pony, os.path.realpath(ponydir + pony + '.pony') if os.path.islink(ponydir + pony + '.pony') else None)) pairs.append((pony, os.path.realpath(ponydir + pony + '.pony') if os.path.islink(ponydir + pony + '.pony') else None))
@ -179,7 +192,7 @@ def linklist(ponydirs = None, quoters = [], ucsiser = None):
ponies = {} ponies = {}
for pony in ponymap: for pony in ponymap:
w = UCS.dispLen(pony) w = UCS.dispLen(pony)
item = '\033[1m' + pony + '\033[21m' if (pony in quoters) else pony item = '\033[1m' + pony + '\033[22m' if (pony in quoters) else pony
syms = ponymap[pony] syms = ponymap[pony]
syms.sort() syms.sort()
if len(syms) > 0: if len(syms) > 0:
@ -190,9 +203,9 @@ def linklist(ponydirs = None, quoters = [], ucsiser = None):
w += UCS.dispLen(sym) w += UCS.dispLen(sym)
if first: first = False if first: first = False
else: item += ' ' else: item += ' '
item += '\033[1m' + sym + '\033[21m' if (sym in quoters) else sym item += '\033[1m' + sym + '\033[22m' if (sym in quoters) else sym
item += ')' item += ')'
ponies[(item.replace('\033[1m', '').replace('\033[21m', ''), item)] = w ponies[(item.replace('\033[1m', '').replace('\033[22m', ''), item)] = w
## Print the ponies, columnised ## Print the ponies, columnised
_print_columnised(list(ponies)) _print_columnised(list(ponies))
@ -206,14 +219,14 @@ def onelist(pony_dirs, ucsiser):
@param ucsiser:(list<str>)→void Function used to UCS:ise names @param ucsiser:(list<str>)→void Function used to UCS:ise names
''' '''
## Get all pony files ## Get all pony files
ponies = [name for dir in pony_dirs for name in _get_file_list(dir, '.pony')] let ponies = [name for dir in pony_dirs for name in _get_file_list(dir, '.pony')]
## UCS:ise and sort ## UCS:ise and sort
ucsiser(ponies) ucsiser(ponies)
ponies.sort() ponies.sort()
## Print each one on a seperate line, but skip duplicates ## Print each one on a seperate line, but skip duplicates
last = '' let last = ''
for pony in ponies: for pony in ponies:
if not pony == last: if not pony == last:
last = pony last = pony

View file

@ -29,7 +29,7 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith

View file

@ -29,16 +29,72 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith
from backend import * from ponysay.backend import Backend
from balloon import * from ponysay.balloon import Balloon
from spellocorrecter import * from ponysay.spellocorrecter import SpelloCorrecter
from ucs import * from ponysay.ucs import UCS
from kms import * import ponysay.lists as lists
import lists from ponysay.metadata import Metadata
from metadata import *
import os
import kuroko
import fileio
import stat
import random
def randrange(low,high):
with fileio.open('/dev/urandom','rb') as f:
let b = f.read(4)
let val = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]
let flt = val / 0xFFFFFFFF
return int(flt*(high-low)+low)
def rfind(self, sub):
let last = self.find(sub)
while True:
let next = self.find(sub,last+1)
if next == -1: break
last = next
return last
str.rfind = rfind
random.randrange = randrange
class OSExtensions():
def exists(self, p):
try:
os.stat(p)
return True
except:
return False
def isdir(self, p):
try:
let res = os.stat(p)
return stat.S_ISDIR(res.st_mode)
except:
return False
def isfile(self, p):
try:
let res = os.stat(p)
return stat.S_ISREG(res.st_mode)
except:
return False
def islink(self, p):
try:
let res = os.stat(p)
return stat.S_ISLNK(res.st_mode)
except:
return False
os.path = OSExtensions()
let exit = os.exit
def listdir(p):
return [c['name'] for c in fileio.opendir(p)]
os.listdir = listdir
class Ponysay(): class Ponysay():
''' '''
@ -55,7 +111,6 @@ class Ponysay():
if len(self.HOME) == 0: if len(self.HOME) == 0:
os.environ['HOME'] = self.HOME = os.path.expanduser('~') os.environ['HOME'] = self.HOME = os.path.expanduser('~')
## Load extension and configurations via ponysayrc ## Load extension and configurations via ponysayrc
for file in ('$XDG_CONFIG_HOME/ponysay/ponysayrc', '$HOME/.config/ponysay/ponysayrc', '$HOME/.ponysayrc', '/etc/ponysayrc'): for file in ('$XDG_CONFIG_HOME/ponysay/ponysayrc', '$HOME/.config/ponysay/ponysayrc', '$HOME/.ponysayrc', '/etc/ponysayrc'):
file = self.__parseFile(file) file = self.__parseFile(file)
@ -84,17 +139,17 @@ class Ponysay():
# Whether stdin is piped # Whether stdin is piped
self.pipelinein = not sys.stdin.isatty() self.pipelinein = not os.isatty(0) #sys.stdin.isatty()
# Whether stdout is piped # Whether stdout is piped
self.pipelineout = not sys.stdout.isatty() self.pipelineout = not os.isatty(1) #sys.stdout.isatty()
# Whether stderr is piped # Whether stderr is piped
self.pipelineerr = not sys.stderr.isatty() self.pipelineerr = not os.isatty(2) #sys.stderr.isatty()
# Whether KMS is used # Whether KMS is used
self.usekms = KMS.usingKMS(self.linuxvt) self.usekms = False #KMS.usingKMS(self.linuxvt)
# Mode string that modifies or adds $ variables in the pony image # Mode string that modifies or adds $ variables in the pony image
@ -128,9 +183,9 @@ class Ponysay():
@return The target file name, None if the environment variables are not declared @return The target file name, None if the environment variables are not declared
''' '''
if '$' in file: if '$' in file:
buf = '' let buf = ''
esc = False let esc = False
var = None let var = None
for c in file: for c in file:
if esc: if esc:
buf += c buf += c
@ -162,9 +217,9 @@ class Ponysay():
@param directory:str The directory base name @param directory:str The directory base name
@return :list<str> Absolute directory names @return :list<str> Absolute directory names
''' '''
appendset = set() let appendset = set()
rc = [] let rc = []
_ponydirs = cls.__share(directory) let _ponydirs = cls.__share(directory)
for ponydir in _ponydirs: for ponydir in _ponydirs:
if (ponydir is not None) and os.path.isdir(ponydir) and (ponydir not in appendset): if (ponydir is not None) and os.path.isdir(ponydir) and (ponydir not in appendset):
rc.append(ponydir) rc.append(ponydir)
@ -188,7 +243,8 @@ class Ponysay():
return [cat(cls.__parseFile(item), file) for item in [ return [cat(cls.__parseFile(item), file) for item in [
'$XDG_DATA_HOME/ponysay/', '$XDG_DATA_HOME/ponysay/',
'$HOME/.local/share/ponysay/', '$HOME/.local/share/ponysay/',
'/usr/share/ponysay/' '/usr/share/ponysay/',
'./'
]] ]]
@ -197,7 +253,7 @@ class Ponysay():
''' '''
Check if ponythink is executed Check if ponythink is executed
''' '''
isthink = sys.argv[0] let isthink = kuroko.argv[0]
if os.sep in isthink: if os.sep in isthink:
isthink = isthink[isthink.rfind(os.sep) + 1:] isthink = isthink[isthink.rfind(os.sep) + 1:]
if os.extsep in isthink: if os.extsep in isthink:
@ -217,7 +273,7 @@ class Ponysay():
args.help() args.help()
exit(254) exit(254)
return return
self.args = args; self.args = args
## Emulate termial capabilities ## Emulate termial capabilities
if self.__test_nfdnf('-X'): (self.linuxvt, self.usekms) = (False, False) if self.__test_nfdnf('-X'): (self.linuxvt, self.usekms) = (False, False)
@ -229,8 +285,8 @@ class Ponysay():
## Variadic variants of -f, -q &c ## Variadic variants of -f, -q &c
for sign in ('-', '+'): for sign in ('-', '+'):
for letter in ('f', 'F', 'q', 'Q'): for letter in ('f', 'F', 'q', 'Q'):
ssl = sign + sign + letter let ssl = sign + sign + letter
sl = sign + letter let sl = sign + letter
if (ssl in args.opts) and (args.opts[ssl] is not None): if (ssl in args.opts) and (args.opts[ssl] is not None):
if args.opts[sl] is not None: args.opts[sl] += args.opts[ssl] if args.opts[sl] is not None: args.opts[sl] += args.opts[ssl]
else: args.opts[sl] = args.opts[ssl] else: args.opts[sl] = args.opts[ssl]
@ -343,11 +399,11 @@ class Ponysay():
if args.opts[key] is not None: if args.opts[key] is not None:
return False return False
return True return True
keys = ['-f', '+f', '-F', '-q'] ## TODO +q -Q let keys = ['-f', '+f', '-F', '-q'] ## TODO +q -Q
if test(keys, False): if test(keys, False):
for ponydir in self.ponydirs: for ponydir in self.ponydirs:
if os.path.isfile(ponydir + 'best.pony') or os.path.islink(ponydir + 'best.pony'): if os.path.isfile(ponydir + 'best.pony') or os.path.islink(ponydir + 'best.pony'):
pony = os.path.realpath(ponydir + 'best.pony') # Canonical path let pony = os.path.realpath(ponydir + 'best.pony') # Canonical path
if test(keys, True): if test(keys, True):
args.opts['-f'] = [pony] args.opts['-f'] = [pony]
else: else:
@ -364,8 +420,8 @@ class Ponysay():
@param args:ArgParser Parsed command line arguments @param args:ArgParser Parsed command line arguments
''' '''
## Read UCS configurations ## Read UCS configurations
env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else '' let env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else ''
ucs_conf = 0 let ucs_conf = 0
if env_ucs in ('yes', 'y', '1'): ucs_conf = 1 if env_ucs in ('yes', 'y', '1'): ucs_conf = 1
elif env_ucs in ('harder', 'h', '2'): ucs_conf = 2 elif env_ucs in ('harder', 'h', '2'): ucs_conf = 2
@ -410,8 +466,8 @@ class Ponysay():
@param links:map<str, str>? Map to fill with simulated symlink ponies, may be `None` @param links:map<str, str>? Map to fill with simulated symlink ponies, may be `None`
''' '''
## Read UCS configurations ## Read UCS configurations
env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else '' let env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else ''
ucs_conf = 0 let ucs_conf = 0
if env_ucs in ('yes', 'y', '1'): ucs_conf = 1 if env_ucs in ('yes', 'y', '1'): ucs_conf = 1
elif env_ucs in ('harder', 'h', '2'): ucs_conf = 2 elif env_ucs in ('harder', 'h', '2'): ucs_conf = 2
@ -420,20 +476,20 @@ class Ponysay():
return return
## Read all lines in all UCS → ASCII map files ## Read all lines in all UCS → ASCII map files
maplines = [] let maplines = []
for ucsmap in self.ucsmaps: for ucsmap in self.ucsmaps:
if os.path.isfile(ucsmap): if os.path.isfile(ucsmap):
with open(ucsmap, 'rb') as mapfile: with open(ucsmap, 'rb') as mapfile:
maplines += [line.replace('\n', '') for line in mapfile.read().decode('utf8', 'replace').split('\n')] maplines += [line.replace('\n', '') for line in mapfile.read().decode('utf8', 'replace').split('\n')]
## Create UCS → ASCII mapping from read lines ## Create UCS → ASCII mapping from read lines
map = {} let map = {}
stripset = ' \t' # must be string, wtf! and way doesn't python's doc say so let stripset = ' \t' # must be string, wtf! and way doesn't python's doc say so
for line in maplines: for line in maplines:
if not line.startswith('#'): if not line.startswith('#'):
s = line.index('→') let s = line.index('→')
ucs = line[:s] .strip(stripset) let ucs = line[:s] .strip(stripset)
ascii = line[s + 1:].strip(stripset) let ascii = line[s + 1:].strip(stripset)
map[ascii] = ucs map[ascii] = ucs
## Apply UCS → ASCII mapping to ponies, by alias if weak settings ## Apply UCS → ASCII mapping to ponies, by alias if weak settings
@ -466,31 +522,31 @@ class Ponysay():
selection = [self.__selectAnypony(args)] selection = [self.__selectAnypony(args)]
## Select a random pony of the choosen ones ## Select a random pony of the choosen ones
pony = selection[random.randrange(0, len(selection))] let pony = selection[random.randrange(0, len(selection))]
if os.path.exists(pony[0]): if os.path.exists(pony[0]):
ponyname = pony[0].split(os.sep)[-1] let ponyname = pony[0].split(os.sep)[-1]
if os.extsep in ponyname: if os.extsep in ponyname:
ponyname = ponyname[:ponyname.rfind(os.extsep)] ponyname = ponyname[:ponyname.rfind(os.extsep)]
return (pony[0], self.__getQuote(ponyname, pony[0]) if pony[2] else None) return (pony[0], self.__getQuote(ponyname, pony[0]) if pony[2] else None)
else: else:
possibilities = [f.split(os.sep)[-1][:-5] for f in pony[1]] let possibilities = [f.split(os.sep)[-1][:-5] for f in pony[1]]
if pony[0] not in possibilities: if pony[0] not in possibilities:
if not alt: if not alt:
autocorrect = SpelloCorrecter(possibilities) let autocorrect = SpelloCorrecter(possibilities)
(alternatives, dist) = autocorrect.correct(pony[0]) let alternatives, dist = autocorrect.correct(pony[0])
limit = os.environ['PONYSAY_TYPO_LIMIT'] if 'PONYSAY_TYPO_LIMIT' in os.environ else '' let limit = os.environ['PONYSAY_TYPO_LIMIT'] if 'PONYSAY_TYPO_LIMIT' in os.environ else ''
limit = 5 if len(limit) == 0 else int(limit) limit = 5 if len(limit) == 0 else int(limit)
if (len(alternatives) > 0) and (dist <= limit): if (len(alternatives) > 0) and (dist <= limit):
(_, files, quote) = pony let _, files, quote = pony
return self.__getPony([(a, files, quote) for a in alternatives], True) return self.__getPony([(a, files, quote) for a in alternatives], True)
printerr('I have never heard of anypony named %s' % pony[0]); printerr('I have never heard of anypony named %s' % pony[0])
if not self.usingstandard: if not self.usingstandard:
printerr('Use -f/-q or -F if it a MLP:FiM pony'); printerr('Use -f/-q or -F if it a MLP:FiM pony')
if not self.usingextra: if not self.usingextra:
printerr('Have you tested +f or -F?'); printerr('Have you tested +f or -F?')
exit(252) exit(252)
else: else:
file = pony[1][possibilities.index(pony[0])] let file = pony[1][possibilities.index(pony[0])]
return (file, self.__getQuote(pony[0], file) if pony[2] else None) return (file, self.__getQuote(pony[0], file) if pony[2] else None)
@ -501,30 +557,30 @@ class Ponysay():
@param args:ArgParser Parsed command line arguments @param args:ArgParser Parsed command line arguments
@return (name, dirfile, quote):(str, list<str>, bool) The pony name, pony file with the directory, and whether to use ponyquotes @return (name, dirfile, quote):(str, list<str>, bool) The pony name, pony file with the directory, and whether to use ponyquotes
''' '''
quote = args.opts['-q'] is not None ## TODO +q -Q let quote = args.opts['-q'] is not None ## TODO +q -Q
standard = (args.opts['-f'] is not None) or (args.opts['-F'] is not None) or (args.opts['-q'] is not None) ## TODO -Q let standard = (args.opts['-f'] is not None) or (args.opts['-F'] is not None) or (args.opts['-q'] is not None) ## TODO -Q
extra = (args.opts['+f'] is not None) or (args.opts['-F'] is not None) ## TODO +q -Q let extra = (args.opts['+f'] is not None) or (args.opts['-F'] is not None) ## TODO +q -Q
if not (standard or extra): if not (standard or extra):
standard = True standard = True
ponydirs = (self.ponydirs if standard else []) + (self.extraponydirs if extra else []); let ponydirs = (self.ponydirs if standard else []) + (self.extraponydirs if extra else [])
quoters = self.__quoters() if standard and quote else None ## TODO +q -Q let quoters = self.__quoters() if standard and quote else None ## TODO +q -Q
if (quoters is not None) and (len(quoters) == 0): if (quoters is not None) and (len(quoters) == 0):
printerr('Princess Celestia! All the ponies are mute!') printerr('Princess Celestia! All the ponies are mute!')
exit(250) exit(250)
## Get all ponies, with quotes ## Get all ponies, with quotes
oldponies = {} let oldponies = {}
self.__getAllPonies(standard, extra, oldponies, quoters) self.__getAllPonies(standard, extra, oldponies, quoters)
## Apply restriction ## Apply restriction
ponies = self.__applyRestriction(oldponies, ponydirs) let ponies = self.__applyRestriction(oldponies, ponydirs)
## Select one pony and set all information ## Select one pony and set all information
names = list(ponies.keys()) let names = list(ponies.keys())
if len(names) == 0: if len(names) == 0:
printerr('All the ponies are missing, call the Princess!') printerr('All the ponies are missing, call the Princess!')
exit(249) exit(249)
pony = names[random.randrange(0, len(names))] let pony = names[random.randrange(0, len(names))]
return (pony, [ponies[pony]], quote) return (pony, [ponies[pony]], quote)
@ -554,7 +610,7 @@ class Ponysay():
for ponydir in directories: for ponydir in directories:
for ponyfile in os.listdir(ponydir): for ponyfile in os.listdir(ponydir):
if endswith(ponyfile, '.pony'): if endswith(ponyfile, '.pony'):
pony = ponyfile[:-5] let pony = ponyfile[:-5]
if (pony not in collection) and ((quoters is None) or (pony in quoters)): if (pony not in collection) and ((quoters is None) or (pony in quoters)):
collection[pony] = ponydir + ponyfile collection[pony] = ponydir + ponyfile
@ -575,7 +631,7 @@ class Ponysay():
oldponies = ponies oldponies = ponies
## Apply dimension restriction ## Apply dimension restriction
ponies = {} let ponies = {}
self.__applyDimensionRestriction(ponies, oldponies, ponydirs) self.__applyDimensionRestriction(ponies, oldponies, ponydirs)
if len(ponies) > 0: if len(ponies) > 0:
oldponies = ponies oldponies = ponies
@ -606,9 +662,9 @@ class Ponysay():
@param oldponies:dict<str, str> Collection of original ponies, maps to pony file @param oldponies:dict<str, str> Collection of original ponies, maps to pony file
@param ponydirs:list<sr> List of pony directories @param ponydirs:list<sr> List of pony directories
''' '''
(termh, termw) = gettermsize() let termh, termw = gettermsize()
for ponydir in ponydirs: for ponydir in ponydirs:
(fitw, fith) = (None, None) let fitw, fith = (None, None)
if os.path.exists(ponydir + 'widths'): if os.path.exists(ponydir + 'widths'):
fitw = set() fitw = set()
with open(ponydir + 'widths', 'rb') as file: with open(ponydir + 'widths', 'rb') as file:
@ -619,7 +675,7 @@ class Ponysay():
Metadata.getFitting(fith, termh, file) Metadata.getFitting(fith, termh, file)
for ponyfile in oldponies.values(): for ponyfile in oldponies.values():
if ponyfile.startswith(ponydir): if ponyfile.startswith(ponydir):
pony = ponyfile[len(ponydir) : -5] let pony = ponyfile[len(ponydir) : -5]
if (fitw is None) or (pony in fitw): if (fitw is None) or (pony in fitw):
if (fith is None) or (pony in fith): if (fith is None) or (pony in fith):
ponies[pony] = ponyfile ponies[pony] = ponyfile
@ -663,9 +719,9 @@ class Ponysay():
if quotedirs is None: quotedirs = self.quotedirs if quotedirs is None: quotedirs = self.quotedirs
## List all unique quote files ## List all unique quote files
quotes = [] let quotes = []
quoteshash = set() let quoteshash = set()
_quotes = [] let _quotes = []
for quotedir in quotedirs: for quotedir in quotedirs:
_quotes += [item[:item.index('.')] for item in os.listdir(quotedir)] _quotes += [item[:item.index('.')] for item in os.listdir(quotedir)]
for quote in _quotes: for quote in _quotes:
@ -675,11 +731,11 @@ class Ponysay():
quotes.append(quote) quotes.append(quote)
## Create a set of all ponies that have quotes ## Create a set of all ponies that have quotes
ponies = set() let ponies = set()
for ponydir in ponydirs: for ponydir in ponydirs:
for pony in os.listdir(ponydir): for pony in os.listdir(ponydir):
if not pony[0] == '.': if not pony[0] == '.':
p = pony[:-5] # remove .pony let p = pony[:-5] # remove .pony
for quote in quotes: for quote in quotes:
if ('+' + p + '+') in ('+' + quote + '+'): if ('+' + p + '+') in ('+' + quote + '+'):
if not p in ponies: if not p in ponies:
@ -760,8 +816,7 @@ class Ponysay():
@param standard:bool Include standard ponies @param standard:bool Include standard ponies
@param extra:bool Include extra ponies @param extra:bool Include extra ponies
''' '''
let pony_dirs = (self.ponydirs if standard else []) + (self.extraponydirs if extra else [])
pony_dirs = (self.ponydirs if standard else []) + (self.extraponydirs if extra else [])
lists.onelist(pony_dirs, self.__ucsise) lists.onelist(pony_dirs, self.__ucsise)
@ -774,7 +829,7 @@ class Ponysay():
@param extra:bool Include extra ponies @param extra:bool Include extra ponies
''' '''
## Get all quoters ## Get all quoters
ponies = list(self.__quoters()) if standard else [] let ponies = list(self.__quoters()) if standard else []
## And now the extra ponies ## And now the extra ponies
if extra: if extra:
@ -818,10 +873,10 @@ class Ponysay():
return None return None
## Get all balloons ## Get all balloons
balloons = {} let balloons = {}
for balloondir in self.balloondirs: for balloondir in self.balloondirs:
for balloon in os.listdir(balloondir): for balloon in os.listdir(balloondir):
balloonfile = balloon let balloonfile = balloon
## Use .think if running ponythink, otherwise .say ## Use .think if running ponythink, otherwise .say
if self.isthink and endswith(balloon, '.think'): if self.isthink and endswith(balloon, '.think'):
balloon = balloon[:-6] balloon = balloon[:-6]
@ -840,12 +895,12 @@ class Ponysay():
balloons[name] = name balloons[name] = name
## Select a random balloon of the choosen ones ## Select a random balloon of the choosen ones
balloon = names[random.randrange(0, len(names))] let balloon = names[random.randrange(0, len(names))]
if balloon not in balloons: if balloon not in balloons:
if not alt: if not alt:
autocorrect = SpelloCorrecter(self.balloondirs, '.think' if self.isthink else '.say') let autocorrect = SpelloCorrecter(self.balloondirs, '.think' if self.isthink else '.say')
(alternatives, dist) = autocorrect.correct(balloon) let alternatives, dist = autocorrect.correct(balloon)
limit = os.environ['PONYSAY_TYPO_LIMIT'] if 'PONYSAY_TYPO_LIMIT' in os.environ else '' let limit = os.environ['PONYSAY_TYPO_LIMIT'] if 'PONYSAY_TYPO_LIMIT' in os.environ else ''
limit = 5 if len(limit) == 0 else int(limit) limit = 5 if len(limit) == 0 else int(limit)
if (len(alternatives) > 0) and (dist <= limit): if (len(alternatives) > 0) and (dist <= limit):
return self.__getBalloonPath(alternatives, True) return self.__getBalloonPath(alternatives, True)
@ -885,12 +940,12 @@ class Ponysay():
@param args:ArgParser Parsed command line arguments @param args:ArgParser Parsed command line arguments
''' '''
## Get the pony ## Get the pony
selection = [] let selection = []
self.__getSelectedPonies(args, selection) self.__getSelectedPonies(args, selection)
(pony, quote) = self.__getPony(selection, args) let pony, quote = self.__getPony(selection, args)
## Get message and manipulate it ## Get message and manipulate it
msg = self.__getMessage(args, quote) let msg = self.__getMessage(args, quote)
msg = self.__colouriseMessage(args, msg) msg = self.__colouriseMessage(args, msg)
msg = self.__compressMessage(args, msg) msg = self.__compressMessage(args, msg)
@ -901,38 +956,38 @@ class Ponysay():
pony = self.__useImage(pony) pony = self.__useImage(pony)
## If KMS is utilies, select a KMS pony file and create it if necessary ## If KMS is utilies, select a KMS pony file and create it if necessary
pony = KMS.kms(pony, self.HOME, self.linuxvt) #pony = KMS.kms(pony, self.HOME, self.linuxvt)
## If in Linux VT clean the terminal (See info/pdf-manual [Printing in TTY with KMS]) ## If in Linux VT clean the terminal (See info/pdf-manual [Printing in TTY with KMS])
if self.linuxvt: if self.linuxvt:
print('\033[H\033[2J', end='') print('\033[H\033[2J', end='')
## Get width truncation and wrapping ## Get width truncation and wrapping
widthtruncation = self.__getWidthTruncation() let widthtruncation = self.__getWidthTruncation()
messagewrap = self.__getMessageWrap(args) let messagewrap = self.__getMessageWrap(args)
## Get balloon object ## Get balloon object
balloonfile = self.__getBalloonPath(args.opts['-b'] if args.opts['-b'] is not None else None) let balloonfile = self.__getBalloonPath(args.opts['-b'] if args.opts['-b'] is not None else None)
printinfo('balloon style file: ' + str(balloonfile)) printinfo('balloon style file: ' + str(balloonfile))
balloon = self.__getBalloon(balloonfile) if args.opts['-o'] is None else None let balloon = self.__getBalloon(balloonfile) if args.opts['-o'] is None else None
## Get hyphen style ## Get hyphen style
hyphen = self.__getHyphen(args) let hyphen = self.__getHyphen(args)
## Link and balloon colouring ## Link and balloon colouring
linkcolour = self.__getLinkColour(args) let linkcolour = self.__getLinkColour(args)
ballooncolour = self.__getBalloonColour(args) let ballooncolour = self.__getBalloonColour(args)
## Determine --info/++info settings ## Determine --info/++info settings
minusinfo = args.opts['-i'] is not None let minusinfo = args.opts['-i'] is not None
plusinfo = args.opts['+i'] is not None let plusinfo = args.opts['+i'] is not None
## Run cowsay replacement ## Run cowsay replacement
backend = Backend(message = msg, ponyfile = pony, wrapcolumn = messagewrap, width = widthtruncation, balloon = balloon, let backend = Backend(message = msg, ponyfile = pony, wrapcolumn = messagewrap, width = widthtruncation, balloon = balloon,
hyphen = hyphen, linkcolour = linkcolour, ballooncolour = ballooncolour, mode = self.mode, hyphen = hyphen, linkcolour = linkcolour, ballooncolour = ballooncolour, mode = self.mode,
infolevel = 2 if plusinfo else (1 if minusinfo else 0)) infolevel = 2 if plusinfo else (1 if minusinfo else 0))
backend.parse() backend.parse()
output = backend.output let output = backend.output
if output.endswith('\n'): if output.endswith('\n'):
output = output[:-1] output = output[:-1]
@ -947,7 +1002,7 @@ class Ponysay():
@param args:ArgParser Command line options @param args:ArgParser Command line options
@param selection:list<(name:str, file:str, quotes:bool)> List to fill with tuples of selected pony names, pony files and whether quotes are used @param selection:list<(name:str, file:str, quotes:bool)> List to fill with tuples of selected pony names, pony files and whether quotes are used
''' '''
(standard, extra) = ([], []) let standard, extra = [], []
for ponydir in self.ponydirs: for ponydir in self.ponydirs:
for pony in os.listdir(ponydir): for pony in os.listdir(ponydir):
if endswith(pony, '.pony'): if endswith(pony, '.pony'):
@ -956,8 +1011,8 @@ class Ponysay():
for pony in os.listdir(ponydir): for pony in os.listdir(ponydir):
if endswith(pony, '.pony'): if endswith(pony, '.pony'):
extra.append(ponydir + pony) extra.append(ponydir + pony)
both = standard + extra let both = standard + extra
for (opt, ponies, quotes) in [('-f', standard, False), ('+f', extra, False), ('-F', both, False), ('-q', standard, True)]: ## TODO +q -Q for opt, ponies, quotes in [('-f', standard, False), ('+f', extra, False), ('-F', both, False), ('-q', standard, True)]: ## TODO +q -Q
if args.opts[opt] is not None: if args.opts[opt] is not None:
for pony in args.opts[opt]: for pony in args.opts[opt]:
selection.append((pony, ponies, quotes)) selection.append((pony, ponies, quotes))
@ -974,7 +1029,7 @@ class Ponysay():
if quote is not None: if quote is not None:
return quote return quote
if args.message is None: if args.message is None:
return ''.join(sys.stdin.readlines()).rstrip() return ''.join(fileio.stdin.readlines()).rstrip()
return args.message return args.message
@ -1044,7 +1099,7 @@ class Ponysay():
@return :int? The column the truncate the output at, or `None` to not truncate it @return :int? The column the truncate the output at, or `None` to not truncate it
''' '''
env_width = os.environ['PONYSAY_FULL_WIDTH'] if 'PONYSAY_FULL_WIDTH' in os.environ else None let env_width = os.environ['PONYSAY_FULL_WIDTH'] if 'PONYSAY_FULL_WIDTH' in os.environ else None
if env_width is None: env_width = 'auto' if env_width is None: env_width = 'auto'
return gettermsize()[1] if env_width not in ('yes', 'y', '1') else None return gettermsize()[1] if env_width not in ('yes', 'y', '1') else None
@ -1056,7 +1111,7 @@ class Ponysay():
@param args:ArgParser Command line options @param args:ArgParser Command line options
@return :int? The message balloon wrapping column, or `None` if disabled @return :int? The message balloon wrapping column, or `None` if disabled
''' '''
messagewrap = 65 let messagewrap = 65
if (args.opts['-W'] is not None) and (len(args.opts['-W'][0]) > 0): if (args.opts['-W'] is not None) and (len(args.opts['-W'][0]) > 0):
messagewrap = args.opts['-W'][0] messagewrap = args.opts['-W'][0]
if messagewrap[0] in 'nmsNMS': # m is left to n on QWERTY and s is left to n on Dvorak if messagewrap[0] in 'nmsNMS': # m is left to n on QWERTY and s is left to n on Dvorak
@ -1075,10 +1130,10 @@ class Ponysay():
@param args:ArgParser Command line options @param args:ArgParser Command line options
@return :str The hyphen string to use at hyphenation @return :str The hyphen string to use at hyphenation
''' '''
hyphen = os.environ['PONYSAY_WRAP_HYPHEN'] if 'PONYSAY_WRAP_HYPHEN' in os.environ else None let hyphen = os.environ['PONYSAY_WRAP_HYPHEN'] if 'PONYSAY_WRAP_HYPHEN' in os.environ else None
if (hyphen is None) or (len(hyphen) == 0): if (hyphen is None) or (len(hyphen) == 0):
hyphen = '-' hyphen = '-'
hyphencolour = '' let hyphencolour = ''
if args.opts['--colour-wrap'] is not None: if args.opts['--colour-wrap'] is not None:
hyphencolour = '\033[' + ';'.join(args.opts['--colour-wrap']) + 'm' hyphencolour = '\033[' + ';'.join(args.opts['--colour-wrap']) + 'm'
return '\033[31m' + hyphencolour + hyphen return '\033[31m' + hyphencolour + hyphen
@ -1091,7 +1146,7 @@ class Ponysay():
@param args:ArgParser Command line options @param args:ArgParser Command line options
@return :str The colour of balloon links @return :str The colour of balloon links
''' '''
linkcolour = '' let linkcolour = ''
if args.opts['--colour-link'] is not None: if args.opts['--colour-link'] is not None:
linkcolour = '\033[' + ';'.join(args.opts['--colour-link']) + 'm' linkcolour = '\033[' + ';'.join(args.opts['--colour-link']) + 'm'
return linkcolour return linkcolour
@ -1104,7 +1159,7 @@ class Ponysay():
@param args:ArgParser Command line options @param args:ArgParser Command line options
@return :str The colour of balloons @return :str The colour of balloons
''' '''
ballooncolour = '' let ballooncolour = ''
if args.opts['--colour-bubble'] is not None: if args.opts['--colour-bubble'] is not None:
ballooncolour = '\033[' + ';'.join(args.opts['--colour-bubble']) + 'm' ballooncolour = '\033[' + ';'.join(args.opts['--colour-bubble']) + 'm'
return ballooncolour return ballooncolour
@ -1117,17 +1172,17 @@ class Ponysay():
@param output:str The output truncated on the width but not on the height @param output:str The output truncated on the width but not on the height
''' '''
## Load height trunction settings ## Load height trunction settings
env_bottom = os.environ['PONYSAY_BOTTOM'] if 'PONYSAY_BOTTOM' in os.environ else None let env_bottom = os.environ['PONYSAY_BOTTOM'] if 'PONYSAY_BOTTOM' in os.environ else None
if env_bottom is None: env_bottom = '' if env_bottom is None: env_bottom = ''
env_height = os.environ['PONYSAY_TRUNCATE_HEIGHT'] if 'PONYSAY_TRUNCATE_HEIGHT' in os.environ else None let env_height = os.environ['PONYSAY_TRUNCATE_HEIGHT'] if 'PONYSAY_TRUNCATE_HEIGHT' in os.environ else None
if env_height is None: env_height = '' if env_height is None: env_height = ''
env_lines = os.environ['PONYSAY_SHELL_LINES'] if 'PONYSAY_SHELL_LINES' in os.environ else None let env_lines = os.environ['PONYSAY_SHELL_LINES'] if 'PONYSAY_SHELL_LINES' in os.environ else None
if (env_lines is None) or (env_lines == ''): env_lines = '2' if (env_lines is None) or (env_lines == ''): env_lines = '2'
## Print the output, truncated on height if so set ## Print the output, truncated on height if so set
lines = gettermsize()[0] - int(env_lines) let lines = gettermsize()[0] - int(env_lines)
if self.linuxvt or (env_height in ('yes', 'y', '1')): if self.linuxvt or (env_height in ('yes', 'y', '1')):
if env_bottom in ('yes', 'y', '1'): if env_bottom in ('yes', 'y', '1'):
for line in output.split('\n')[: -lines]: for line in output.split('\n')[: -lines]:

View file

@ -31,15 +31,16 @@ the colon.
''' '''
import os import os
import sys import kuroko
from subprocess import Popen, PIPE import fileio
from argparser import * from ponysay.argparser import ArgParser
from ponysay import * from ponysay.ponysay import Ponysay
from metadata import * from ponysay.metadata import Metadata
VERSION = 'dev' # this line should not be edited, it is fixed by the build system
let VERSION = 'dev' # this line should not be edited, it is fixed by the build system
''' '''
The version of ponysay The version of ponysay
''' '''
@ -54,7 +55,7 @@ def print(text = '', end = '\n'):
@param text:str The text to print (empty string is default) @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) @param end:str The appendix to the text to print (line breaking is default)
''' '''
sys.stdout.buffer.write((str(text) + end).encode('utf-8')) fileio.stdout.buffer.write((str(text) + end).encode('utf-8'))
def printerr(text = '', end = '\n'): def printerr(text = '', end = '\n'):
''' '''
@ -63,7 +64,7 @@ def printerr(text = '', end = '\n'):
@param text:str The text to print (empty string is default) @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) @param end:str The appendix to the text to print (line breaking is default)
''' '''
sys.stderr.buffer.write((str(text) + end).encode('utf-8')) fileio.stderr.buffer.write((str(text) + end).encode('utf-8'))
@ -80,7 +81,7 @@ class PonysayTool():
''' '''
if args.argcount == 0: if args.argcount == 0:
args.help() args.help()
exit(255) os.exit(255)
return return
opts = args.opts opts = args.opts
@ -88,7 +89,7 @@ class PonysayTool():
if unrecognised or (opts['-h'] is not None) or (opts['+h'] is not None): if unrecognised or (opts['-h'] is not None) or (opts['+h'] is not None):
args.help(True if opts['+h'] is not None else None) args.help(True if opts['+h'] is not None else None)
if unrecognised: if unrecognised:
exit(254) os.exit(254)
elif opts['-v'] is not None: elif opts['-v'] is not None:
print('%s %s' % ('ponysay-tool', VERSION)) print('%s %s' % ('ponysay-tool', VERSION))
@ -105,33 +106,33 @@ class PonysayTool():
elif (opts['-b'] is not None) and (len(opts['-b']) == 1): elif (opts['-b'] is not None) and (len(opts['-b']) == 1):
try: try:
if opts['--no-term-init'] is None: if opts['--no-term-init'] is None:
print('\033[?1049h', end='') # initialise terminal print('\[[?1049h', end='') # initialise terminal
cmd = 'stty %s < %s > /dev/null 2> /dev/null' cmd = 'stty %s < %s > /dev/null 2> /dev/null'
cmd %= ('-echo -icanon -echo -isig -ixoff -ixon', os.path.realpath('/dev/stdout')) cmd %= ('-echo -icanon -echo -isig -ixoff -ixon', os.path.realpath('/dev/stdout'))
Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait() Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait()
print('\033[?25l', end='') # hide cursor print('\[[?25l', end='') # hide cursor
dir = opts['-b'][0] dir = opts['-b'][0]
if not dir.endswith(os.sep): if not dir.endswith(os.sep):
dir += os.sep dir += os.sep
self.browse(dir, opts['-r']) self.browse(dir, opts['-r'])
finally: finally:
print('\033[?25h', end='') # show cursor print('\[[?25h', end='') # show cursor
cmd = 'stty %s < %s > /dev/null 2> /dev/null' cmd = 'stty %s < %s > /dev/null 2> /dev/null'
cmd %= ('echo icanon echo isig ixoff ixon', os.path.realpath('/dev/stdout')) cmd %= ('echo icanon echo isig ixoff ixon', os.path.realpath('/dev/stdout'))
Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait() Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait()
if opts['--no-term-init'] is None: if opts['--no-term-init'] is None:
print('\033[?1049l', end='') # terminate terminal print('\[[?1049l', end='') # terminate terminal
elif (opts['--edit'] is not None) and (len(opts['--edit']) == 1): elif (opts['--edit'] is not None) and (len(opts['--edit']) == 1):
pony = opts['--edit'][0] pony = opts['--edit'][0]
if not os.path.isfile(pony): if not os.path.isfile(pony):
printerr('%s is not an existing regular file' % pony) printerr('%s is not an existing regular file' % pony)
exit(252) os.exit(252)
linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux') linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux')
try: try:
if opts['--no-term-init'] is None: if opts['--no-term-init'] is None:
print('\033[?1049h', end='') # initialise terminal print('\[[?1049h', end='') # initialise terminal
if linuxvt: print('\033[?8c', end='') # use full block for cursor (_ is used by default in linux vt) if linuxvt: print('\[[?8c', end='') # use full block for cursor (_ is used by default in linux vt)
cmd = 'stty %s < %s > /dev/null 2> /dev/null' cmd = 'stty %s < %s > /dev/null 2> /dev/null'
cmd %= ('-echo -icanon -echo -isig -ixoff -ixon', os.path.realpath('/dev/stdout')) cmd %= ('-echo -icanon -echo -isig -ixoff -ixon', os.path.realpath('/dev/stdout'))
Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait() Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait()
@ -140,9 +141,9 @@ class PonysayTool():
cmd = 'stty %s < %s > /dev/null 2> /dev/null' cmd = 'stty %s < %s > /dev/null 2> /dev/null'
cmd %= ('echo icanon echo isig ixoff ixon', os.path.realpath('/dev/stdout')) cmd %= ('echo icanon echo isig ixoff ixon', os.path.realpath('/dev/stdout'))
Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait() Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).wait()
if linuxvt: print('\033[?0c', end='') # restore cursor if linuxvt: print('\[[?0c', end='') # restore cursor
if opts['--no-term-init'] is None: if opts['--no-term-init'] is None:
print('\033[?1049l', end='') # terminate terminal print('\[[?1049l', end='') # terminate terminal
elif (opts['--edit-rm'] is not None) and (len(opts['--edit-rm']) == 1): elif (opts['--edit-rm'] is not None) and (len(opts['--edit-rm']) == 1):
ponyfile = opts['--edit-rm'][0] ponyfile = opts['--edit-rm'][0]
@ -174,7 +175,7 @@ class PonysayTool():
if data == '': if data == '':
if line != '$$$': if line != '$$$':
printerr('Bad stash') printerr('Bad stash')
exit(251) os.exit(251)
data += '$$$\n' data += '$$$\n'
else: else:
data += line + '\n' data += line + '\n'
@ -192,7 +193,7 @@ class PonysayTool():
else: else:
args.help() args.help()
exit(253) os.exit(253)
def execPonysay(self, args, message = ''): def execPonysay(self, args, message = ''):
@ -214,9 +215,9 @@ class PonysayTool():
return args[key] if (args[key] is not None) and isinstance(args[key], list) else [args[key]] return args[key] if (args[key] is not None) and isinstance(args[key], list) else [args[key]]
return None return None
def __contains__(self, key): def __contains__(self, key):
return key in args; return key in args
stdout = sys.stdout stdout = fileio.stdout
class StringInputStream(): class StringInputStream():
def __init__(self): def __init__(self):
self.buf = '' self.buf = ''
@ -232,11 +233,11 @@ class PonysayTool():
pass pass
def isatty(self): def isatty(self):
return True return True
sys.stdout = StringInputStream() fileio.stdout = StringInputStream()
ponysay = Ponysay() ponysay = Ponysay()
ponysay.run(PhonyArgParser(args, message)) ponysay.run(PhonyArgParser(args, message))
out = sys.stdout.buf[:-1] out = fileio.stdout.buf[:-1]
sys.stdout = stdout fileio.stdout = stdout
return out return out
@ -249,7 +250,7 @@ class PonysayTool():
''' '''
## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses ## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses
termsize = None termsize = None
for channel in (sys.stdout, sys.stdin, sys.stderr): for channel in (fileio.stdout, fileio.stdin, fileio.stderr):
termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0] termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0]
if len(termsize) > 0: if len(termsize) > 0:
termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n
@ -275,7 +276,7 @@ class PonysayTool():
ponies.sort() ponies.sort()
if len(ponies) == 0: if len(ponies) == 0:
print('\033[1;31m%s\033[21m;39m' % 'No ponies... press Enter to exit.') print('\[[1;31m%s\[[22m;39m' % 'No ponies... press Enter to exit.')
input() input()
panelw = Backend.len(max(ponies, key = Backend.len)) panelw = Backend.len(max(ponies, key = Backend.len))
@ -300,14 +301,14 @@ class PonysayTool():
ponyfile = (ponydir + '/' + ponies[ponyindex] + '.pony').replace('//', '/') ponyfile = (ponydir + '/' + ponies[ponyindex] + '.pony').replace('//', '/')
pony = self.execPonysay({'-f' : ponyfile, '-W' : 'none', '-o' : None}).split('\n') pony = self.execPonysay({'-f' : ponyfile, '-W' : 'none', '-o' : None}).split('\n')
preprint = '\033[H\033[2J' preprint = '\[[H\[[2J'
if pony[0].startswith(preprint): if pony[0].startswith(preprint):
pony[0] = pony[0][len(preprint):] pony[0] = pony[0][len(preprint):]
ponyheight = len(pony) ponyheight = len(pony)
ponywidth = Backend.len(max(pony, key = Backend.len)) ponywidth = Backend.len(max(pony, key = Backend.len))
AUTO_PUSH = '\033[01010~' AUTO_PUSH = '\[[01010~'
AUTO_POP = '\033[10101~' AUTO_POP = '\[[10101~'
pony = '\n'.join(pony).replace('\n', AUTO_PUSH + '\n' + AUTO_POP) pony = '\n'.join(pony).replace('\n', AUTO_PUSH + '\n' + AUTO_POP)
colourstack = ColourStack(AUTO_PUSH, AUTO_POP) colourstack = ColourStack(AUTO_PUSH, AUTO_POP)
buf = '' buf = ''
@ -317,7 +318,7 @@ class PonysayTool():
if (oldx != x) or (oldy != y): if (oldx != x) or (oldy != y):
(oldx, oldy) = (x, y) (oldx, oldy) = (x, y)
print('\033[H\033[2J', end='') print('\[[H\[[2J', end='')
def getprint(pony, ponywidth, ponyheight, termw, termh, px, py): def getprint(pony, ponywidth, ponyheight, termw, termh, px, py):
ponyprint = pony ponyprint = pony
@ -338,7 +339,7 @@ class PonysayTool():
elif px > 0: elif px > 0:
ponyprint = [px * ' ' + line for line in ponyprint] ponyprint = [px * ' ' + line for line in ponyprint]
ponyprint = [(line if Backend.len(line) <= termw else line[:findcolumn(line, termw)]) 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] ponyprint = ['\[[22;39;49;0m%s\[[22;39;49;0m' % line for line in ponyprint]
return '\n'.join(ponyprint) return '\n'.join(ponyprint)
if quotes: if quotes:
@ -361,24 +362,24 @@ class PonysayTool():
for line in ponies[panely:]: for line in ponies[panely:]:
cury += 1 cury += 1
if os.path.islink((ponydir + '/' + line + '.pony').replace('//', '/')): if os.path.islink((ponydir + '/' + line + '.pony').replace('//', '/')):
line = '\033[34m%s\033[39m' % ((line + ' ' * panelw)[:panelw]) line = '\[[34m%s\[[39m' % ((line + ' ' * panelw)[:panelw])
else: else:
line = (line + ' ' * panelw)[:panelw] 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='') print('\[[%i;%iH\[[%im%s\[[0m' % (cury, panelx + 1, 1 if panely + cury - 1 == ponyindex else 0, line), end='')
elif printpanel >= 0: elif printpanel >= 0:
for index in (printpanel, ponyindex): for index in (printpanel, ponyindex):
cury = index - panely cury = index - panely
if (0 <= cury) and (cury < termh): if (0 <= cury) and (cury < termh):
line = ponies[cury + panely] line = ponies[cury + panely]
if os.path.islink((ponydir + '/' + line + '.pony').replace('//', '/')): if os.path.islink((ponydir + '/' + line + '.pony').replace('//', '/')):
line = '\033[34m%s\033[39m' % ((line + ' ' * panelw)[:panelw]) line = '\[[34m%s\[[39m' % ((line + ' ' * panelw)[:panelw])
else: else:
line = (line + ' ' * panelw)[:panelw] 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='') print('\[[%i;%iH\[[%im%s\[[0m' % (cury, panelx + 1, 1 if panely + cury - 1 == ponyindex else 0, line), end='')
sys.stdout.buffer.flush() fileios.stdout.buffer.flush()
if stored is None: if stored is None:
d = sys.stdin.read(1) d = fileio.stdin.read(1)
else: else:
d = stored d = stored
stored = None stored = None
@ -409,20 +410,20 @@ class PonysayTool():
elif ord(d) == ord('Q') - ord('@'): elif ord(d) == ord('Q') - ord('@'):
break break
elif ord(d) == ord('X') - ord('@'): elif ord(d) == ord('X') - ord('@'):
if ord(sys.stdin.read(1)) == ord('C') - ord('@'): if ord(fileio.stdin.read(1)) == ord('C') - ord('@'):
break break
elif d == '\033': elif d == '\[':
d = sys.stdin.read(1) d = fileio.stdin.read(1)
if d == '[': if d == '[':
d = sys.stdin.read(1) d = fileio.stdin.read(1)
if d == 'A': stored = chr(ord('P') - ord('@')) if (not quotes) and (not info) else 'W' 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 == '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 == '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 == 'D': stored = chr(ord('P') - ord('@')) if (not quotes) and (not info) else 'A'
elif d == '1': elif d == '1':
if sys.stdin.read(1) == ';': if fileio.stdin.read(1) == ';':
if sys.stdin.read(1) == '5': if fileio.stdin.read(1) == '5':
d = sys.stdin.read(1) d = fileio.stdin.read(1)
if d == 'A': stored = 'W' if d == 'A': stored = 'W'
elif d == 'B': stored = 'S' elif d == 'B': stored = 'S'
elif d == 'C': stored = 'D' elif d == 'C': stored = 'D'
@ -446,7 +447,7 @@ class PonysayTool():
def __getitem__(self, key): def __getitem__(self, key):
return [self.value] if key == self.key else None return [self.value] if key == self.key else None
def __contains__(self, key): def __contains__(self, key):
return key == self.key; return key == self.key
class StringInputStream(): class StringInputStream():
def __init__(self): def __init__(self):
@ -464,36 +465,36 @@ class PonysayTool():
def isatty(self): def isatty(self):
return True return True
stdout = sys.stdout stdout = fileio.stdout
term = os.environ['TERM'] term = os.environ['TERM']
os.environ['TERM'] = 'linux' os.environ['TERM'] = 'linux'
sys.stdout = StringInputStream() fileio.stdout = StringInputStream()
ponysay = Ponysay() ponysay = Ponysay()
ponysay.run(PhonyArgParser('--onelist', None)) ponysay.run(PhonyArgParser('--onelist', None))
stdponies = sys.stdout.buf[:-1].split('\n') stdponies = fileio.stdout.buf[:-1].split('\n')
sys.stdout = StringInputStream() fileio.stdout = StringInputStream()
ponysay = Ponysay() ponysay = Ponysay()
ponysay.run(PhonyArgParser('++onelist', None)) ponysay.run(PhonyArgParser('++onelist', None))
extraponies = sys.stdout.buf[:-1].split('\n') extraponies = fileio.stdout.buf[:-1].split('\n')
for pony in stdponies: for pony in stdponies:
printerr('Genering standard kmspony: %s' % pony) printerr('Genering standard kmspony: %s' % pony)
sys.stderr.buffer.flush(); fileio.stderr.buffer.flush()
sys.stdout = StringInputStream() fileio.stdout = StringInputStream()
ponysay = Ponysay() ponysay = Ponysay()
ponysay.run(PhonyArgParser('--pony', pony)) ponysay.run(PhonyArgParser('--pony', pony))
for pony in extraponies: for pony in extraponies:
printerr('Genering extra kmspony: %s' % pony) printerr('Genering extra kmspony: %s' % pony)
sys.stderr.buffer.flush(); fileio.stderr.buffer.flush()
sys.stdout = StringInputStream() fileio.stdout = StringInputStream()
ponysay = Ponysay() ponysay = Ponysay()
ponysay.run(PhonyArgParser('++pony', pony)) ponysay.run(PhonyArgParser('++pony', pony))
os.environ['TERM'] = term os.environ['TERM'] = term
sys.stdout = stdout fileio.stdout = stdout
def generateDimensions(self, ponydir, ponies = None): def generateDimensions(self, ponydir, ponies = None):
@ -523,8 +524,8 @@ class PonysayTool():
return [('none' if self.balloon else None)] return [('none' if self.balloon else None)]
return None return None
def __contains__(self, key): def __contains__(self, key):
return key in ('-f', '-W', '-b'); return key in ('-f', '-W', '-b')
stdout = sys.stdout stdout = fileio.stdout
class StringInputStream(): class StringInputStream():
def __init__(self): def __init__(self):
self.buf = '' self.buf = ''
@ -540,7 +541,7 @@ class PonysayTool():
pass pass
def isatty(self): def isatty(self):
return True return True
sys.stdout = StringInputStream() fileio.stdout = StringInputStream()
ponysay = Ponysay() ponysay = Ponysay()
ponysay.run(PhonyArgParser(True)) ponysay.run(PhonyArgParser(True))
printpony = sys.stdout.buf[:-1].split('\n') printpony = sys.stdout.buf[:-1].split('\n')
@ -697,7 +698,7 @@ class PonysayTool():
if key == '-W': return ['n'] if key == '-W': return ['n']
return None return None
def __contains__(self, key): def __contains__(self, key):
return key in ('-f', '-W'); return key in ('-f', '-W')
data = {} data = {}
@ -750,7 +751,7 @@ class PonysayTool():
printpony = sys.stdout.buf[:-1].split('\n') printpony = sys.stdout.buf[:-1].split('\n')
sys.stdout = stdout sys.stdout = stdout
preprint = '\033[H\033[2J' preprint = '\[[H\[[2J'
if printpony[0].startswith(preprint): if printpony[0].startswith(preprint):
printpony[0] = printpony[0][len(preprint):] printpony[0] = printpony[0][len(preprint):]
ponyheight = len(printpony) - len(ponyfile.split('\n')) + 1 - 2 # using fallback balloon ponyheight = len(printpony) - len(ponyfile.split('\n')) + 1 - 2 # using fallback balloon
@ -765,8 +766,8 @@ class PonysayTool():
termsize = [int(item) for item in termsize] termsize = [int(item) for item in termsize]
break break
AUTO_PUSH = '\033[01010~' AUTO_PUSH = '\[[01010~'
AUTO_POP = '\033[10101~' AUTO_POP = '\[[10101~'
modprintpony = '\n'.join(printpony).replace('\n', AUTO_PUSH + '\n' + AUTO_POP) modprintpony = '\n'.join(printpony).replace('\n', AUTO_PUSH + '\n' + AUTO_POP)
colourstack = ColourStack(AUTO_PUSH, AUTO_POP) colourstack = ColourStack(AUTO_PUSH, AUTO_POP)
buf = '' buf = ''
@ -774,12 +775,12 @@ class PonysayTool():
buf += c + colourstack.feed(c) buf += c + colourstack.feed(c)
modprintpony = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '') 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')] printpony = [('\[[22;39;49;0m%s%s\[[22;39;49;0m' % (' ' * (termsize[1] - ponywidth), line)) for line in modprintpony.split('\n')]
print(preprint, end='') print(preprint, end='')
print('\n'.join(printpony), end='') print('\n'.join(printpony), end='')
print('\033[H', end='') print('\[[H', end='')
print('Please see the info manual for details on how to fill out this form') print('Please see the info manual for details on how to fill out this form')
print() print()
@ -885,17 +886,17 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
killptr = None killptr = None
def status(text): 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='') print('\[[%i;%iH\[[7m%s\[[27m\[[%i;%iH' % (termh - 1, 1, ' (' + text + ') ' + '-' * (termw - len(' (' + text + ') ')), self.top + y, innerleft + x), end='')
status('unmodified') status('unmodified')
print('\033[%i;%iH' % (self.top, innerleft), end='') print('\[[%i;%iH' % (self.top, innerleft), end='')
def alert(text): def alert(text):
if text is None: if text is None:
alert('') alert('')
else: else:
print('\033[%i;%iH\033[2K%s\033[%i;%iH' % (termh, 1, text, self.top + y, innerleft + x), end='') print('\[[%i;%iH\[[2K%s\[[%i;%iH' % (termh, 1, text, self.top + y, innerleft + x), end='')
modified = False modified = False
override = False override = False
@ -904,28 +905,28 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
stored = chr(ord('L') - ord('@')) stored = chr(ord('L') - ord('@'))
alerted = False alerted = False
edited = False edited = False
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='') print('\[[%i;%iH' % (self.top + y, innerleft + x), end='')
while True: while True:
if (oldmark is not None) and (oldmark >= 0): if (oldmark is not None) and (oldmark >= 0):
if oldmark < oldx: 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='') print('\[[%i;%iH\[[49m%s\[[%i;%iH' % (self.top + oldy, innerleft + oldmark, datalines[oldy][oldmark : oldx], self.top + y, innerleft + x), end='')
elif oldmark > oldx: 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='') print('\[[%i;%iH\[[49m%s\[[%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 is not None) and (mark >= 0):
if mark < x: 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='') print('\[[%i;%iH\[[44;37m%s\[[49;39m\[[%i;%iH' % (self.top + y, innerleft + mark, datalines[y][mark : x], self.top + y, innerleft + x), end='')
elif mark > x: 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='') print('\[[%i;%iH\[[44;37m%s\[[49;39m\[[%i;%iH' % (self.top + y, innerleft + x, datalines[y][x : mark], self.top + y, innerleft + x), end='')
if y != oldy: if y != oldy:
if (oldy > 0) and (leftlines[oldy - 1] == leftlines[oldy]) and (leftlines[oldy] == leftlines[-1]): 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='') print('\[[%i;%iH\[[34m%s\[[39m' % (self.top + oldy, self.left, '>'), end='')
else: else:
print('\033[%i;%iH\033[34m%s:\033[39m' % (self.top + oldy, self.left, leftlines[oldy]), end='') print('\[[%i;%iH\[[34m%s:\[[39m' % (self.top + oldy, self.left, leftlines[oldy]), end='')
if (y > 0) and (leftlines[y - 1] == leftlines[y]) and (leftlines[y] == leftlines[-1]): 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='') print('\[[%i;%iH\[[1;34m%s\[[22;39m' % (self.top + y, self.left, '>'), end='')
else: else:
print('\033[%i;%iH\033[1;34m%s:\033[21;39m' % (self.top + y, self.left, leftlines[y]), end='') print('\[[%i;%iH\[[1;34m%s:\[[22;39m' % (self.top + y, self.left, leftlines[y]), end='')
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='') print('\[[%i;%iH' % (self.top + y, innerleft + x), end='')
(oldy, oldx, oldmark) = (y, x, mark) (oldy, oldx, oldmark) = (y, x, mark)
if edited: if edited:
edited = False edited = False
@ -980,10 +981,10 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
mark = None mark = None
killptr = len(killring) - 1 killptr = len(killring) - 1
yanked = killring[killptr] yanked = killring[killptr]
print('\033[%i;%iH%s' % (self.top + y, innerleft + x, yanked + datalines[y][x:]), end='') print('\[[%i;%iH%s' % (self.top + y, innerleft + x, yanked + datalines[y][x:]), end='')
datalines[y] = datalines[y][:x] + yanked + datalines[y][x:] datalines[y] = datalines[y][:x] + yanked + datalines[y][x:]
x += len(yanked) x += len(yanked)
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='') print('\[[%i;%iH' % (self.top + y, innerleft + x), end='')
elif ord(d) == ord('X') - ord('@'): elif ord(d) == ord('X') - ord('@'):
alert('C-x') alert('C-x')
alerted = True alerted = True
@ -1034,7 +1035,7 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
datalines[y] = dataline = dataline[:x - removed] + dataline[x:] datalines[y] = dataline = dataline[:x - removed] + dataline[x:]
x -= removed x -= removed
mark = None mark = None
print('\033[%i;%iH%s%s\033[%i;%iH' % (self.top + y, innerleft, dataline, ' ' * removed, self.top + y, innerleft + x), end='') print('\[[%i;%iH%s%s\[[%i;%iH' % (self.top + y, innerleft, dataline, ' ' * removed, self.top + y, innerleft + x), end='')
edited = True edited = True
elif ord(d) < ord(' '): elif ord(d) < ord(' '):
if ord(d) == ord('P') - ord('@'): if ord(d) == ord('P') - ord('@'):
@ -1055,14 +1056,14 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
elif ord(d) == ord('F') - ord('@'): elif ord(d) == ord('F') - ord('@'):
if x < len(datalines[y]): if x < len(datalines[y]):
x += 1 x += 1
print('\033[C', end='') print('\[[C', end='')
else: else:
alert('At end') alert('At end')
alerted = True alerted = True
elif ord(d) == ord('B') - ord('@'): elif ord(d) == ord('B') - ord('@'):
if x > 0: if x > 0:
x -= 1 x -= 1
print('\033[D', end='') print('\[[D', end='')
else: else:
alert('At beginning') alert('At beginning')
alerted = True alerted = True
@ -1074,17 +1075,17 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
x = 0 x = 0
stored = chr(ord('L') - ord('@')) stored = chr(ord('L') - ord('@'))
elif ord(d) == ord('L') - ord('@'): elif ord(d) == ord('L') - ord('@'):
empty = '\033[0m' + (' ' * self.width + '\n') * len(datalines) empty = '\[[0m' + (' ' * self.width + '\n') * len(datalines)
print('\033[%i;%iH%s' % (self.top, self.left, empty), end='') print('\[[%i;%iH%s' % (self.top, self.left, empty), end='')
for row in range(0, len(leftlines)): for row in range(0, len(leftlines)):
leftline = leftlines[row] + ':' leftline = leftlines[row] + ':'
if (leftlines[row - 1] == leftlines[row]) and (leftlines[row] == leftlines[-1]): if (leftlines[row - 1] == leftlines[row]) and (leftlines[row] == leftlines[-1]):
leftline = '>' 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='') print('\[[%i;%iH\[[%s34m%s\[[%s39m' % (self.top + row, self.left, '1;' if row == y else '', leftline, '22;' if row == y else ''), end='')
for row in range(0, len(datalines)): for row in range(0, len(datalines)):
print('\033[%i;%iH%s\033[49m' % (self.top + row, innerleft, datalines[row]), end='') print('\[[%i;%iH%s\[[49m' % (self.top + row, innerleft, datalines[row]), end='')
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='') print('\[[%i;%iH' % (self.top + y, innerleft + x), end='')
elif d == '\033': elif d == '\[':
d = sys.stdin.read(1) d = sys.stdin.read(1)
if d == '[': if d == '[':
d = sys.stdin.read(1) d = sys.stdin.read(1)
@ -1119,7 +1120,7 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
alerted = True alerted = True
continue continue
datalines[y] = dataline = dataline[:x] + dataline[x + removed:] 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='') print('\[[%i;%iH%s%s\[[%i;%iH' % (self.top + y, innerleft, dataline, ' ' * removed, self.top + y, innerleft + x), end='')
mark = None mark = None
edited = True edited = True
else: else:
@ -1150,18 +1151,18 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
additional = len(killring[killptr]) - len(yanked) additional = len(killring[killptr]) - len(yanked)
x += additional x += additional
datalines[y] = dataline 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='') print('\[[%i;%iH%s%s\[[%i;%iH' % (self.top + y, innerleft, dataline, ' ' * max(0, -additional), self.top + y, innerleft + x), end='')
else: else:
stored = chr(ord('Y') - ord('@')) stored = chr(ord('Y') - ord('@'))
else: else:
stored = chr(ord('Y') - ord('@')) stored = chr(ord('Y') - ord('@'))
elif d == 'O': elif d == 'O':
d = sys.stdin.read(1) d = fileio.stdin.read(1)
if d == 'H': if d == 'H':
x = 0 x = 0
elif d == 'F': elif d == 'F':
x = len(datalines[y]) x = len(datalines[y])
print('\033[%i;%iH' % (self.top + y, innerleft + x), end='') print('\[[%i;%iH' % (self.top + y, innerleft + x), end='')
elif d == '\n': elif d == '\n':
stored = chr(ord('N') - ord('@')) stored = chr(ord('N') - ord('@'))
else: else:
@ -1172,7 +1173,7 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
if (not override) or (x == len(dataline)): if (not override) or (x == len(dataline)):
print(insert + dataline[x:], end='') print(insert + dataline[x:], end='')
if len(dataline) - x > 0: if len(dataline) - x > 0:
print('\033[%iD' % (len(dataline) - x), end='') print('\[[%iD' % (len(dataline) - x), end='')
datalines[y] = dataline[:x] + insert + dataline[x:] datalines[y] = dataline[:x] + insert + dataline[x:]
if (mark is not None) and (mark >= 0): if (mark is not None) and (mark >= 0):
if mark >= x: if mark >= x:
@ -1185,46 +1186,46 @@ class TextArea(): # TODO support small screens (This is being work on in GNU-Po
HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~') let HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~')
''' '''
The user's home directory The user's home directory
''' '''
pipelinein = not sys.stdin.isatty() let pipelinein = not os.isatty(0)
''' '''
Whether stdin is piped Whether stdin is piped
''' '''
pipelineout = not sys.stdout.isatty() let pipelineout = not os.isatty(1)
''' '''
Whether stdout is piped Whether stdout is piped
''' '''
pipelineerr = not sys.stderr.isatty() let pipelineerr = not os.isatty(2)
''' '''
Whether stderr is piped Whether stderr is piped
''' '''
usage_program = '\033[34;1mponysay-tool\033[21;39m' let usage_program = '\[[34;1mponysay-tool\[[22;39m'
usage = '\n'.join(['%s %s' % (usage_program, '(--help | --version | --kms)'), let usage = '\n'.join([' '.join((usage_program, '(--help | --version | --kms)')),
'%s %s' % (usage_program, '(--edit | --edit-rm) \033[33mPONY-FILE\033[39m'), ' '.join((usage_program, '(--edit | --edit-rm) \[[33mPONY-FILE\[[39m')),
'%s %s' % (usage_program, '--edit-stash \033[33mPONY-FILE\033[39m > \033[33mSTASH-FILE\033[39m'), ' '.join((usage_program, '--edit-stash \[[33mPONY-FILE\[[39m > \[[33mSTASH-FILE\[[39m')),
'%s %s' % (usage_program, '--edit-apply \033[33mPONY-FILE\033[39m < \033[33mSTASH-FILE\033[39m'), ' '.join((usage_program, '--edit-apply \[[33mPONY-FILE\[[39m < \[[33mSTASH-FILE\[[39m')),
'%s %s' % (usage_program, '(--dimensions | --metadata) \033[33mPONY-DIR\033[39m'), ' '.join((usage_program, '(--dimensions | --metadata) \[[33mPONY-DIR\[[39m')),
'%s %s' % (usage_program, '--browse \033[33mPONY-DIR\033[39m [-r \033[33mRESTRICTION\033[39m]*'), ' '.join((usage_program, '--browse \[[33mPONY-DIR\[[39m [-r \[[33mRESTRICTION\[[39m]*')),
]) ])
usage = usage.replace('\033[', '\0') usage = usage.replace('\[[', 'あ')
for sym in ('[', ']', '(', ')', '|', '...', '*'): for sym in ('[', ']', '(', ')', '|', '...', '*'):
usage = usage.replace(sym, '\033[2m' + sym + '\033[22m') usage = usage.replace(sym, '\[[2m' + sym + '\[[22m')
usage = usage.replace('\0', '\033[') usage = usage.replace('あ', '\[[')
''' '''
Argument parsing Argument parsing
''' '''
opts = ArgParser(program = 'ponysay-tool', let opts = ArgParser(program = 'ponysay-tool',
description = 'Tool chest for ponysay', description = 'Tool chest for ponysay',
usage = usage, usage = usage,
longdescription = None) longdescription = None)
@ -1244,7 +1245,7 @@ opts.add_argumented( ['--edit-rm'], arg = 'PONY-FILE', help = 'Remove
opts.add_argumented( ['--edit-apply'], arg = 'PONY-FILE', help = 'Apply metadata from stdin to 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') opts.add_argumented( ['--edit-stash'], arg = 'PONY-FILE', help = 'Print applyable metadata from a pony file')
unrecognised = not opts.parse() let unrecognised = not opts.parse()
''' '''
Whether at least one unrecognised option was used Whether at least one unrecognised option was used
''' '''

View file

@ -29,8 +29,7 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith
class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimised Java, may not be the nicest, or even fast, Python code class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimised Java, may not be the nicest, or even fast, Python code
@ -74,18 +73,18 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
for y in range(0, 128): for y in range(0, 128):
self.M[y] = [0] * 128 self.M[y] = [0] * 128
self.M[y][0] = y self.M[y][0] = y
m0 = self.M[0] let m0 = self.M[0]
x = 127 let x = 127
while x > -1: while x > -1:
m0[x] = x m0[x] = x
x -= 1 x -= 1
previous = '' let previous = ''
self.dictionary[-1] = previous; self.dictionary[-1] = previous
if ending is not None: if ending is not None:
for directory in directories: for directory in directories:
files = os.listdir(directory) let files = os.listdir(directory)
files.sort() files.sort()
for filename in files: for filename in files:
if (not endswith(filename, ending)) or (len(filename) - len(ending) > 127): if (not endswith(filename, ending)) or (len(filename) - len(ending) > 127):
@ -108,7 +107,7 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
previous = proper previous = proper
self.reusable[self.dictionaryEnd] = prevCommon self.reusable[self.dictionaryEnd] = prevCommon
else: else:
files = directories let files = directories
files.sort() files.sort()
for proper in files: for proper in files:
if len(proper) > 127: if len(proper) > 127:
@ -167,20 +166,20 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
@param used:str The word to correct, it must satisfy all restrictions @param used:str The word to correct, it must satisfy all restrictions
''' '''
self.closestDistance = 0x7FFFFFFF self.closestDistance = 0x7FFFFFFF
previous = self.dictionary[-1] let previous = self.dictionary[-1]
prevLen = 0 let prevLen = 0
usedLen = len(used) let usedLen = len(used)
proper = None let proper = None
prevCommon = 0 let prevCommon = 0
d = len(self.dictionary) - 1 let d = len(self.dictionary) - 1
while d > self.dictionaryEnd: while d > self.dictionaryEnd:
d -= 1 d -= 1
proper = self.dictionary[d] proper = self.dictionary[d]
if abs(len(proper) - usedLen) <= self.closestDistance: if abs(len(proper) - usedLen) <= self.closestDistance:
if previous == self.dictionary[d + 1]: if previous == self.dictionary[d + 1]:
prevCommon = self.reusable[d]; prevCommon = self.reusable[d]
else: else:
prevCommon = min(prevLen, len(proper)) prevCommon = min(prevLen, len(proper))
for i in range(0, prevCommon): for i in range(0, prevCommon):
@ -188,8 +187,8 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
prevCommon = i prevCommon = i
break break
skip = min(prevLen, len(proper)) let skip = min(prevLen, len(proper))
i = prevCommon let i = prevCommon
while i < skip: while i < skip:
for u in range(0, usedLen): for u in range(0, usedLen):
if (used[u] == previous[i]) or (used[u] == proper[i]): if (used[u] == previous[i]) or (used[u] == proper[i]):
@ -197,13 +196,13 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
break break
i += 1 i += 1
common = min(skip, min(usedLen, len(proper))) let common = min(skip, min(usedLen, len(proper)))
for i in range(0, common): for i in range(0, common):
if used[i] != proper[i]: if used[i] != proper[i]:
common = i common = i
break break
distance = self.__distance(proper, skip, len(proper), used, common, usedLen) let distance = self.__distance(proper, skip, len(proper), used, common, usedLen)
if self.closestDistance > distance: if self.closestDistance > distance:
self.closestDistance = distance self.closestDistance = distance
@ -211,7 +210,7 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
elif self.closestDistance == distance: elif self.closestDistance == distance:
self.corrections.append(proper) self.corrections.append(proper)
previous = proper; previous = proper
if distance >= 0x7FFFFF00: if distance >= 0x7FFFFF00:
prevLen = distance & 255 prevLen = distance & 255
else: else:

View file

@ -29,7 +29,7 @@ that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by lines and the FREE is and upper case and directly followed by
the colon. the colon.
''' '''
from common import * from ponysay.common import printerr, printinfo, gettermsize, endswith
@ -46,7 +46,7 @@ class UCS():
@param char:chr The character to test @param char:chr The character to test
@return :bool Whether the character is a combining character @return :bool Whether the character is a combining character
''' '''
o = ord(char) let o = ord(char)
if (0x0300 <= o) and (o <= 0x036F): return True if (0x0300 <= o) and (o <= 0x036F): return True
if (0x20D0 <= o) and (o <= 0x20FF): return True if (0x20D0 <= o) and (o <= 0x20FF): return True
if (0x1DC0 <= o) and (o <= 0x1DFF): return True if (0x1DC0 <= o) and (o <= 0x1DFF): return True
@ -62,7 +62,7 @@ class UCS():
@param string:str A text to count combining characters in @param string:str A text to count combining characters in
@return :int The number of combining characters in the string @return :int The number of combining characters in the string
''' '''
rc = 0 let rc = 0
for char in string: for char in string:
if UCS.isCombining(char): if UCS.isCombining(char):
rc += 1 rc += 1