beginning of ponysay-tool which will be able to edit pony meta data. ponysay.py as been a bit edited so ponysay-tool can run it internally

This commit is contained in:
Mattias Andrée 2012-10-30 01:31:17 +01:00
parent 2d5aad7bfb
commit 0a79a833d4
2 changed files with 384 additions and 188 deletions

176
ponysay-tool.py Executable file
View file

@ -0,0 +1,176 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
ponysay - Ponysay, cowsay reimplementation for ponies
Copyright (C) 2012 Erkin Batu Altunbaş et al.
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
'''
import os
import shutil
import sys
import random
from subprocess import Popen, PIPE
from ponysay import *
'''
The version of ponysay
'''
VERSION = 'dev' # this line should not be edited, it is fixed by the build system
'''
Hack to enforce UTF-8 in output (in the future, if you see anypony not using utf-8 in
programs by default, report them to Princess Celestia so she can banish them to the moon)
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
def print(text = '', end = '\n'):
sys.stdout.buffer.write((str(text) + end).encode('utf-8'))
'''
stderr equivalent to print()
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
def printerr(text = '', end = '\n'):
sys.stderr.buffer.write((str(text) + end).encode('utf-8'))
'''
This is the mane class of ponysay-tool
'''
class PonysayTool():
'''
Starts the part of the program the arguments indicate
@param args:ArgParser Parsed command line arguments
'''
def __init__(self, args):
if args.argcount == 0:
args.help()
exit(255)
return
opts = args.opts
if unrecognised or (opts['-h'] is not None):
args.help()
if unrecognised:
exit(254)
elif opts['-v'] is not None:
print('%s %s' % ('ponysay', VERSION))
elif (opts['--edit'] is not None) and (len(opts['--edit']) == 1):
pony = opts['--edit'][0]
if not os.path.isfile(pony):
printerr('%s is not an existing regular file' % pony)
exit(252)
self.editmeta(pony)
else:
exit(253)
'''
Edit a pony file's meta data
@param ponyfile:str A pony file to edit
'''
def editmeta(self, ponyfile):
(data, meta, image) = 3 * [None]
with open(ponyfile, 'rb') as file:
data = file.read().decode('utf8', 'replace')
data = [line.replace('\n', '') for line in data.split('\n')]
if data[0] != '$$$':
image = data
meta = []
else:
sep = 1
while data[sep] != '$$$':
sep += 1
meta = data[1:][:sep]
image = data[sep + 1:]
class PhonyArgsParser:
def __init__(self):
self.argcount = 3
self.message = ponyfile
self.opts = self
def __getitem__(self, key):
return [ponyfile] if key == '-f' else None
ponysay = Ponysay()
ponysay.run(PhonyArgsParser())
'''
Start the program from ponysay.__init__ if this is the executed file
'''
if __name__ == '__main__':
'''
The user's home directory
'''
HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~')
'''
Whether stdin is piped
'''
pipelinein = not sys.stdin.isatty()
'''
Whether stdout is piped
'''
pipelineout = not sys.stdout.isatty()
'''
Whether stderr is piped
'''
pipelineerr = not sys.stderr.isatty()
usage_program = '\033[34;1mponysay-tool\033[21;39m'
usage = '\n'.join(['%s %s' % (usage_program, '(--help | --version)'),
'%s %s' % (usage_program, '--edit \033[4mPONY-FILE\033[24m'),])
usage = usage.replace('\033[', '\0')
for sym in ('[', ']', '(', ')', '|', '...', '*'):
usage = usage.replace(sym, '\033[2m' + sym + '\033[22m')
usage = usage.replace('\0', '\033[')
'''
Argument parsing
'''
opts = ArgParser(program = 'ponysay-tool',
description = 'Tool chest for ponysay',
usage = usage,
longdescription = None)
opts.add_argumentless(['-h', '--help'], help = 'Print this help message.')
opts.add_argumentless(['-v', '--version'], help = 'Print the version of the program.')
opts.add_argumented( ['--edit'], arg = 'PONY-FILE', help = 'Edit a pony file\'s meta data')
'''
Whether at least one unrecognised option was used
'''
unrecognised = not opts.parse()
PonysayTool(args = opts)

View file

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
ponysay.py - Ponysay, cowsay reimplementation for ponies ponysay - Ponysay, cowsay reimplementation for ponies
Copyright (C) 2012 Erkin Batu Altunbaş et al. Copyright (C) 2012 Erkin Batu Altunbaş et al.
This program is free software. It comes without any warranty, to This program is free software. It comes without any warranty, to
@ -87,30 +87,165 @@ def endswith(text, ending):
This is the mane class of ponysay This is the mane class of ponysay
''' '''
class Ponysay(): class Ponysay():
'''
Constructor
'''
def __init__(self):
'''
The user's home directory
'''
self.HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~')
## Change system enviroments with ~/.ponysayrc
if os.path.exists(self.HOME + '/.ponysayrc'):
with open(self.HOME + '/.ponysayrc', 'rb') as ponysayrc:
code = ponysayrc.read().decode('utf8', 'replace') + '\n'
env = os.environ
code = compile(code, self.HOME + '/.ponysayrc', 'exec')
exec(code)
self.HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~') # in case ~/.ponysayrc changes it
'''
Whether any unrecognised options was parsed, this should be set by the invoker before run()
'''
self.unrecognised = False
'''
Whether the program is execute in Linux VT (TTY)
'''
self.linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux')
'''
Whether the script is executed as ponythink
'''
self.isthink = (len(__file__) >= len('think')) and (__file__.endswith('think'))
self.isthink = ((len(__file__) >= len('think.py')) and (__file__.endswith('think.py'))) or self.isthink
'''
Whether stdin is piped
'''
self.pipelinein = not sys.stdin.isatty()
'''
Whether stdout is piped
'''
self.pipelineout = not sys.stdout.isatty()
'''
Whether stderr is piped
'''
self.pipelineerr = not sys.stderr.isatty()
'''
Whether KMS is used
'''
self.usekms = self.isUsingKMS()
'''
Mode string that modifies or adds $ variables in the pony image
'''
self.mode = ''
'''
The directories where pony files are stored, ttyponies/ are used if the terminal is Linux VT (also known as TTY) and not with KMS
'''
appendset = set()
self.xponydirs = []
_ponydirs = [self.HOME + '/.local/share/ponysay/ponies/', '/usr/share/ponysay/ponies/']
for ponydir in _ponydirs:
if os.path.isdir(ponydir) and (ponydir not in appendset):
self.xponydirs.append(ponydir)
appendset.add(ponydir)
appendset = set()
self.vtponydirs = []
_ponydirs = [self.HOME + '/.local/share/ponysay/ttyponies/', '/usr/share/ponysay/ttyponies/']
for ponydir in _ponydirs:
if os.path.isdir(ponydir) and (ponydir not in appendset):
self.vtponydirs.append(ponydir)
appendset.add(ponydir)
'''
The directories where pony files are stored, extrattyponies/ are used if the terminal is Linux VT (also known as TTY) and not with KMS
'''
appendset = set()
self.extraxponydirs = []
_extraponydirs = [self.HOME + '/.local/share/ponysay/extraponies/', '/usr/share/ponysay/extraponies/']
for extraponydir in _extraponydirs:
if os.path.isdir(extraponydir) and (extraponydir not in appendset):
self.extraxponydirs.append(extraponydir)
appendset.add(extraponydir)
appendset = set()
self.extravtponydirs = []
_extraponydirs = [self.HOME + '/.local/share/ponysay/extrattyponies/', '/usr/share/ponysay/extrattyponies/']
for extraponydir in _extraponydirs:
if os.path.isdir(extraponydir) and (extraponydir not in appendset):
self.extravtponydirs.append(extraponydir)
appendset.add(extraponydir)
'''
The directories where quotes files are stored
'''
appendset = set()
self.quotedirs = []
_quotedirs = [self.HOME + '/.local/share/ponysay/quotes/', '/usr/share/ponysay/quotes/']
for quotedir in _quotedirs:
if os.path.isdir(quotedir) and (quotedir not in appendset):
self.quotedirs.append(quotedir)
appendset.add(quotedir)
'''
The directories where balloon style files are stored
'''
appendset = set()
self.balloondirs = []
_balloondirs = [self.HOME + '/.local/share/ponysay/balloons/', '/usr/share/ponysay/balloons/']
for balloondir in _balloondirs:
if os.path.isdir(balloondir) and (balloondir not in appendset):
self.balloondirs.append(balloondir)
appendset.add(balloondir)
'''
ucsmap files
'''
appendset = set()
self.ucsmaps = []
_ucsmaps = [self.HOME + '/.local/share/ponysay/ucsmap', '/usr/share/ponysay/ucsmap']
for ucsmap in _ucsmaps:
if os.path.isdir(ucsmap) and (ucsmap not in appendset):
self.ucsmaps.append(ucsmap)
appendset.add(ucsmap)
''' '''
Starts the part of the program the arguments indicate Starts the part of the program the arguments indicate
@param args:ArgParser Parsed command line arguments @param args:ArgParser Parsed command line arguments
''' '''
def __init__(self, args): def run(self, args):
if (args.argcount == 0) and not pipelinein: if (args.argcount == 0) and not self.pipelinein:
args.help() args.help()
exit(254) exit(254)
return return
## Modifyable global variables
global linuxvt
global usekms
global mode
global ponydirs
global extraponydirs
## Emulate termial capabilities ## Emulate termial capabilities
if args.opts['-X'] is not None: (linuxvt, usekms) = (False, False) if args.opts['-X'] is not None: (self.linuxvt, self.usekms) = (False, False)
elif args.opts['-V'] is not None: (linuxvt, usekms) = (True, False) elif args.opts['-V'] is not None: (self.linuxvt, self.usekms) = (True, False)
elif args.opts['-K'] is not None: (linuxvt, usekms) = (True, True) elif args.opts['-K'] is not None: (self.linuxvt, self.usekms) = (True, True)
ponydirs = vtponydirs if linuxvt and not usekms else xponydirs self.ponydirs = self.vtponydirs if self.linuxvt and not self.usekms else self.xponydirs
extraponydirs = extravtponydirs if linuxvt and not usekms else extraxponydirs self.extraponydirs = self.extravtponydirs if self.linuxvt and not self.usekms else self.extraxponydirs
## Variadic variants of -f, +f and -q ## Variadic variants of -f, +f and -q
if args.opts['--f'] is not None: if args.opts['--f'] is not None:
@ -139,9 +274,9 @@ class Ponysay():
else: else:
## Colouring features ## Colouring features
if args.opts['--colour-pony'] is not None: if args.opts['--colour-pony'] is not None:
mode += '\033[' + ';'.join(args.opts['--colour-pony']) + 'm' self.mode += '\033[' + ';'.join(args.opts['--colour-pony']) + 'm'
else: else:
mode += '\033[0m' self.mode += '\033[0m'
if args.opts['+c'] is not None: if args.opts['+c'] is not None:
if args.opts['--colour-msg'] is None: args.opts['--colour-msg'] = args.opts['+c'] if args.opts['--colour-msg'] is None: args.opts['--colour-msg'] = args.opts['+c']
if args.opts['--colour-link'] is None: args.opts['--colour-link'] = args.opts['+c'] if args.opts['--colour-link'] is None: args.opts['--colour-link'] = args.opts['+c']
@ -152,7 +287,7 @@ class Ponysay():
self.__bestpony(args) self.__bestpony(args)
self.__ucsremap(args) self.__ucsremap(args)
if args.opts['-o'] is not None: if args.opts['-o'] is not None:
mode += '$/= $$\\= $' self.mode += '$/= $$\\= $'
args.message = '' args.message = ''
## The stuff ## The stuff
@ -167,7 +302,7 @@ class Ponysay():
self.quote(args) self.quote(args)
if warn: if warn:
printerr('-q cannot be used at the same time as -f or +f.') printerr('-q cannot be used at the same time as -f or +f.')
elif not unrecognised: elif not self.unrecognised:
self.print_pony(args) self.print_pony(args)
else: else:
args.help() args.help()
@ -187,10 +322,10 @@ class Ponysay():
def __extraponies(self, args = None): def __extraponies(self, args = None):
## If extraponies are used, change ponydir to extraponydir ## If extraponies are used, change ponydir to extraponydir
if args is None: if args is None:
ponydirs[:] = extraponydirs self.ponydirs[:] = self.extraponydirs
elif args.opts['+f'] is not None: elif args.opts['+f'] is not None:
args.opts['-f'] = args.opts['+f'] args.opts['-f'] = args.opts['+f']
ponydirs[:] = extraponydirs self.ponydirs[:] = self.extraponydirs
''' '''
@ -201,7 +336,7 @@ class Ponysay():
def __bestpony(self, args): def __bestpony(self, args):
## Set best.pony as the pony to display if none is selected ## Set best.pony as the pony to display if none is selected
if (args.opts['-f'] is None) or (args.opts['-q'] is None) or (len(args.opts['-q']) == 0): if (args.opts['-f'] is None) or (args.opts['-q'] is None) or (len(args.opts['-q']) == 0):
for ponydir in 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 pony = os.path.realpath(ponydir + 'best.pony') # Canonical path
args.opts['-f' if args.opts['-q'] is None else '-q'] = [pony] args.opts['-f' if args.opts['-q'] is None else '-q'] = [pony]
@ -226,7 +361,7 @@ class Ponysay():
## Read all lines in all UCS → ASCII map files ## Read all lines in all UCS → ASCII map files
maplines = [] maplines = []
for ucsmap in 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')]
@ -272,7 +407,7 @@ class Ponysay():
## Read all lines in all UCS → ASCII map files ## Read all lines in all UCS → ASCII map files
maplines = [] maplines = []
for ucsmap in 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')]
@ -311,7 +446,7 @@ class Ponysay():
ponies = {} ponies = {}
## List all pony files, without the .pony ending ## List all pony files, without the .pony ending
for ponydir in ponydirs: for ponydir in self.ponydirs:
for ponyfile in os.listdir(ponydir): for ponyfile in os.listdir(ponydir):
if endswith(ponyfile, ".pony"): if endswith(ponyfile, ".pony"):
pony = ponyfile[:-5] pony = ponyfile[:-5]
@ -332,7 +467,7 @@ class Ponysay():
pony = names[random.randrange(0, len(names))] pony = names[random.randrange(0, len(names))]
if pony not in ponies: if pony not in ponies:
if not alt: if not alt:
autocorrect = SpelloCorrecter(ponydirs, '.pony') autocorrect = SpelloCorrecter(self.ponydirs, '.pony')
(alternatives, dist) = autocorrect.correct(pony) (alternatives, dist) = autocorrect.correct(pony)
limit = os.environ['PONYSAY_TYPO_LIMIT'] if 'PONYSAY_TYPO_LIMIT' in os.environ else '' limit = os.environ['PONYSAY_TYPO_LIMIT'] if 'PONYSAY_TYPO_LIMIT' in os.environ else ''
limit = 5 if len(limit) == 0 else int(dist) limit = 5 if len(limit) == 0 else int(dist)
@ -354,7 +489,7 @@ class Ponysay():
quotes = [] quotes = []
quoteshash = set() quoteshash = set()
_quotes = [] _quotes = []
for quotedir in quotedirs: for quotedir in self.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:
if not quote == '': if not quote == '':
@ -364,7 +499,7 @@ class Ponysay():
## Create a set of all ponyes that have quotes ## Create a set of all ponyes that have quotes
ponies = set() ponies = set()
for ponydir in ponydirs: for ponydir in self.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 p = pony[:-5] # remove .pony
@ -384,12 +519,12 @@ class Ponysay():
def __quotes(self): def __quotes(self):
## Get all ponyquote files ## Get all ponyquote files
quotes = [] quotes = []
for quotedir in quotedirs: for quotedir in self.quotedirs:
quotes += [quotedir + item for item in os.listdir(quotedir)] quotes += [quotedir + item for item in os.listdir(quotedir)]
## Create list of all ponyquote file-pairs ## Create list of all ponyquote file-pairs
rc = [] rc = []
for ponydir in ponydirs: for ponydir in self.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 p = pony[:-5] # remove .pony
@ -483,7 +618,7 @@ class Ponysay():
## Get all quoters ## Get all quoters
quoters = self.__quoters() quoters = self.__quoters()
for ponydir in ponydirs: # Loop ponydirs for ponydir in self.ponydirs: # Loop ponydirs
## Get all ponies in the directory ## Get all ponies in the directory
_ponies = os.listdir(ponydir) _ponies = os.listdir(ponydir)
@ -511,7 +646,7 @@ class Ponysay():
termsize = self.__gettermsize() termsize = self.__gettermsize()
quoters = self.__quoters() quoters = self.__quoters()
for ponydir in ponydirs: # Loop ponydirs for ponydir in self.ponydirs: # Loop ponydirs
## Get all pony files in the directory ## Get all pony files in the directory
_ponies = os.listdir(ponydir) _ponies = os.listdir(ponydir)
@ -602,7 +737,7 @@ class Ponysay():
def onelist(self): def onelist(self):
## Get all pony files ## Get all pony files
_ponies = [] _ponies = []
for ponydir in ponydirs: # Loop ponydirs for ponydir in self.ponydirs: # Loop ponydirs
_ponies += os.listdir(ponydir) _ponies += os.listdir(ponydir)
## Remove .pony from all files and skip those that does not have .pony ## Remove .pony from all files and skip those that does not have .pony
@ -636,12 +771,12 @@ class Ponysay():
## Get all balloons ## Get all balloons
balloonset = set() balloonset = set()
for balloondir in balloondirs: for balloondir in self.balloondirs:
for balloon in os.listdir(balloondir): for balloon in os.listdir(balloondir):
## Use .think if running ponythink, otherwise .say ## Use .think if running ponythink, otherwise .say
if isthink and endswith(balloon, '.think'): if self.isthink and endswith(balloon, '.think'):
balloon = balloon[:-6] balloon = balloon[:-6]
elif (not isthink) and endswith(balloon, '.say'): elif (not self.isthink) and endswith(balloon, '.say'):
balloon = balloon[:-4] balloon = balloon[:-4]
else: else:
continue continue
@ -668,13 +803,13 @@ class Ponysay():
## Get all balloons ## Get all balloons
balloons = {} balloons = {}
for balloondir in balloondirs: for balloondir in self.balloondirs:
for balloon in os.listdir(balloondir): for balloon in os.listdir(balloondir):
balloonfile = balloon balloonfile = balloon
## Use .think if running ponythink, otherwise .say ## Use .think if running ponythink, otherwise .say
if isthink and endswith(balloon, '.think'): if self.isthink and endswith(balloon, '.think'):
balloon = balloon[:-6] balloon = balloon[:-6]
elif (not isthink) and endswith(balloon, '.say'): elif (not self.isthink) and endswith(balloon, '.say'):
balloon = balloon[:-4] balloon = balloon[:-4]
else: else:
continue continue
@ -692,7 +827,7 @@ class Ponysay():
balloon = names[random.randrange(0, len(names))] 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(balloondirs, '.think' if isthink else '.say') autocorrect = SpelloCorrecter(self.balloondirs, '.think' if self.isthink else '.say')
(alternatives, dist) = autocorrect.correct(balloon) (alternatives, dist) = autocorrect.correct(balloon)
limit = os.environ['PONYSAY_TYPO_LIMIT'] if 'PONYSAY_TYPO_LIMIT' in os.environ else '' limit = os.environ['PONYSAY_TYPO_LIMIT'] if 'PONYSAY_TYPO_LIMIT' in os.environ else ''
limit = 5 if len(limit) == 0 else int(dist) limit = 5 if len(limit) == 0 else int(dist)
@ -713,7 +848,7 @@ class Ponysay():
def __getballoon(self, balloonfile): def __getballoon(self, balloonfile):
## Use default balloon if none is specified ## Use default balloon if none is specified
if balloonfile is None: if balloonfile is None:
if isthink: if self.isthink:
return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ') return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ')
return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ') return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ')
@ -799,7 +934,7 @@ class Ponysay():
## Use PNG file as pony file ## Use PNG file as pony file
if endswith(pony.lower(), '.png'): if endswith(pony.lower(), '.png'):
pony = '\'' + pony.replace('\'', '\'\\\'\'') + '\'' pony = '\'' + pony.replace('\'', '\'\\\'\'') + '\''
pngcmd = ('img2ponysay -p -- ' if linuxvt else 'img2ponysay -- ') + pony pngcmd = ('img2ponysay -p -- ' if self.linuxvt else 'img2ponysay -- ') + pony
pngpipe = os.pipe() pngpipe = os.pipe()
Popen(pngcmd, stdout=os.fdopen(pngpipe[1], 'w'), shell=True).wait() Popen(pngcmd, stdout=os.fdopen(pngpipe[1], 'w'), shell=True).wait()
pony = '/proc/' + str(os.getpid()) + '/fd/' + str(pngpipe[0]) pony = '/proc/' + str(os.getpid()) + '/fd/' + str(pngpipe[0])
@ -808,7 +943,7 @@ class Ponysay():
pony = self.__kms(pony) pony = self.__kms(pony)
## 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 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
@ -849,8 +984,8 @@ class Ponysay():
## Run cowsay replacement ## Run cowsay replacement
backend = Backend(message = msg, ponyfile = pony, wrapcolumn = messagewrap, width = widthtruncation, backend = Backend(message = msg, ponyfile = pony, wrapcolumn = messagewrap, width = widthtruncation, balloon = balloon,
balloon = balloon, hyphen = hyphen, linkcolour = linkcolour, ballooncolour = ballooncolour) hyphen = hyphen, linkcolour = linkcolour, ballooncolour = ballooncolour, mode = self.mode)
backend.parse() backend.parse()
output = backend.output output = backend.output
if output.endswith('\n'): if output.endswith('\n'):
@ -869,7 +1004,7 @@ class Ponysay():
## Print the output, truncated on height is so set ## Print the output, truncated on height is so set
lines = self.__gettermsize()[0] - int(env_lines) lines = self.__gettermsize()[0] - int(env_lines)
if linuxvt or (env_height is ('yes', 'y', '1')): if self.linuxvt or (env_height is ('yes', 'y', '1')):
if env_bottom is ('yes', 'y', '1'): if env_bottom is ('yes', 'y', '1'):
for line in output.split('\n')[: -lines]: for line in output.split('\n')[: -lines]:
print(line) print(line)
@ -924,10 +1059,9 @@ class Ponysay():
''' '''
Identifies whether KMS support is utilised Identifies whether KMS support is utilised
''' '''
@staticmethod def isUsingKMS(self):
def isUsingKMS():
## KMS is not utilised if Linux VT is not used ## KMS is not utilised if Linux VT is not used
if not linuxvt: if not self.linuxvt:
return False return False
## Read the PONYSAY_KMS_PALETTE environment variable ## Read the PONYSAY_KMS_PALETTE environment variable
@ -953,7 +1087,7 @@ class Ponysay():
''' '''
def __kms(self, pony): def __kms(self, pony):
## If not in Linux VT, return the pony as is ## If not in Linux VT, return the pony as is
if not linuxvt: if not self.linuxvt:
return pony return pony
## KMS support version constant ## KMS support version constant
@ -982,7 +1116,7 @@ class Ponysay():
cachedir = '/var/cache/ponysay' cachedir = '/var/cache/ponysay'
shared = True shared = True
if not os.path.isdir(cachedir): if not os.path.isdir(cachedir):
cachedir = HOME + '/.cache/ponysay' cachedir = self.HOME + '/.cache/ponysay'
shared = False shared = False
if not os.path.isdir(cachedir): if not os.path.isdir(cachedir):
os.makedirs(cachedir) os.makedirs(cachedir)
@ -1081,6 +1215,7 @@ class ArgParser():
@param longdescription:str Long, multi-line, description of the program, may be `None` @param longdescription:str Long, multi-line, description of the program, may be `None`
''' '''
def __init__(self, program, description, usage, longdescription = None): def __init__(self, program, description, usage, longdescription = None):
self.linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux')
self.__program = program self.__program = program
self.__description = description self.__description = description
self.__usage = usage self.__usage = usage
@ -1259,7 +1394,7 @@ class ArgParser():
Prints a colourful help message Prints a colourful help message
''' '''
def help(self): def help(self):
print('\033[1m%s\033[21m %s %s' % (self.__program, '-' if linuxvt else '', self.__description)) print('\033[1m%s\033[21m %s %s' % (self.__program, '-' if self.linuxvt else '', self.__description))
print() print()
if self.__longdescription is not None: if self.__longdescription is not None:
print(self.__longdescription) print(self.__longdescription)
@ -1275,6 +1410,18 @@ class ArgParser():
print('\t%s' % (line)) print('\t%s' % (line))
print() print()
maxfirstlen = []
for opt in self.__arguments:
opt_alts = opt[1]
opt_help = opt[3]
if opt_help is None:
continue
first = opt_alts[0]
last = opt_alts[-1]
if first is not last:
maxfirstlen.append(first)
maxfirstlen = len(max(maxfirstlen, key = len))
print('\033[1mSYNOPSIS:\033[21m') print('\033[1mSYNOPSIS:\033[21m')
(lines, lens) = ([], []) (lines, lens) = ([], [])
for opt in self.__arguments: for opt in self.__arguments:
@ -1287,7 +1434,8 @@ class ArgParser():
(line, l) = ('', 0) (line, l) = ('', 0)
first = opt_alts[0] first = opt_alts[0]
last = opt_alts[-1] last = opt_alts[-1]
alts = ('', last) if first is last else (first, last) alts = ['', last] if first is last else [first, last]
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
@ -1436,8 +1584,9 @@ class Backend():
@param hyphen:str How hyphens added by the wordwrapper should be printed @param hyphen:str How hyphens added by the wordwrapper should be printed
@param linkcolour:str How to colour the link character, empty string if none @param linkcolour:str How to colour the link character, empty string if none
@param ballooncolour:str How to colour the balloon, empty string if none @param ballooncolour:str How to colour the balloon, empty string if none
@param mode:str Mode string for the pony
''' '''
def __init__(self, message, ponyfile, wrapcolumn, width, balloon, hyphen, linkcolour, ballooncolour): def __init__(self, message, ponyfile, wrapcolumn, width, balloon, hyphen, linkcolour, ballooncolour, mode):
self.message = message self.message = message
self.ponyfile = ponyfile self.ponyfile = ponyfile
self.wrapcolumn = None if wrapcolumn is None else wrapcolumn - (0 if balloon is None else balloon.minwidth) self.wrapcolumn = None if wrapcolumn is None else wrapcolumn - (0 if balloon is None else balloon.minwidth)
@ -1445,6 +1594,7 @@ class Backend():
self.balloon = balloon self.balloon = balloon
self.hyphen = hyphen self.hyphen = hyphen
self.ballooncolour = ballooncolour self.ballooncolour = ballooncolour
self.mode = mode
if self.balloon is not None: if self.balloon is not None:
self.link = {'\\' : linkcolour + self.balloon.link, self.link = {'\\' : linkcolour + self.balloon.link,
@ -1469,7 +1619,7 @@ class Backend():
infoend = self.pony.index('\n$$$\n') infoend = self.pony.index('\n$$$\n')
printinfo(self.pony[:infoend]) printinfo(self.pony[:infoend])
self.pony = self.pony[infoend + 5:] self.pony = self.pony[infoend + 5:]
self.pony = mode + self.pony self.pony = self.mode + self.pony
self.__processPony() self.__processPony()
self.__truncate() self.__truncate()
@ -2278,138 +2428,6 @@ class SpelloCorrecter(): # Naïvely and quickly proted and adapted from optimise
Start the program from ponysay.__init__ if this is the executed file Start the program from ponysay.__init__ if this is the executed file
''' '''
if __name__ == '__main__': if __name__ == '__main__':
'''
The user's home directory
'''
HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~')
## Change system enviroments with ~/.ponysayrc
if os.path.exists(HOME + '/.ponysayrc'):
with open(HOME + '/.ponysayrc', 'rb') as ponysayrc:
code = ponysayrc.read().decode('utf8', 'replace') + '\n'
env = os.environ
code = compile(code, HOME + '/.ponysayrc', 'exec')
exec(code)
HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~') # in case ~/.ponysayrc changes it
'''
Whether the program is execute in Linux VT (TTY)
'''
linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux')
'''
Whether the script is executed as ponythink
'''
isthink = (len(__file__) >= len('think')) and (__file__.endswith('think'))
isthink = ((len(__file__) >= len('think.py')) and (__file__.endswith('think.py'))) or isthink
'''
Whether stdin is piped
'''
pipelinein = not sys.stdin.isatty()
'''
Whether stdout is piped
'''
pipelineout = not sys.stdout.isatty()
'''
Whether stderr is piped
'''
pipelineerr = not sys.stderr.isatty()
'''
Whether KMS is used
'''
usekms = Ponysay.isUsingKMS()
'''
Mode string that modifies or adds $ variables in the pony image
'''
mode = ''
'''
The directories where pony files are stored, ttyponies/ are used if the terminal is Linux VT (also known as TTY) and not with KMS
'''
appendset = set()
xponydirs = []
_ponydirs = [HOME + '/.local/share/ponysay/ponies/', '/usr/share/ponysay/ponies/']
for ponydir in _ponydirs:
if os.path.isdir(ponydir) and (ponydir not in appendset):
xponydirs.append(ponydir)
appendset.add(ponydir)
appendset = set()
vtponydirs = []
_ponydirs = [HOME + '/.local/share/ponysay/ttyponies/', '/usr/share/ponysay/ttyponies/']
for ponydir in _ponydirs:
if os.path.isdir(ponydir) and (ponydir not in appendset):
vtponydirs.append(ponydir)
appendset.add(ponydir)
'''
The directories where pony files are stored, extrattyponies/ are used if the terminal is Linux VT (also known as TTY) and not with KMS
'''
appendset = set()
extraxponydirs = []
_extraponydirs = [HOME + '/.local/share/ponysay/extraponies/', '/usr/share/ponysay/extraponies/']
for extraponydir in _extraponydirs:
if os.path.isdir(extraponydir) and (extraponydir not in appendset):
extraxponydirs.append(extraponydir)
appendset.add(extraponydir)
appendset = set()
extravtponydirs = []
_extraponydirs = [HOME + '/.local/share/ponysay/extrattyponies/', '/usr/share/ponysay/extrattyponies/']
for extraponydir in _extraponydirs:
if os.path.isdir(extraponydir) and (extraponydir not in appendset):
extravtponydirs.append(extraponydir)
appendset.add(extraponydir)
'''
The directories where quotes files are stored
'''
appendset = set()
quotedirs = []
_quotedirs = [HOME + '/.local/share/ponysay/quotes/', '/usr/share/ponysay/quotes/']
for quotedir in _quotedirs:
if os.path.isdir(quotedir) and (quotedir not in appendset):
quotedirs.append(quotedir)
appendset.add(quotedir)
'''
The directories where balloon style files are stored
'''
appendset = set()
balloondirs = []
_balloondirs = [HOME + '/.local/share/ponysay/balloons/', '/usr/share/ponysay/balloons/']
for balloondir in _balloondirs:
if os.path.isdir(balloondir) and (balloondir not in appendset):
balloondirs.append(balloondir)
appendset.add(balloondir)
'''
ucsmap files
'''
appendset = set()
ucsmaps = []
_ucsmaps = [HOME + '/.local/share/ponysay/ucsmap', '/usr/share/ponysay/ucsmap']
for ucsmap in _ucsmaps:
if os.path.isdir(ucsmap) and (ucsmap not in appendset):
ucsmaps.append(ucsmap)
appendset.add(ucsmap)
usage_saythink = '\033[34;1m(ponysay | ponythink)\033[21;39m' usage_saythink = '\033[34;1m(ponysay | ponythink)\033[21;39m'
usage_common = '[-c] [-W\033[4mCOLUMN\033[24m] [-b\033[4mSTYLE\033[24m]' usage_common = '[-c] [-W\033[4mCOLUMN\033[24m] [-b\033[4mSTYLE\033[24m]'
usage_listhelp = '(-l | -L | -B | +l | +L | -A | + A | -v | -h)' usage_listhelp = '(-l | -L | -B | +l | +L | -A | + A | -v | -h)'
@ -2481,4 +2499,6 @@ run `man ponysay`. Ponysay has so much more to offer than described here.''')
## Start ## Start
Ponysay(opts) ponysay = Ponysay()
ponysay.unrecognised = unrecognised
ponysay.run(opts)