This commit is contained in:
Pablo Lezaeta 2012-10-25 00:57:06 -03:00
commit ed4e30e3f5
7 changed files with 377 additions and 180 deletions

View file

@ -2,6 +2,19 @@ Version 2.9
New extraponies: molestia (Tumblr) New extraponies: molestia (Tumblr)
The option -q works like -f and -F, it takes one argument, and may be used multiple
times for more arguments.
The old option -q is renamed to --q.
The options --f and --F has been added.
Weighted distance for autocorrection on pony names and boolean style name is set to 5, rather
than unlimited. Currently this cannot be modified (without editing the source code.)
If file descriptor 3 is definied when ponysay is executed, extra information is printed to it.
Version 2.8 Version 2.8
New ponies: airheart, bastionyorsets, gustavelegrand, milkyway, peppermoon, pinkacopter, pinkiefly, New ponies: airheart, bastionyorsets, gustavelegrand, milkyway, peppermoon, pinkacopter, pinkiefly,

View file

@ -53,8 +53,8 @@ Now every time you open a terminal a pony should give your fortune
### Pony quotes ### Pony quotes
Running `ponysay -q` will give you a random pony saying one it its quote from MLP:FiM. Add one or more argument after `-q` to sepecify a Running `ponysay -q` will give you a random pony saying one it its quote from MLP:FiM.
set of ponies from which one will be selected randomly. Just as with `-`f, `-q` can be used multiple time to to sepecify a set of ponies from which one will be selected randomly.
When running `ponysay -l` or `ponysay -L` the ponies which have quotes will be printed bold or bright (depending on terminal). When running `ponysay -l` or `ponysay -L` the ponies which have quotes will be printed bold or bright (depending on terminal).

View file

@ -6,7 +6,7 @@ s:-
w:| w:|
e: | e: |
ww:< ww:<
ee: > ee: >
nw: _ nw: _

View file

@ -23,13 +23,14 @@ ro=0
(hash makeinfo 2>/dev/null) || (bo=1 ; echo 'Missing makeinfo, install texinfo [build optional]') (hash makeinfo 2>/dev/null) || (bo=1 ; echo 'Missing makeinfo, install texinfo [build optional]')
(hash install-info 2>/dev/null) || (bo=1 ; echo 'Missing install-info, install info [build optional]') (hash install-info 2>/dev/null) || (bo=1 ; echo 'Missing install-info, install info [build optional]')
(hash stty 2>/dev/null) || (rr=1 ; echo 'Missing stty, install coreutils [runtime required]') (hash python 2>/dev/null) || (rr=1 ; echo 'Missing python, install python>=3 [build+runtime required]')
(hash python 2>/dev/null) || (rr=1 ; echo 'Missing python, install python>=3 [runtime required]')
(hash cut 2>/dev/null) && (hash python 2>/dev/null) && (hash cut 2>/dev/null) && (hash python 2>/dev/null) &&
(test ! $(env python --version 2>&1 | cut -d ' ' -f 2 | cut -d '.' -f 1) = 3) && ( (test ! $(env python --version 2>&1 | cut -d ' ' -f 2 | cut -d '.' -f 1) = 3) && (
(hash python3 2>/dev/null) || (hash python3 2>/dev/null) ||
(rr=1 ; echo 'Missing python>=3, install python (may be named python3) [runtime required]')) (rr=1 ; echo 'Missing python>=3, install python (may be named python3) [build+runtime required]'))
(hash stty 2>/dev/null) || (rr=1 ; echo 'Missing stty, install coreutils [runtime required]')
(hash tty2colourfultty 2>/dev/null) || (ro=1 ; echo 'Missing tty2colourfultty, install util-say [runtime optional]') (hash tty2colourfultty 2>/dev/null) || (ro=1 ; echo 'Missing tty2colourfultty, install util-say [runtime optional]')
(hash ponysay2ttyponysay 2>/dev/null) || (ro=1 ; echo 'Missing ponysay2ttyponysay, install util-say [runtime optional]') (hash ponysay2ttyponysay 2>/dev/null) || (ro=1 ; echo 'Missing ponysay2ttyponysay, install util-say [runtime optional]')

View file

@ -76,8 +76,8 @@ multiple times, and one of the will be selected randomly.
.B \-q, \-\-quote [\fIname\fP...] .B \-q, \-\-quote [\fIname\fP...]
By using this option, a pony will be printed with quotes from her in My Little Pony: By using this option, a pony will be printed with quotes from her in My Little Pony:
Friendship is Magic. The pony will be selected randomly, unless at least one pony Friendship is Magic. The pony will be selected randomly, unless at least one pony
is added as an argument after \fI-q\fP. If one or more ponies are added after \fI-q\fP is added as an argument to \fI-q\fP. If one or more ponies are added as an argument
the pony will be selected randomly from that set of ponies. to \fI-q\fP the pony will be selected randomly from that set of ponies.
.TP .TP
.B \-W, \-\-wrap \fIcolumn\fP .B \-W, \-\-wrap \fIcolumn\fP
The screen column where the message should be wrapped. The screen column where the message should be wrapped.

View file

@ -116,7 +116,7 @@ way over e-mail.
@cindex invoking @cindex invoking
@cindex options @cindex options
@cindex arguments @cindex arguments
@pindex ponythink @pindex @command{ponythink}
The format for running the @command{ponysay} program is: The format for running the @command{ponysay} program is:
@ -172,7 +172,7 @@ In versions earlier than version 2.0, the if the pony were a file name it had to
include a `@code{/}'. This is not longer required and any existing pony name include a `@code{/}'. This is not longer required and any existing pony name
supersedes file names. supersedes file names.
@item -F @item -F PONY
@itemx ++file PONY @itemx ++file PONY
@itemx ++pony PONY @itemx ++pony PONY
@opindex @option{-F} @opindex @option{-F}
@ -181,6 +181,54 @@ supersedes file names.
Just as @option{-F}, but it uses extra (non-MLP:FiM) ponies instead of standard Just as @option{-F}, but it uses extra (non-MLP:FiM) ponies instead of standard
(MLP:FiM) ponies (MLP:FiM) ponies
@item -q PONY
@itemx --quote PONY
@opindex @option{-q}
@opindex @option{--quote}
@cindex quotes
@cindex pony quotes
By using this option, a pony will be printed with quotes from her in My Little Pony:
Friendship is Magic. The pony will be selected randomly, unless at least one pony
is added as an argument to @option{-q}. If one or more ponies are added as an argument
to @option{-q}, the pony will be selected randomly from that set of ponies.
This option requires the extension @command{ponyquotes4ponysay}, which is included
by default since version 1.2.
The argument can be a file name, but only if it ends with @file{.pony}.
@item --f [PONY...]
@itemx --files [PONY...]
@itemx --ponies [PONY...]
@opindex @option{--f}
@opindex @option{--files}
@opindex @option{--ponies}
Variadic variant of @option{-f}, meaning that all arguments added after this one
will parsed as an argument to this option. Additionally, those options are added
to @option{-f}.
@item --F [PONY...]
@itemx ++files [PONY...]
@itemx ++ponies [PONY...]
@opindex @option{--F}
@opindex @option{++files}
@opindex @option{++ponies}
Variadic variant of @option{-F}, meaning that all arguments added after this one
will parsed as an argument to this option. Additionally, those options are added
to @option{-F}.
@item --q [PONY...]
@itemx --quotes [PONY...]
@opindex @option{--q}
@opindex @option{--quotes}
@cindex quotes
@cindex pony quotes
Variadic variant of @option{-q}, meaning that all arguments added after this one
will parsed as an argument to this option. Additionally, those options are added
to @option{-q}.
An important feature of this options, is that you can but it in the end of the
command line, without any argument to get a quote from any pony with a quote.
@item -b STYLE @item -b STYLE
@itemx --bubble STYLE @itemx --bubble STYLE
@itemx --balloon STYLE @itemx --balloon STYLE
@ -192,19 +240,6 @@ balloon name printed by @option{ponysay -B}. This option can be used multiple
times to specify a set of styles from which one will be selected randomly. If no times to specify a set of styles from which one will be selected randomly. If no
balloon style is specified a fallback style will be used. balloon style is specified a fallback style will be used.
@item -q [PONY...]
@itemx --quote [PONY...]
@opindex @option{-q}
@opindex @option{--quote}
By using this option, a pony will be printed with quotes from her in My Little Pony:
Friendship is Magic. The pony will be selected randomly, unless at least one pony
is added as an argument after @option{-q}. If one or more ponies are added after
@option{-q}, the pony will be selected randomly from that set of ponies.
This option requires the extension @command{ponyquotes4ponysay}, which is included
by default since version 1.2.
The argument can be a file name, but only if it ends with @file{.pony}.
@item -W COLUMN @item -W COLUMN
@itemx --wrap COLUMN @itemx --wrap COLUMN
@opindex @option{-W} @opindex @option{-W}
@ -289,7 +324,8 @@ The first list are the MLP:FiM and the second one are non-MLP:FiM.
@opindex @option{-o} @opindex @option{-o}
@opindex @option{--pony-only} @opindex @option{--pony-only}
@opindex @option{--ponyonly} @opindex @option{--ponyonly}
Print just the pony, nothing else like the speech balloon. Print just the pony, nothing else like the speech balloon. Naturally the
@command{ponysay} will not wait for a message from stdin.
@item -X @item -X
@itemx --256-colours @itemx --256-colours
@ -376,11 +412,13 @@ this file should be a symbolic link to the pony you want as a default. If it is
a symbolic link, @option{-q} cannot determine which quotes to use. a symbolic link, @option{-q} cannot determine which quotes to use.
@node Advanced usage @node Advanced usage
@chapter Advanced usage of @command{ponysay}. @chapter Advanced usage of @command{ponysay}.
@cindex advanced usage @cindex advanced usage
@menu @menu
* Extra information:: Displaying extra information.
* Fortune cookies:: Displaying with fortune cookies. * Fortune cookies:: Displaying with fortune cookies.
* Ponification:: Ponify your fortune cookies. * Ponification:: Ponify your fortune cookies.
* Running on TTY:: Running on TTY (Linux VT). * Running on TTY:: Running on TTY (Linux VT).
@ -388,9 +426,28 @@ a symbolic link, @option{-q} cannot determine which quotes to use.
@end menu @end menu
@node Extra information
@section Extra information
@cindex file descriptor 3
@cindex extra information
@cindex verbose mode
@pindex @command{tee}
If file descriptor 3 is definied when @command{ponysay} is executed, extra information
is printed to it. The printed information includes the name of the pony file, the name
of the balloon style file, and if definied in the pony file, file meta data and comment.
In most shells, a file descriptor 3 can defined using @command{3> FILE}, and linked to
stderr using @command{3>&2}. For example, you can print the information to @file{~/info}
by running @command{ponysay I\'m just the cutest pony! | 3> ~/info}.
The message is not stored this way, for that you can use @command{tee}. However, if you
use @option{-q} the quote file is printed to file descriptor 3.
@node Fortune cookies @node Fortune cookies
@section Fortune cookies @section Fortune cookies
@pindex fortune @pindex @command{fortune}
@cindex startup @cindex startup
@cindex on startup @cindex on startup
@cindex @file{.bashrc} @cindex @file{.bashrc}
@ -410,7 +467,7 @@ described in the previous paragraph every time you open a terminal.
@section Ponification @section Ponification
@cindex ponification @cindex ponification
@cindex text ponification @cindex text ponification
@pindex ponypipe @pindex @command{ponypipe}
You can ponify messages (i.e. replaces words search as `everyone' with `everypony') by You can ponify messages (i.e. replaces words search as `everyone' with `everypony') by
using @code{fortune | ponypipe} instead of using @command{fortune}. @command{ponypipe} using @code{fortune | ponypipe} instead of using @command{fortune}. @command{ponypipe}
@ -447,7 +504,7 @@ You should read more about this in @ref{KMS ponies}.
@node Running on screen @node Running on screen
@section Running on @command{screen} @section Running on @command{screen}
@pindex screen @pindex @command{screen}
@cindex @file{.bashrc} @cindex @file{.bashrc}
@cindex @file{~/.bashrc} @cindex @file{~/.bashrc}
@ -667,7 +724,7 @@ run on it.
@node Cowsay @node Cowsay
@section Cowsay @section Cowsay
@pindex cowsay @pindex @command{cowsay}
This section describes the limitation of @command{cowsay}, but since version 2.1 This section describes the limitation of @command{cowsay}, but since version 2.1
@command{cowsay} is no longer used because of it. So none of the following limitations @command{cowsay} is no longer used because of it. So none of the following limitations
@ -1951,8 +2008,22 @@ sequences.
@itemize @bullet @itemize @bullet
@item @item
New extraponies: @file{molestia} (Tumblr) New extraponies: @file{molestia} (Tumblr)
@item
The option @option{-q} works like @option{-f} and @option{-F}, it takes one argument, and
may be used multiple times for more arguments.
@item
The old option @option{-q} is renamed to @option{--q}.
@item
The options @option{--f} and @option{--F} has been added.
@item
Weighted distance for autocorrection on pony names and boolean style name is set to 5, rather
than unlimited. Currently this cannot be modified (without editing the source code.)
@item
If file descriptor 3 is definied when @command{ponysay} is executed, extra information is
printed to it.
@end itemize @end itemize
@heading Version 2.8 @heading Version 2.8
@itemize @bullet @itemize @bullet
@item @item

View file

@ -47,6 +47,29 @@ programs by default, report them to Princess Celestia so she can banish them to
def print(text = '', end = '\n'): def print(text = '', end = '\n'):
sys.stdout.buffer.write((str(text) + end).encode('utf-8')) 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'))
fd3 = None
'''
/proc/self/fd/3 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 printinfo(text = '', end = '\n'):
global fd3
if os.path.exists('/proc/self/fd/3'):
if fd3 is None:
fd3 = os.fdopen(3, 'w')
fd3.write(str(text) + end)
''' '''
Checks whether a text ends with a specific text, but has more Checks whether a text ends with a specific text, but has more
@ -56,7 +79,7 @@ Checks whether a text ends with a specific text, but has more
@return :bool The result of the test @return :bool The result of the test
''' '''
def endswith(text, ending): def endswith(text, ending):
return text.endswith(ending) and not (text == ending); return text.endswith(ending) and not (text == ending)
@ -72,6 +95,7 @@ class Ponysay():
def __init__(self, args): def __init__(self, args):
if (args.argcount == 0) and not pipelinein: if (args.argcount == 0) and not pipelinein:
args.help() args.help()
exit(254)
return return
## Modifyable global variables ## Modifyable global variables
@ -82,18 +106,23 @@ class Ponysay():
global extraponydirs global extraponydirs
## Emulate termial capabilities ## Emulate termial capabilities
if args.opts['-X'] is not None: if args.opts['-X'] is not None: (linuxvt, usekms) = (False, False)
linuxvt = False elif args.opts['-V'] is not None: (linuxvt, usekms) = (True, False)
usekms = False elif args.opts['-K'] is not None: (linuxvt, usekms) = (True, True)
elif args.opts['-V'] is not None:
linuxvt = True
usekms = False
elif args.opts['-K'] is not None:
linuxvt = True
usekms = True
ponydirs = vtponydirs if linuxvt and not usekms else xponydirs ponydirs = vtponydirs if linuxvt and not usekms else xponydirs
extraponydirs = extravtponydirs if linuxvt and not usekms else extraxponydirs extraponydirs = extravtponydirs if linuxvt and not usekms else extraxponydirs
## Variadic variants of -f, -F and -q
if args.opts['--f'] is not None:
if args.opts['-f'] is not None: args.opts['-f'] += args.opts['--f']
else: args.opts['-f'] = args.opts['--f']
if args.opts['--F'] is not None:
if args.opts['-F'] is not None: args.opts['-F'] += args.opts['--F']
else: args.opts['-F'] = args.opts['--F']
if args.opts['--q'] is not None:
if args.opts['-q'] is not None: args.opts['-q'] += args.opts['--q']
else: args.opts['-q'] = args.opts['--q']
## Run modes ## Run modes
if args.opts['-h'] is not None: args.help() if args.opts['-h'] is not None: args.help()
elif args.opts['--quoters'] is not None: self.quoters() elif args.opts['--quoters'] is not None: self.quoters()
@ -124,10 +153,26 @@ class Ponysay():
self.__ucsremap(args) self.__ucsremap(args)
if args.opts['-o'] is not None: if args.opts['-o'] is not None:
mode += '$/= $$\\= $' mode += '$/= $$\\= $'
args.message = ''
## The stuff ## The stuff
if args.opts['-q'] is not None: self.quote(args) if args.opts['-q'] is not None:
else: self.print_pony(args) warn = (args.opts['-f'] is not None) or (args.opts['-F'] is not None)
if (len(args.opts['-q']) == 1) and ((args.opts['-q'][0] == '-f') or (args.opts['-q'][0] == '-F')):
warn = True
if args.opts['-q'][0] == '-f':
args.opts['-q'] = args.files
if args.opts['-f'] is not None:
args.opts['-q'] += args.opts['-f']
self.quote(args)
if warn:
printerr('-q cannot be used at the same time as -f or -F.')
elif not unrecognised:
self.print_pony(args)
else:
args.help()
exit(255)
return
############################################## ##############################################
@ -289,7 +334,7 @@ class Ponysay():
if not alt: if not alt:
autocorrect = SpelloCorrecter(ponydirs, '.pony') autocorrect = SpelloCorrecter(ponydirs, '.pony')
(alternatives, dist) = autocorrect.correct(pony) (alternatives, dist) = autocorrect.correct(pony)
if len(alternatives) > 0: if (len(alternatives) > 0) and (dist <= 5): # TODO the limit `dist` should be configureable
return self.__getponypath(alternatives, True) return self.__getponypath(alternatives, True)
sys.stderr.write('I have never heard of anypony named %s\n' % (pony)); sys.stderr.write('I have never heard of anypony named %s\n' % (pony));
exit(1) exit(1)
@ -362,10 +407,13 @@ class Ponysay():
''' '''
def __gettermsize(self): def __gettermsize(self):
## Call `stty` to determine the size of the terminal, this way is better then using python's ncurses ## Call `stty` to determine the size of the terminal, this way is better then using python's ncurses
termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=sys.stderr).communicate()[0] for channel in (sys.stderr, sys.stdout, sys.stdin):
termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0]
termsize = [int(item) for item in termsize] if len(termsize) > 0:
return termsize termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n
termsize = [int(item) for item in termsize]
return termsize
return (24, 80) # fall back to minimal sane size
@ -642,9 +690,9 @@ class Ponysay():
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(balloondirs, '.think' if isthink else '.say')
alternatives = autocorrect.correct(balloon)[0] (alternatives, dist) = autocorrect.correct(balloon)
if len(alternatives) > 0: if (len(alternatives) > 0) and (dist <= 5): # TODO the limit `dist` should be configureable
return self.__getponypath(alternatives, True) return self.__getballoonpath(alternatives, True)
sys.stderr.write('That balloon style %s does not exist\n' % (balloon)); sys.stderr.write('That balloon style %s does not exist\n' % (balloon));
exit(1) exit(1)
else: else:
@ -661,8 +709,8 @@ class Ponysay():
## 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 isthink:
return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ') return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ')
return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ') return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ')
## Initialise map for balloon parts ## Initialise map for balloon parts
map = {} map = {}
@ -671,7 +719,8 @@ class Ponysay():
## Read all lines in the balloon file ## Read all lines in the balloon file
with open(balloonfile, 'rb') as balloonstream: with open(balloonfile, 'rb') as balloonstream:
data = [line.replace('\n', '') for line in balloonstream.read().decode('utf8', 'replace').split('\n')] data = balloonstream.read().decode('utf8', 'replace')
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 last = None
@ -740,6 +789,7 @@ class Ponysay():
## Get the pony ## Get the pony
pony = self.__getponypath(args.opts['-f']) pony = self.__getponypath(args.opts['-f'])
printinfo('pony file: ' + pony)
## Use PNG file as pony file ## Use PNG file as pony file
if endswith(pony.lower(), '.png'): if endswith(pony.lower(), '.png'):
@ -763,7 +813,9 @@ class Ponysay():
messagewrap = int(args.opts['-W'][0]) if args.opts['-W'] is not None else None messagewrap = int(args.opts['-W'][0]) if args.opts['-W'] is not None else None
## Get balloon object ## Get balloon object
balloon = self.__getballoon(self.__getballoonpath(args.opts['-b'])) if args.opts['-o'] is None else None balloonfile = self.__getballoonpath(args.opts['-b'])
printinfo('balloon style file: ' + str(balloonfile))
balloon = self.__getballoon(balloonfile) if args.opts['-o'] is None else None
## Get hyphen style ## Get hyphen style
hyphencolour = '' hyphencolour = ''
@ -839,6 +891,7 @@ class Ponysay():
## Select a random ponyquote-pair, load it and print it ## Select a random ponyquote-pair, load it and print it
if not len(pairs) == 0: if not len(pairs) == 0:
pair = pairs[random.randrange(0, len(pairs))] pair = pairs[random.randrange(0, len(pairs))]
printinfo('quote file: ' + pair[1])
with open(pair[1], 'rb') as qfile: with open(pair[1], 'rb') as qfile:
args.message = qfile.read().decode('utf8', 'replace').strip() args.message = qfile.read().decode('utf8', 'replace').strip()
args.opts['-f'] = [pair[0]] args.opts['-f'] = [pair[0]]
@ -1067,7 +1120,8 @@ class ArgParser():
''' '''
Parse arguments Parse arguments
@param args:list<str> The command line arguments, should include the execute file at index 0, `sys.argv` is default @param args:list<str> The command line arguments, should include the execute file at index 0, `sys.argv` is default
@return :bool Whether no unrecognised option is used
''' '''
def parse(self, argv = sys.argv): def parse(self, argv = sys.argv):
self.argcount = len(argv) - 1 self.argcount = len(argv) - 1
@ -1083,9 +1137,11 @@ class ArgParser():
tmpdashed = False tmpdashed = False
get = 0 get = 0
dontget = 0 dontget = 0
self.rc = True
def unrecognised(arg): def unrecognised(arg):
sys.stderr.write('%s: warning: unrecognised option %s\n' % (self.__program, arg)) sys.stderr.write('%s: warning: unrecognised option %s\n' % (self.__program, arg))
self.rc = False
while len(deque) != 0: while len(deque) != 0:
arg = deque[0] arg = deque[0]
@ -1178,6 +1234,8 @@ class ArgParser():
break break
self.message = ' '.join(self.files) if len(self.files) > 0 else None self.message = ' '.join(self.files) if len(self.files) > 0 else None
return self.rc
''' '''
@ -1294,7 +1352,7 @@ class Balloon():
minN = len(max([ne, nne, n, nnw, nw], key = len)) minN = len(max([ne, nne, n, nnw, nw], key = len))
minS = len(max([se, sse, s, ssw, sw], key = len)) minS = len(max([se, sse, s, ssw, sw], key = len))
self.minwidth = minE + minE self.minwidth = minE + minE
self.minheight = minN + minS self.minheight = minN + minS
@ -1326,7 +1384,7 @@ class 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]) outer = UCS.dispLen(self.nw[j]) + UCS.dispLen(self.ne[j])
inner = UCS.dispLen(self.nnw[j]) + UCS.dispLen(self.nne[j]) 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:
rc.append(self.nw[j] + self.n[j] * (w - outer) + self.ne[j]) rc.append(self.nw[j] + self.n[j] * (w - outer) + self.ne[j])
@ -1337,7 +1395,7 @@ class 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]) outer = UCS.dispLen(self.sw[j]) + UCS.dispLen(self.se[j])
inner = UCS.dispLen(self.ssw[j]) + UCS.dispLen(self.sse[j]) 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:
rc.append(self.sw[j] + self.s[j] * (w - outer) + self.se[j]) rc.append(self.sw[j] + self.s[j] * (w - outer) + self.se[j])
@ -1365,7 +1423,7 @@ class Backend():
def __init__(self, message, ponyfile, wrapcolumn, width, balloon, hyphen, linkcolour, ballooncolour): def __init__(self, message, ponyfile, wrapcolumn, width, balloon, hyphen, linkcolour, ballooncolour):
self.message = message self.message = message
self.ponyfile = ponyfile self.ponyfile = ponyfile
self.wrapcolumn = wrapcolumn self.wrapcolumn = None if wrapcolumn is None else wrapcolumn - balloon.minwidth
self.width = width self.width = width
self.balloon = balloon self.balloon = balloon
self.hyphen = hyphen self.hyphen = hyphen
@ -1386,12 +1444,41 @@ class Backend():
''' '''
def parse(self): def parse(self):
self.__expandMessage() self.__expandMessage()
self.__unpadMessage()
self.__loadFile() self.__loadFile()
if self.pony.startswith('$$$\n'):
self.pony = self.pony[4:]
infoend = self.pony.index('\n$$$\n')
printinfo(self.pony[:infoend])
self.pony = self.pony[infoend + 5:]
self.pony = mode + self.pony self.pony = mode + self.pony
self.__processPony() self.__processPony()
self.__truncate() self.__truncate()
'''
Remove padding spaces fortune cookies are padded with whitespace (damn featherbrains)
'''
def __unpadMessage(self):
lines = self.message.split('\n')
for spaces in (8, 4, 2, 1):
padded = True
for line in lines:
if not line.startswith(' ' * spaces):
padded = False
break
if padded:
for i in range(0, len(lines)):
line = lines[i]
while line.startswith(' ' * spaces):
line = line[spaces:]
lines[i] = line
lines = [line.rstrip(' ') for line in lines]
self.message = '\n'.join(lines)
''' '''
Converts all tabs in the message to spaces by expanding Converts all tabs in the message to spaces by expanding
''' '''
@ -1475,10 +1562,10 @@ class Backend():
while i < n: while i < n:
c = self.pony[i] c = self.pony[i]
if c == '\t': if c == '\t':
n += 8 - (indent & 7) n += 7 - (indent & 7)
ed = ' ' * (7 - (indent & 7)) ed = ' ' * (8 - (indent & 7))
c = ' ' c = ' '
self.pony = self.pony[:i] + ed + self.pony[i:] 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:
@ -1669,131 +1756,149 @@ class Backend():
@return :str The message wrapped @return :str The message wrapped
''' '''
def __wrapMessage(self, message, wrap): def __wrapMessage(self, message, wrap):
AUTO_PUSH = '\033[0101y0~'
AUTO_POP = '\033[10101~'
msg = message.replace('\n', AUTO_PUSH + '\n' + AUTO_POP);
cstack = ColourStack(AUTO_PUSH, AUTO_POP)
buf = '' buf = ''
for c in msg: try:
buf += c + cstack.feed(c) AUTO_PUSH = '\033[01010~'
lines = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n') AUTO_POP = '\033[10101~'
buf = '' msg = message.replace('\n', AUTO_PUSH + '\n' + AUTO_POP);
for line in lines: cstack = ColourStack(AUTO_PUSH, AUTO_POP)
b = [None] * len(line) for c in msg:
map = {} buf += c + cstack.feed(c)
(bi, cols, w) = (0, 0, wrap) lines = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n')
(indent, indentc) = (-1, 0) buf = ''
(i, n) = (0, len(line)) for line in lines:
while i <= n: b = [None] * len(line)
d = None map = {0 : 0}
if i < n: (bi, cols, w) = (0, 0, wrap)
d = line[i] (indent, indentc) = (-1, 0)
i += 1
if d == '\033': # TODO this should use self.__getcolour() (i, n) = (0, len(line))
## Invisible stuff while i <= n:
b[bi] = d d = None
bi += 1 if i < n:
b[bi] = line[i] d = line[i]
d = line[i]
bi += 1
i += 1 i += 1
if d == '[': if d == '\033': # TODO this should use self.__getcolour()
while True: ## Invisible stuff
b[bi] = line[i] b[bi] = d
d = line[i] bi += 1
bi += 1
i += 1
if (('a' <= d) and (d <= 'z')) or (('A' <= d) and (d <= 'Z')) or (d == '~'):
break
elif d == ']':
b[bi] = line[i] b[bi] = line[i]
d = line[i] d = line[i]
bi += 1 bi += 1
i += 1 i += 1
if d == 'P': if d == '[':
for j in range(0, 7): while True:
b[bi] = line[i] b[bi] = line[i]
d = line[i]
bi += 1 bi += 1
i += 1 i += 1
elif (d is not None) and (d != ' '): if (('a' <= d) and (d <= 'z')) or (('A' <= d) and (d <= 'Z')) or (d == '~'):
## Fetch word break
if indent == -1: elif d == ']':
indent = i - 1 b[bi] = line[i]
for j in range(0, indent): d = line[i]
if line[j] == ' ': bi += 1
indentc += 1 i += 1
b[bi] = d if d == 'P':
bi += 1 for j in range(0, 7):
if (not UCS.isCombining(d)) and (d != '­'): b[bi] = line[i]
cols += 1 bi += 1
map[cols] = bi i += 1
else: elif (d is not None) and (d != ' '):
## Wrap? ## Fetch word
mm = 0 if indent == -1:
bisub = 0 indent = i - 1
iwrap = wrap - (0 if indent == 1 else indentc) for j in range(0, indent):
if line[j] == ' ':
while ((w > 8) and (cols > w + 5)) or (cols > iwrap): # TODO make configurable indentc += 1
## wrap b[bi] = d
x = w; bi += 1
nbsp = b[map[mm + x]] == ' ' if (not UCS.isCombining(d)) and (d != '­'):
m = map[mm + x] cols += 1
map[cols] = bi
if ('­' in b[bisub : m]) and not nbsp:
hyphen = m - 1
while b[hyphen] != '­':
hyphen -= 1
while map[mm + x] > hyphen: ## Only looking backward, if foreward is required the word is probabily not hyphenated correctly
x -= 1
x += 1
m = map[mm + x]
mm += x - (0 if nbsp else 1) ## 1 so we have space for a hythen
for bb in b[bisub : m]:
buf += bb
buf += '\n' if nbsp else '\0\n'
cols -= x - (0 if nbsp else 1)
bisub = m
w = iwrap
if indent != -1:
buf += line[:indent]
for j in range(bisub, bi):
b[j - bisub] = b[j]
bi -= bisub
if cols > w:
buf += '\n'
w = wrap
if indent != -1:
buf += line[:indent]
w -= indentc
for bb in b[:bi]:
buf += bb
w -= cols
cols = 0
bi = 0
if d is None:
i += 1
else: else:
if w > 0: ## Wrap?
buf += ' ' mm = 0
w -= 1 bisub = 0
else: iwrap = wrap - (0 if indent == 1 else indentc)
while ((w > 8) and (cols > w + 5)) or (cols > iwrap): # TODO make configurable
## wrap
x = w;
if mm + x not in map: # Too much whitespace ?
cols = 0
break
nbsp = b[map[mm + x]] == ' '
m = map[mm + x]
if ('­' in b[bisub : m]) and not nbsp:
hyphen = m - 1
while b[hyphen] != '­':
hyphen -= 1
while map[mm + x] > hyphen: ## Only looking backward, if foreward is required the word is probabily not hyphenated correctly
x -= 1
x += 1
m = map[mm + x]
mm += x - (0 if nbsp else 1) ## 1 so we have space for a hythen
for bb in b[bisub : m]:
buf += bb
buf += '\n' if nbsp else '\0\n'
cols -= x - (0 if nbsp else 1)
bisub = m
w = iwrap
if indent != -1:
buf += line[:indent]
for j in range(bisub, bi):
b[j - bisub] = b[j]
bi -= bisub
if cols > w:
buf += '\n' buf += '\n'
w = wrap w = wrap
if indent != -1: if indent != -1:
buf + line[:indent] buf += line[:indent]
w -= indentc w -= indentc
buf += '\n' for bb in b[:bi]:
buf += bb
rc = '\n'.join(line.rstrip() for line in buf[:-1].split('\n')); w -= cols
rc = rc.replace('­', ''); # remove soft hyphens cols = 0
rc = rc.replace('\0', '%s%s%s' % (AUTO_PUSH, self.hyphen, AUTO_POP)) bi = 0
return rc if d is None:
i += 1
else:
if w > 0:
buf += ' '
w -= 1
else:
buf += '\n'
w = wrap
if indent != -1:
buf + line[:indent]
w -= indentc
buf += '\n'
rc = '\n'.join(line.rstrip() for line in buf[:-1].split('\n'));
rc = rc.replace('­', ''); # remove soft hyphens
rc = rc.replace('\0', '%s%s%s' % (AUTO_PUSH, self.hyphen, AUTO_POP))
return rc
except Exception as err:
import traceback
errormessage = ''.join(traceback.format_exception(type(err), err, None))
rc = '\n'.join(line.rstrip() for line in buf.split('\n'));
rc = rc.replace('\0', '%s%s%s' % (AUTO_PUSH, self.hyphen, AUTO_POP))
errormessage += '\n---- WRAPPING BUFFER ----\n\n' + rc
try:
if os.readlink('/proc/self/fd/2') != os.readlink('/proc/self/fd/1'):
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
''' '''
@ -1825,7 +1930,7 @@ class ColourStack():
@return :str String that should be inserted into your buffer @return :str String that should be inserted into your buffer
''' '''
def push(self): def push(self):
self.stack = [[self.bufproto, None, None, [False] * 9]] + self.stack self.stack.insert(0, [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'
@ -1837,9 +1942,10 @@ class ColourStack():
@return :str String that should be inserted into your buffer @return :str String that should be inserted into your buffer
''' '''
def pop(self): def pop(self):
old = self.stack[0] old = self.stack.pop(0)
self.stack = self.stack[1:]
rc = '\033[0;' rc = '\033[0;'
if len(self.stack) == 0: # last resort in case something made it pop too mush
push()
new = self.stack[0] 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] + ';'
@ -1984,7 +2090,7 @@ class SpelloCorrecter(): # Naïvely and quickly proted and adapted from optimise
m0[x] = x m0[x] = x
x -= 1 x -= 1
previous = "" previous = ''
self.dictionary[-1] = previous; self.dictionary[-1] = previous;
for directory in directories: for directory in directories:
@ -2022,7 +2128,7 @@ class SpelloCorrecter(): # Naïvely and quickly proted and adapted from optimise
# break # break
# previous = proper # previous = proper
# self.reusable[self.dictionaryEnd] = prevCommon # self.reusable[self.dictionaryEnd] = prevCommon
# index -= 1; # index -= 1;
''' '''
@ -2270,8 +2376,8 @@ 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 | -v | -h)' usage_listhelp = '(-l | -L | -B | +l | +L | -v | -h)'
usage_file = '[-f\033[4mPONY\033[24m]* [[--] \033[4mmessage\033[24m]' usage_file = '[-f\033[4mPONY\033[24m]* [[--] \033[4mmessage\033[24m]'
usage_xfile = '[-F\033[4mPONY\033[24m]* [[--] \033[4mmessage\033[24m]' usage_xfile = '(-F\033[4mPONY\033[24m)* [[--] \033[4mmessage\033[24m]'
usage_quote = '-q [\033[4mPONY\033[24m*]' usage_quote = '(-q \033[4mPONY\033[24m)*'
usage = '%s %s\n%s %s %s\n%s %s %s\n%s %s %s' % (usage_saythink, usage_listhelp, usage = '%s %s\n%s %s %s\n%s %s %s\n%s %s %s' % (usage_saythink, usage_listhelp,
usage_saythink, usage_common, usage_file, usage_saythink, usage_common, usage_file,
@ -2325,9 +2431,15 @@ opts.add_argumented( ['-W', '--wrap'], arg = 'COLUMN', help =
opts.add_argumented( ['-b', '--bubble', '--balloon'], arg = 'STYLE', help = 'Select a balloon style.') opts.add_argumented( ['-b', '--bubble', '--balloon'], arg = 'STYLE', help = 'Select a balloon style.')
opts.add_argumented( ['-f', '--file', '--pony'], arg = 'PONY', help = 'Select a pony.\nEither a file name or a pony name.') opts.add_argumented( ['-f', '--file', '--pony'], arg = 'PONY', help = 'Select a pony.\nEither a file name or a pony name.')
opts.add_argumented( ['-F', '++file', '++pony'], arg = 'PONY', help = 'Select a non-MLP:FiM pony.') opts.add_argumented( ['-F', '++file', '++pony'], arg = 'PONY', help = 'Select a non-MLP:FiM pony.')
opts.add_variadic( ['-q', '--quote'], arg = 'PONY', help = 'Select a ponies which will quote themself.') opts.add_argumented( ['-q', '--quote'], arg = 'PONY', help = 'Select a pony which will quote herself.')
opts.add_variadic( ['--f', '--files', '--ponies'], arg = 'PONY')
opts.parse() opts.add_variadic( ['--F', '++files', '++ponies'], arg = 'PONY')
opts.add_variadic( ['--q', '--quotes'], arg = 'PONY')
'''
Whether at least one unrecognised option was used
'''
unrecognised = not opts.parse()