diff --git a/CHANGELOG b/CHANGELOG index 68e52818..94c02393 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,19 @@ Version 2.9 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 New ponies: airheart, bastionyorsets, gustavelegrand, milkyway, peppermoon, pinkacopter, pinkiefly, diff --git a/balloons/cowsay.say b/balloons/cowsay.say index bb486dbd..51942faf 100644 --- a/balloons/cowsay.say +++ b/balloons/cowsay.say @@ -6,7 +6,7 @@ s:- w:| e: | -ww:< +ww:< ee: > nw: _ diff --git a/manuals/manpage.0 b/manuals/manpage.0 index 9627f62e..5407e2c8 100644 --- a/manuals/manpage.0 +++ b/manuals/manpage.0 @@ -76,8 +76,8 @@ multiple times, and one of the will be selected randomly. .B \-q, \-\-quote [\fIname\fP...] 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 \fI-q\fP. If one or more ponies are added after \fI-q\fP -the pony will be selected randomly from that set of ponies. +is added as an argument to \fI-q\fP. If one or more ponies are added as an argument +to \fI-q\fP the pony will be selected randomly from that set of ponies. .TP .B \-W, \-\-wrap \fIcolumn\fP The screen column where the message should be wrapped. diff --git a/manuals/ponysay.texinfo b/manuals/ponysay.texinfo index 3cf81314..fd347176 100644 --- a/manuals/ponysay.texinfo +++ b/manuals/ponysay.texinfo @@ -116,7 +116,7 @@ way over e-mail. @cindex invoking @cindex options @cindex arguments -@pindex ponythink +@pindex @command{ponythink} 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 supersedes file names. -@item -F +@item -F PONY @itemx ++file PONY @itemx ++pony PONY @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 (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 @itemx --bubble 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 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 @itemx --wrap COLUMN @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{--pony-only} @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 @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. + @node Advanced usage @chapter Advanced usage of @command{ponysay}. @cindex advanced usage @menu +* Extra information:: Displaying extra information. * Fortune cookies:: Displaying with fortune cookies. * Ponification:: Ponify your fortune cookies. * 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 + +@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 @section Fortune cookies -@pindex fortune +@pindex @command{fortune} @cindex startup @cindex on startup @cindex @file{.bashrc} @@ -410,7 +467,7 @@ described in the previous paragraph every time you open a terminal. @section Ponification @cindex ponification @cindex text ponification -@pindex ponypipe +@pindex @command{ponypipe} 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} @@ -447,7 +504,7 @@ You should read more about this in @ref{KMS ponies}. @node Running on screen @section Running on @command{screen} -@pindex screen +@pindex @command{screen} @cindex @file{.bashrc} @cindex @file{~/.bashrc} @@ -667,7 +724,7 @@ run on it. @node Cowsay @section Cowsay -@pindex cowsay +@pindex @command{cowsay} 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 @@ -1951,8 +2008,22 @@ sequences. @itemize @bullet @item 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 + @heading Version 2.8 @itemize @bullet @item diff --git a/ponysay.py b/ponysay.py index b7abe7f9..de6997a3 100755 --- a/ponysay.py +++ b/ponysay.py @@ -47,6 +47,29 @@ programs by default, report them to Princess Celestia so she can banish them to 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')) + +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 @@ -56,7 +79,7 @@ Checks whether a text ends with a specific text, but has more @return :bool The result of the test ''' 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): if (args.argcount == 0) and not pipelinein: args.help() + exit(254) return ## Modifyable global variables @@ -82,18 +106,23 @@ class Ponysay(): global extraponydirs ## Emulate termial capabilities - if args.opts['-X'] is not None: - linuxvt = False - usekms = False - elif args.opts['-V'] is not None: - linuxvt = True - usekms = False - elif args.opts['-K'] is not None: - linuxvt = True - usekms = True + if args.opts['-X'] is not None: (linuxvt, usekms) = (False, False) + elif args.opts['-V'] is not None: (linuxvt, usekms) = (True, False) + elif args.opts['-K'] is not None: (linuxvt, usekms) = (True, True) ponydirs = vtponydirs if linuxvt and not usekms else xponydirs 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 if args.opts['-h'] is not None: args.help() elif args.opts['--quoters'] is not None: self.quoters() @@ -124,10 +153,26 @@ class Ponysay(): self.__ucsremap(args) if args.opts['-o'] is not None: mode += '$/= $$\\= $' + args.message = '' ## The stuff - if args.opts['-q'] is not None: self.quote(args) - else: self.print_pony(args) + if args.opts['-q'] is not None: + 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: autocorrect = SpelloCorrecter(ponydirs, '.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) sys.stderr.write('I have never heard of anypony named %s\n' % (pony)); exit(1) @@ -362,10 +407,13 @@ class Ponysay(): ''' def __gettermsize(self): ## 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] - termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n - termsize = [int(item) for item in termsize] - return termsize + for channel in (sys.stderr, sys.stdout, sys.stdin): + termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0] + if len(termsize) > 0: + termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n + termsize = [int(item) for item in termsize] + return termsize + return (24, 80) # fall back to minimal sane size @@ -642,9 +690,9 @@ class Ponysay(): if balloon not in balloons: if not alt: autocorrect = SpelloCorrecter(balloondirs, '.think' if isthink else '.say') - alternatives = autocorrect.correct(balloon)[0] - if len(alternatives) > 0: - return self.__getponypath(alternatives, True) + (alternatives, dist) = autocorrect.correct(balloon) + if (len(alternatives) > 0) and (dist <= 5): # TODO the limit `dist` should be configureable + return self.__getballoonpath(alternatives, True) sys.stderr.write('That balloon style %s does not exist\n' % (balloon)); exit(1) else: @@ -661,8 +709,8 @@ class Ponysay(): ## Use default balloon if none is specified if balloonfile is None: if isthink: - return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ') - return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ') + return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ') + return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ') ## Initialise map for balloon parts map = {} @@ -671,7 +719,8 @@ class Ponysay(): ## Read all lines in the balloon file 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 last = None @@ -740,6 +789,7 @@ class Ponysay(): ## Get the pony pony = self.__getponypath(args.opts['-f']) + printinfo('pony file: ' + pony) ## Use PNG file as pony file 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 ## 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 hyphencolour = '' @@ -839,6 +891,7 @@ class Ponysay(): ## Select a random pony–quote-pair, load it and print it if not len(pairs) == 0: pair = pairs[random.randrange(0, len(pairs))] + printinfo('quote file: ' + pair[1]) with open(pair[1], 'rb') as qfile: args.message = qfile.read().decode('utf8', 'replace').strip() args.opts['-f'] = [pair[0]] @@ -1067,7 +1120,8 @@ class ArgParser(): ''' Parse arguments - @param args:list The command line arguments, should include the execute file at index 0, `sys.argv` is default + @param args:list 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): self.argcount = len(argv) - 1 @@ -1083,9 +1137,11 @@ class ArgParser(): tmpdashed = False get = 0 dontget = 0 + self.rc = True def unrecognised(arg): sys.stderr.write('%s: warning: unrecognised option %s\n' % (self.__program, arg)) + self.rc = False while len(deque) != 0: arg = deque[0] @@ -1178,6 +1234,8 @@ class ArgParser(): break 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)) minS = len(max([se, sse, s, ssw, sw], key = len)) - self.minwidth = minE + minE + self.minwidth = minE + minE self.minheight = minN + minS @@ -1326,7 +1384,7 @@ class Balloon(): for j in range(0, len(self.n)): outer = UCS.dispLen(self.nw[j]) + UCS.dispLen(self.ne[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]) else: 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)): outer = UCS.dispLen(self.sw[j]) + UCS.dispLen(self.se[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]) else: 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): self.message = message self.ponyfile = ponyfile - self.wrapcolumn = wrapcolumn + self.wrapcolumn = None if wrapcolumn is None else wrapcolumn - balloon.minwidth self.width = width self.balloon = balloon self.hyphen = hyphen @@ -1386,12 +1444,41 @@ class Backend(): ''' def parse(self): self.__expandMessage() + self.__unpadMessage() 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.__processPony() 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 ''' @@ -1475,10 +1562,10 @@ class Backend(): while i < n: c = self.pony[i] if c == '\t': - n += 8 - (indent & 7) - ed = ' ' * (7 - (indent & 7)) + n += 7 - (indent & 7) + ed = ' ' * (8 - (indent & 7)) c = ' ' - self.pony = self.pony[:i] + ed + self.pony[i:] + self.pony = self.pony[:i] + ed + self.pony[i + 1:] i += 1 if c == '$': if dollar is not None: @@ -1669,131 +1756,149 @@ class Backend(): @return :str The message wrapped ''' 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 = '' - for c in msg: - buf += c + cstack.feed(c) - lines = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n') - buf = '' - for line in lines: - b = [None] * len(line) - map = {} - (bi, cols, w) = (0, 0, wrap) - (indent, indentc) = (-1, 0) + try: + AUTO_PUSH = '\033[01010~' + AUTO_POP = '\033[10101~' + msg = message.replace('\n', AUTO_PUSH + '\n' + AUTO_POP); + cstack = ColourStack(AUTO_PUSH, AUTO_POP) + for c in msg: + buf += c + cstack.feed(c) + lines = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n') + buf = '' - (i, n) = (0, len(line)) - while i <= n: - d = None - if i < n: - d = line[i] - i += 1 - if d == '\033': # TODO this should use self.__getcolour() - ## Invisible stuff - b[bi] = d - bi += 1 - b[bi] = line[i] - d = line[i] - bi += 1 + for line in lines: + b = [None] * len(line) + map = {0 : 0} + (bi, cols, w) = (0, 0, wrap) + (indent, indentc) = (-1, 0) + + (i, n) = (0, len(line)) + while i <= n: + d = None + if i < n: + d = line[i] i += 1 - if d == '[': - while True: - b[bi] = line[i] - d = line[i] - bi += 1 - i += 1 - if (('a' <= d) and (d <= 'z')) or (('A' <= d) and (d <= 'Z')) or (d == '~'): - break - elif d == ']': + if d == '\033': # TODO this should use self.__getcolour() + ## Invisible stuff + b[bi] = d + bi += 1 b[bi] = line[i] d = line[i] bi += 1 i += 1 - if d == 'P': - for j in range(0, 7): + if d == '[': + while True: b[bi] = line[i] + d = line[i] bi += 1 i += 1 - elif (d is not None) and (d != ' '): - ## Fetch word - if indent == -1: - indent = i - 1 - for j in range(0, indent): - if line[j] == ' ': - indentc += 1 - b[bi] = d - bi += 1 - if (not UCS.isCombining(d)) and (d != '­'): - cols += 1 - map[cols] = bi - else: - ## Wrap? - mm = 0 - bisub = 0 - iwrap = wrap - (0 if indent == 1 else indentc) - - while ((w > 8) and (cols > w + 5)) or (cols > iwrap): # TODO make configurable - ## wrap - x = w; - 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' - 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 + if (('a' <= d) and (d <= 'z')) or (('A' <= d) and (d <= 'Z')) or (d == '~'): + break + elif d == ']': + b[bi] = line[i] + d = line[i] + bi += 1 + i += 1 + if d == 'P': + for j in range(0, 7): + b[bi] = line[i] + bi += 1 + i += 1 + elif (d is not None) and (d != ' '): + ## Fetch word + if indent == -1: + indent = i - 1 + for j in range(0, indent): + if line[j] == ' ': + indentc += 1 + b[bi] = d + bi += 1 + if (not UCS.isCombining(d)) and (d != '­'): + cols += 1 + map[cols] = bi else: - if w > 0: - buf += ' ' - w -= 1 - else: + ## Wrap? + mm = 0 + bisub = 0 + 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' w = wrap if indent != -1: - buf + line[:indent] + 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 + for bb in b[:bi]: + buf += bb + w -= cols + cols = 0 + bi = 0 + 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 ''' 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: return None return '\033[0m' @@ -1837,9 +1942,10 @@ class ColourStack(): @return :str String that should be inserted into your buffer ''' def pop(self): - old = self.stack[0] - self.stack = self.stack[1:] + old = self.stack.pop(0) rc = '\033[0;' + if len(self.stack) == 0: # last resort in case something made it pop too mush + push() new = self.stack[0] if new[1] is not None: rc += new[1] + ';' 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 x -= 1 - previous = "" + previous = '' self.dictionary[-1] = previous; for directory in directories: @@ -2022,7 +2128,7 @@ class SpelloCorrecter(): # Naïvely and quickly proted and adapted from optimise # break # previous = proper # 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_listhelp = '(-l | -L | -B | +l | +L | -v | -h)' usage_file = '[-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_xfile = '(-F\033[4mPONY\033[24m)* [[--] \033[4mmessage\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_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( ['-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_variadic( ['-q', '--quote'], arg = 'PONY', help = 'Select a ponies which will quote themself.') - -opts.parse() +opts.add_argumented( ['-q', '--quote'], arg = 'PONY', help = 'Select a pony which will quote herself.') +opts.add_variadic( ['--f', '--files', '--ponies'], arg = 'PONY') +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()