From 59247bf6e9c80e17fac4e58b80ad1bee1e602fa0 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@operamail.com>
Date: Sun, 30 Sep 2012 00:53:16 +0200
Subject: use `with` instead of `try ... finally`

---
 ponysay.py | 131 ++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 74 insertions(+), 57 deletions(-)

diff --git a/ponysay.py b/ponysay.py
index d1571a7..c1be5df 100755
--- a/ponysay.py
+++ b/ponysay.py
@@ -30,6 +30,7 @@ import random
 from subprocess import Popen, PIPE
 
 
+
 '''
 The version of ponysay
 '''
@@ -56,7 +57,6 @@ class Ponysay():
             args.help()
             return
         
-        
         if   args.opts['-h']        is not None:  args.help()
         elif args.opts['--quoters'] is not None:  self.quoters()
         elif args.opts['--onelist'] is not None:  self.onelist()
@@ -85,6 +85,7 @@ class Ponysay():
     Use extra ponies
     '''
     def __extraponies(self, args = None):
+        ## If extraponies are used, change ponydir to extraponydir
         if args is None:
             ponydirs[:] = extraponydirs
         elif args.opts['-F'] is not None:
@@ -96,6 +97,7 @@ class Ponysay():
     Use best.pony if nothing else is set
     '''
     def __bestpony(self, args):
+        ## 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):
             for ponydir in ponydirs:
                 if os.path.isfile(ponydir + 'best.pony') or os.path.islink(ponydir + 'best.pony'):
@@ -108,25 +110,24 @@ class Ponysay():
     Apply pony name remapping to args according to UCS settings
     '''
     def __ucsremap(self, args):
+        ## Read UCS configurations
         env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else ''
         ucs_conf = 0
         if   env_ucs in ('yes',    'y', '1'):  ucs_conf = 1
         elif env_ucs in ('harder', 'h', '2'):  ucs_conf = 2
         
+        ## Stop USC is not used
         if ucs_conf == 0:
             return
         
+        ## Read all lines in all UCS → ASCII map files
         maplines = []
         for ucsmap in ucsmaps:
             if os.path.isfile(ucsmap):
-                mapfile = None
-                try:
-                    mapfile = open(ucsmap, 'rb')
+                with open(ucsmap, 'rb') as mapfile:
                     maplines += [line.replace('\n', '') for line in mapfile.read().decode('utf8', 'replace').split('\n')]
-                finally:
-                    if mapfile is not None:
-                        mapfile.close()
         
+        ## Create USC → ASCII mapping from read lines
         map = {}
         stripset = ' \t' # must be string, wtf! and way doesn't python's doc say so
         for line in maplines:
@@ -136,6 +137,7 @@ class Ponysay():
                 ascii = line[s + 1:].strip(stripset)
                 map[ucs] = ascii
         
+        ## Apply USC → ASCII mapping to -f and -q arguments
         for flag in ('-f', '-q'):
             if args.opts[flag] is not None:
                 for i in range(0, len(args.opts[flag])):
@@ -151,25 +153,24 @@ class Ponysay():
     Apply USC:ise pony names according to UCS settings
     '''
     def __ucsise(self, ponies, links = None):
+        ## Read UCS configurations
         env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else ''
         ucs_conf = 0
         if   env_ucs in ('yes',    'y', '1'):  ucs_conf = 1
         elif env_ucs in ('harder', 'h', '2'):  ucs_conf = 2
         
+        ## Stop USC is not used
         if ucs_conf == 0:
             return
         
+        ## Read all lines in all UCS → ASCII map files
         maplines = []
         for ucsmap in ucsmaps:
             if os.path.isfile(ucsmap):
-                mapfile = None
-                try:
-                    mapfile = open(ucsmap, 'rb')
+                with open(ucsmap, 'rb') as mapfile:
                     maplines += [line.replace('\n', '') for line in mapfile.read().decode('utf8', 'replace').split('\n')]
-                finally:
-                    if mapfile is not None:
-                        mapfile.close()
         
+        ## Create USC → ASCII mapping from read lines
         map = {}
         stripset = ' \t' # must be string, wtf! and way doesn't python's doc say so
         for line in maplines:
@@ -179,6 +180,7 @@ class Ponysay():
                 ascii = line[s + 1:].strip(stripset)
                 map[ascii] = ucs
         
+        ## Apply USC → ACII mapping to ponies, by alias if weak settings
         if ucs_conf == 1:
             for pony in ponies:
                 if pony in map:
@@ -197,20 +199,24 @@ class Ponysay():
     def __getponypath(self, names = None):
         ponies = {}
         
+        ## List all pony files, without the .pony ending
         for ponydir in ponydirs:
             for ponyfile in os.listdir(ponydir):
                 pony = ponyfile[:-5]
                 if pony not in ponies:
                     ponies[pony] = ponydir + ponyfile
         
+        ## Filter out all choosen ponies
         if not names == None:
             for name in names:
                 if os.path.exists(name):
                     ponies[name] = name
         
+        ## If there is not select ponies, choose all of them
         if names == None:
             names = list(ponies.keys())
         
+        ## Select a random pony of the choosen onles
         pony = names[random.randrange(0, len(names))]
         if pony not in ponies:
             sys.stderr.write('I have never heard of anypony named %s\n' % (pony));
@@ -223,6 +229,7 @@ class Ponysay():
     Returns a set with all ponies that have quotes and are displayable
     '''
     def __quoters(self):
+        ## List all unique quote files
         quotes = []
         quoteshash = set()
         _quotes = []
@@ -234,6 +241,7 @@ class Ponysay():
                     quoteshash.add(quote)
                     quotes.append(quote)
         
+        ## Create a set of all ponyes that have quotes
         ponies = set()
         for ponydir in ponydirs:
             for pony in os.listdir(ponydir):
@@ -251,11 +259,13 @@ class Ponysay():
     Returns a list with all (pony, quote file) pairs
     '''
     def __quotes(self):
+        ## Get all ponyquote files
         quotes = []
         for quotedir in quotedirs:
             quotes += [quotedir + item for item in os.listdir(quotedir)]
-        rc = []
         
+        ## Create list of all pony–quote file-pairs
+        rc = []
         for ponydir in ponydirs:
             for pony in os.listdir(ponydir):
                 if not pony[0] == '.':
@@ -273,6 +283,7 @@ class Ponysay():
     Gets the size of the terminal in (rows, columns)
     '''
     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]
@@ -288,16 +299,20 @@ class Ponysay():
     Columnise a list and prints it
     '''
     def __columnise(self, ponies):
+        ## Get terminal width, and a 2 which is the space between columns
         termwidth = self.__gettermsize()[1] + 2
+        ## Sort the ponies, and get the cells' widths, and the largest width + 2
         ponies.sort(key = lambda pony : pony[0])
         widths = [UCS.dispLen(pony[0]) for pony in ponies]
         width = max(widths) + 2 # longest pony file name + space between columns
         
-        cols = termwidth // width
+        ## Calculate the number of rows and columns, can create a list of empty columns
+        cols = termwidth // width # do not believe electricians, this means ⌊termwidth / width⌋
         rows = (len(ponies) + cols - 1) // cols
         columns = []
         for c in range(0, cols):  columns.append([])
         
+        ## Fill the columns with cells of ponies
         (y, x) = (0, 0)
         for j in range(0, len(ponies)):
             cell = ponies[j][1] + ' ' * (width - widths[j]);
@@ -307,6 +322,7 @@ class Ponysay():
                 x += 1
                 y = 0
         
+        ## Make the columnisation nicer by letting the last row be partially empty rather than the last column
         diff = rows * cols - len(ponies)
         if diff > 2:
             c = cols - 1
@@ -318,6 +334,7 @@ class Ponysay():
                 diff -= 1
                 pass
         
+        ## Create rows from columns
         lines = []
         for r in range(0, rows):
              lines.append([])
@@ -325,7 +342,8 @@ class Ponysay():
                  if r < len(columns[c]):
                      line = lines[r].append(columns[c][r])
         
-        print('\n'.join([''.join(line)[:-2] for line in lines]));
+        ## Print the matrix, with one extra blank row
+        print('\n'.join([''.join(line)[:-2] for line in lines]))
         print()
     
     
@@ -333,20 +351,26 @@ class Ponysay():
     Lists the available ponies
     '''
     def list(self):
+        ## Get all quoters
         quoters = self.__quoters()
         
         for ponydir in ponydirs: # Loop ponydirs
+            ## Get all ponies in the directory
             _ponies = os.listdir(ponydir)
+            
+            ## Remove .pony from all files and skip those that does not have .pony
             ponies = []
             for pony in _ponies:
                 if (len(pony) > 5) and (pony[-5:] == '.pony'):
                     ponies.append(pony[:-5])
+            
+            ## UCS:ise pony names, they are already sorted
             self.__ucsise(ponies)
             
+            ## If ther directory is not empty print its name and all ponies, columnised
             if len(ponies) == 0:
                 continue
             print('\033[1mponies located in ' + ponydir + '\033[21m')
-            
             self.__columnise([(pony, '\033[1m' + pony + '\033[21m' if pony in quoters else pony) for pony in ponies])
     
     
@@ -354,22 +378,30 @@ class Ponysay():
     Lists the available ponies with alternatives inside brackets
     '''
     def linklist(self):
+        ## Get the size of the terminal and all ponies with quotes
         termsize = self.__gettermsize()
         quoters = self.__quoters()
         
         for ponydir in ponydirs: # Loop ponydirs
+            ## Get all pony files in the directory
             _ponies = os.listdir(ponydir)
+            
+            ## Remove .pony from all files and skip those that does not have .pony
             ponies = []
             for pony in _ponies:
                 if (len(pony) > 5) and (pony[-5:] == '.pony'):
                     ponies.append(pony[:-5])
             
+            ## If there are no ponies in the directory skip to next directory, otherwise, print the directories name
             if len(ponies) == 0:
                 continue
             print('\033[1mponies located in ' + ponydir + '\033[21m')
             
+            ## UCS:ise pony names
             pseudolinkmap = {}
             self.__ucsise(ponies, pseudolinkmap)
+            
+            ## Create target–link-pair, with `None` as link if the file is not a symlink or in `pseudolinkmap`
             pairs = []
             for pony in ponies:
                 if pony in pseudolinkmap:
@@ -377,6 +409,7 @@ class Ponysay():
                 else:
                     pairs.append((pony, os.path.realpath(ponydir + pony + '.pony') if os.path.islink(ponydir + pony + '.pony') else None))
             
+            ## Create map from source pony to alias ponies for each pony
             ponymap = {}
             for pair in pairs:
                 if (pair[1] is None) or (pair[1] == ''):
@@ -391,7 +424,7 @@ class Ponysay():
                     else:
                         ponymap[target] = [pair[0]]
             
-            width = 0
+            ## Create list of source ponies concatenated with alias ponies in brackets
             ponies = {}
             for pony in ponymap:
                 w = UCS.dispLen(pony)
@@ -409,22 +442,24 @@ class Ponysay():
                         item += '\033[1m' + sym + '\033[21m' if (sym in quoters) else sym
                     item += ')'
                 ponies[(item.replace('\033[1m', '').replace('\033[21m', ''), item)] = w
-                if width < w:
-                    width = w
             
+            ## Print the ponies, columnised
             self.__columnise(list(ponies))
     
     
     '''
-    Lists with all ponies that have quotes and are displayable
+    Lists with all ponies that have quotes and are displayable, on one column without anything bold or otherwise formated
     '''
     def quoters(self):
-        last = ''
-        ponies = []
-        for pony in self.__quoters():
-            ponies.append(pony)
+        ## Get all quoters
+        ponies = self.__quoters()
+        
+        ## USC:ise and sort
         self.__ucsise(ponies)
         ponies.sort()
+        
+        ## Print each one on a seperate line, but skip duplicates
+        last = ''
         for pony in ponies:
             if not pony == last:
                 last = pony
@@ -432,19 +467,26 @@ class Ponysay():
     
     
     '''
-    Lists the available ponies one one column without anything bold
+    Lists the available ponies on one column without anything bold or otherwise formated
     '''
     def onelist(self):
-        last = ''
+        ## Get all pony files
         _ponies = []
         for ponydir in ponydirs: # Loop ponydirs
             _ponies += os.listdir(ponydir)
+        
+        ## Remove .pony from all files and skip those that does not have .pony
         ponies = []
         for pony in _ponies:
             if (len(pony) > 5) and (pony[-5:] == '.pony'):
                 ponies.append(pony[:-5])
+        
+        ## USC:ise and sort
         self.__ucsise(ponies)
         ponies.sort()
+        
+        ## Print each one on a seperate line, but skip duplicates
+        last = ''
         for pony in ponies:
             if not pony == last:
                 last = pony
@@ -525,13 +567,8 @@ class Ponysay():
         for elem in ('\\', '/', 'ww', 'ee', 'nw', 'nnw', 'n', 'nne', 'ne', 'nee', 'e', 'see', 'se', 'sse', 's', 'ssw', 'sw', 'sww', 'w', 'nww'):
             map[elem] = []
         
-        balloonstream = None
-        try:
-            balloonstream = open(balloonfile, 'rb')
+        with open(balloonfile, 'rb') as balloonstream:
             data = [line.replace('\n', '') for line in balloonstream.read().decode('utf8', 'replace').split('\n')]
-        finally:
-            if balloonstream is not None:
-                balloonstream.close()
         
         last = None
         for line in data:
@@ -663,13 +700,8 @@ class Ponysay():
             
         if not len(pairs) == 0:
             pair = pairs[random.randrange(0, len(pairs))]
-            qfile = None
-            try:
-                qfile = open(pair[1], 'rb')
+            with open(pair[1], 'rb') as qfile:
                 args.message = qfile.read().decode('utf8', 'replace').strip()
-            finally:
-                if qfile is not None:
-                    qfile.close()
             args.opts['-f'] = [pair[0]]
         elif len(args.opts['-q']) == 0:
             sys.stderr.write('Princess Celestia! All the ponies are mute!\n')
@@ -735,14 +767,9 @@ class Ponysay():
         if not os.path.isfile(cachedir + '/.version'):
             newversion = True
         else:
-            cachev = None
-            try:
-                cachev = open(cachedir + '/.version', 'rb')
+            with open(cachedir + '/.version', 'rb') as cachev:
                 if cachev.read().decode('utf8', 'replace').replace('\n', '') == KMS_VERSION:
                     newversion = True
-            finally:
-                if cachev is not None:
-                    cachev.close()
         if newversion:
             for cached in os.listdir(cachedir):
                 cached = cachedir + '/' + cached
@@ -750,13 +777,8 @@ class Ponysay():
                     shutil.rmtree(cached, False)
                 else:
                     os.remove(cached)
-            cachev = None
-            try:
-                cachev = open(cachedir + '/.version', 'w+')
+            with open(cachedir + '/.version', 'w+') as cachev:
                 cachev.write(KMS_VERSION)
-            finally:
-                if cachev is not None:
-                    cachev.close()
         
         kmsponies = cachedir + '/kmsponies/' + palettefile
         kmspony = (kmsponies + pony).replace('//', '/')
@@ -1157,13 +1179,8 @@ class Backend():
     Loads the pony file
     '''
     def __loadFile(self):
-        ponystream = None
-        try:
-            ponystream = open(self.ponyfile, 'rb')
+        with open(self.ponyfile, 'rb') as ponystream:
             self.pony = ponystream.read().decode('utf8', 'replace')
-        finally:
-            if ponystream is not None:
-                ponystream.close()
     
     
     '''
-- 
cgit 


From 0cd697f9464080371e8600ab4b05154165015820 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@operamail.com>
Date: Sun, 30 Sep 2012 01:11:50 +0200
Subject: documenting the code

---
 ponysay.py | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/ponysay.py b/ponysay.py
index c1be5df..5f5afe9 100755
--- a/ponysay.py
+++ b/ponysay.py
@@ -502,20 +502,26 @@ class Ponysay():
     Prints a list of all balloons
     '''
     def balloonlist(self):
+        ## Get the size of the terminal
         termsize = self.__gettermsize()
-        balloonset = set()
         
+        ## Get all balloons
+        balloonset = set()
         for balloondir in balloondirs:
             for balloon in os.listdir(balloondir):
+                ## Use .think if running ponythink, otherwise .say
                 if isthink and (len(balloon) > 6) and (balloon[-6:] == '.think'):
                     balloon = balloon[:-6]
                 elif (not isthink) and (len(balloon) > 4) and (balloon[-4:] == '.say'):
                     balloon = balloon[:-4]
                 else:
                     continue
+                
+                ## Add the balloon if there is none with the same name
                 if balloon not in balloonset:
                     balloonset.add(balloon)
         
+        ## Print all balloos, columnised
         self.__columnise([(balloon, balloon) for balloon in list(balloonset)])
     
     
@@ -523,29 +529,33 @@ class Ponysay():
     Returns one file with full path, names is filter for style names, also accepts filepaths
     '''
     def __getballoonpath(self, names):
+        ## Stop if their is no choosen balloon
         if names is None:
             return None
-        balloons = {}
         
+        ## Get all balloons
+        balloons = {}
         for balloondir in balloondirs:
             for balloon in os.listdir(balloondir):
                 balloonfile = balloon
+                ## Use .think if running ponythink, otherwise .say
                 if isthink and (len(balloon) > 6) and (balloon[-6:] == '.think'):
                     balloon = balloon[:-6]
                 elif (not isthink) and (len(balloon) > 4) and (balloon[-4:] == '.say'):
                     balloon = balloon[:-4]
                 else:
                     continue
+                
+                ## Add the balloon if there is none with the same name
                 if balloon not in balloons:
                     balloons[balloon] = balloondir + balloonfile
         
+        ## Support for explicit balloon file names
         for name in names:
             if os.path.exists(name):
                 balloons[name] = name
         
-        if names == None:
-            names = list(balloons.keys())
-        
+        ## Select a random balloon of the choosen ones
         balloon = names[random.randrange(0, len(names))]
         if balloon not in balloons:
             sys.stderr.write('That balloon style %s does not exist\n' % (balloon));
-- 
cgit 


From 9919d3e228761f318c8cee2d2eaf22c51f0e3ca3 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@operamail.com>
Date: Sun, 30 Sep 2012 01:13:21 +0200
Subject: doc derp

---
 ponysay.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ponysay.py b/ponysay.py
index 5f5afe9..418cab0 100755
--- a/ponysay.py
+++ b/ponysay.py
@@ -206,7 +206,7 @@ class Ponysay():
                 if pony not in ponies:
                     ponies[pony] = ponydir + ponyfile
         
-        ## Filter out all choosen ponies
+        ## Support for explicit pony file names
         if not names == None:
             for name in names:
                 if os.path.exists(name):
-- 
cgit 


From 19609f75b2021d5c97fa872aeb4f27736fb01236 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@operamail.com>
Date: Sun, 30 Sep 2012 02:33:58 +0200
Subject: documenting the code

---
 ponysay.py | 46 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 40 insertions(+), 6 deletions(-)

diff --git a/ponysay.py b/ponysay.py
index 418cab0..790af7c 100755
--- a/ponysay.py
+++ b/ponysay.py
@@ -568,18 +568,22 @@ class Ponysay():
     Creates the balloon style object
     '''
     def __getballoon(self, balloonfile):
+        ## Use default balloon if none is specified
         if balloonfile is None:
             if isthink:
                 return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ')
             return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ')
         
+        ## Initialise map for balloon parts
         map = {}
         for elem in ('\\', '/', 'ww', 'ee', 'nw', 'nnw', 'n', 'nne', 'ne', 'nee', 'e', 'see', 'se', 'sse', 's', 'ssw', 'sw', 'sww', 'w', 'nww'):
             map[elem] = []
         
+        ## 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')]
         
+        ## Parse the balloon file, and fill the map
         last = None
         for line in data:
             if len(line) > 0:
@@ -590,6 +594,7 @@ class Ponysay():
                     value = line[len(last) + 1:]
                     map[last].append(value)
         
+        ## Return the balloon
         return Balloon(map['\\'][0], map['/'][0], map['ww'][0], map['ee'][0], map['nw'], map['nnw'], map['n'],
                        map['nne'], map['ne'], map['nee'][0], map['e'][0], map['see'][0], map['se'], map['sse'],
                        map['s'], map['ssw'], map['sw'], map['sww'][0], map['w'][0], map['nww'][0])
@@ -604,6 +609,7 @@ class Ponysay():
     Prints the name of the program and the version of the program
     '''
     def version(self):
+        ## Prints the "ponysay $VERSION", if this is modified, ./dev/dist.sh must be modified accordingly
         print('%s %s' % ('ponysay', VERSION))
     
     
@@ -611,12 +617,14 @@ class Ponysay():
     Print the pony with a speech or though bubble. message, pony and wrap from args are used.
     '''
     def print_pony(self, args):
+        ## Get message and remove tailing whitespace from stdin (but not for each line)
         if args.message == None:
             msg = ''.join(sys.stdin.readlines()).rstrip()
         else:
             msg = args.message
         
-        if args.opts['-c'] is not None: ## This algorithm should give some result as cowsay's (according to tests)
+        ## This algorithm should give some result as cowsay's (according to tests)
+        if args.opts['-c'] is not None:
             buf = ''
             last = ' '
             CHARS = '\t \n'
@@ -636,8 +644,10 @@ class Ponysay():
                     last = c
             msg = buf.replace('\n', '\n\n')
         
+        ## Get the pony
         pony = self.__getponypath(args.opts['-f'])
         
+        ## Use PNG file as pony file
         if (len(pony) > 4) and (pony[-4:].lower() == '.png'):
             pony = '\'' + pony.replace('\'', '\'\\\'\'') + '\''
             pngcmd = ('img2ponysay -p -- ' if linuxvt else 'img2ponysay -- ') + pony
@@ -645,18 +655,23 @@ class Ponysay():
             Popen(pngcmd, stdout=os.fdopen(pngpipe[1], 'w'), shell=True).wait()
             pony = '/proc/' + str(os.getpid()) + '/fd/' + str(pngpipe[0])
         
+        ## If KMS is utilies, select a KMS pony file and create it if necessary
         pony = self.__kms(pony)
         
+        ## If in Linux VT clean the terminal (See info/pdf-manual [Printing in TTY with KMS])
         if linuxvt:
             print('\033[H\033[2J', end='')
         
+        ## Width Get truncation and wrapping
         env_width = os.environ['PONYSAY_FULL_WIDTH'] if 'PONYSAY_FULL_WIDTH' in os.environ else None
         if env_width is None:  env_width = ''
         widthtruncation = self.__gettermsize()[1] if env_width not in ('yes', 'y', '1') else None
         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']))
         
+        ## Run cowsay replacement
         backend = Backend(message = msg, ponyfile = pony, wrapcolumn = messagewrap if messagewrap is not None else 40, width = widthtruncation, balloon = balloon)
         backend.parse()
         output = backend.output
@@ -664,6 +679,7 @@ class Ponysay():
             output = output[:-1]
         
         
+        ## Load height trunction settings
         env_bottom = os.environ['PONYSAY_BOTTOM'] if 'PONYSAY_BOTTOM' in os.environ else None
         if env_bottom is None:  env_bottom = ''
         
@@ -673,9 +689,8 @@ class Ponysay():
         env_lines = os.environ['PONYSAY_SHELL_LINES'] if 'PONYSAY_SHELL_LINES' in os.environ else None
         if (env_lines is None) or (env_lines == ''):  env_lines = '2'
         
+        ## Print the output, truncated on height is so set
         lines = self.__gettermsize()[0] - int(env_lines)
-        
-        
         if linuxvt or (env_height is ('yes', 'y', '1')):
             if env_bottom is ('yes', 'y', '1'):
                 for line in output.split('\n')[: -lines]:
@@ -691,6 +706,7 @@ class Ponysay():
     Print the pony with a speech or though bubble and a self quote
     '''
     def quote(self, args):
+        ## Get all quotes, and if any pony is choosen just keep them
         pairs = self.__quotes()
         if len(args.opts['-q']) > 0:
             ponyset = {}
@@ -707,7 +723,8 @@ class Ponysay():
                 if pair[0] in ponyset:
                     alts.append((ponyset[pair[0]], pair[1]))
             pairs = alts
-            
+        
+        ## Select a random pony–quote-pair, load it and print it
         if not len(pairs) == 0:
             pair = pairs[random.randrange(0, len(pairs))]
             with open(pair[1], 'rb') as qfile:
@@ -724,22 +741,26 @@ class Ponysay():
     
     
     '''
-    Indentifies whether KMS support is utilised
+    Identifies whether KMS support is utilised
     '''
     @staticmethod
     def isUsingKMS():
+        ## KMS is not utilised if Linux VT is not used
         if not linuxvt:
             return False
         
+        ## Read the PONYSAY_KMS_PALETTE environment variable
         env_kms = os.environ['PONYSAY_KMS_PALETTE'] if 'PONYSAY_KMS_PALETTE' in os.environ else None
         if env_kms is None:  env_kms = ''
         
+        ## Read the PONYSAY_KMS_PALETTE_CMD environment variable, and run it
         env_kms_cmd = os.environ['PONYSAY_KMS_PALETTE_CMD'] if 'PONYSAY_KMS_PALETTE_CMD' in os.environ else None
         if (env_kms_cmd is not None) and (not env_kms_cmd == ''):
             env_kms = Popen(shlex.split(env_kms_cmd), stdout=PIPE, stdin=sys.stderr).communicate()[0].decode('utf8', 'replace')
             if env_kms[-1] == '\n':
                 env_kms = env_kms[:-1]
         
+        ## If the palette string is empty KMS is not utilised
         return env_kms != ''
     
     
@@ -747,32 +768,40 @@ class Ponysay():
     Returns the file name of the input pony converted to a KMS pony, or if KMS is not used, the input pony itself
     '''
     def __kms(self, pony):
+        ## If not in Linux VT, return the pony as is
         if not linuxvt:
             return pony
         
+        ## KMS support version constant
         KMS_VERSION = '1'
         
+        ## Read the PONYSAY_KMS_PALETTE environment variable
         env_kms = os.environ['PONYSAY_KMS_PALETTE'] if 'PONYSAY_KMS_PALETTE' in os.environ else None
         if env_kms is None:  env_kms = ''
         
+        ## Read the PONYSAY_KMS_PALETTE_CMD environment variable, and run it
         env_kms_cmd = os.environ['PONYSAY_KMS_PALETTE_CMD'] if 'PONYSAY_KMS_PALETTE_CMD' in os.environ else None
         if (env_kms_cmd is not None) and (not env_kms_cmd == ''):
             env_kms = Popen(shlex.split(env_kms_cmd), stdout=PIPE, stdin=sys.stderr).communicate()[0].decode('utf8', 'replace')
             if env_kms[-1] == '\n':
                 env_kms = env_kms[:-1]
         
+        ## If not using KMS, return the pony as is
         if env_kms == '':
             return pony
         
+        ## Store palette string and a clong with just the essentials
         palette = env_kms
         palettefile = env_kms.replace('\033]P', '')
         
+        ## Get and in necessary make cache directory
         cachedir = '/var/cache/ponysay'
         if not os.path.isdir(cachedir):
             cachedir = HOME + '/.cache/ponysay'
             if not os.path.isdir(cachedir):
                 os.makedirs(cachedir)
         
+        ## KMS support version control, clean everything if not matching
         newversion = False
         if not os.path.isfile(cachedir + '/.version'):
             newversion = True
@@ -790,20 +819,24 @@ class Ponysay():
             with open(cachedir + '/.version', 'w+') as cachev:
                 cachev.write(KMS_VERSION)
         
+        ## Get kmspony directory and kmspony file
         kmsponies = cachedir + '/kmsponies/' + palettefile
         kmspony = (kmsponies + pony).replace('//', '/')
         
+        ## If the kmspony is missing, create it
         if not os.path.isfile(kmspony):
+            ## Protokmsponies are uncolourful ttyponies
             protokmsponies = cachedir + '/protokmsponies/'
             protokmspony = (protokmsponies + pony).replace('//', '/')
-            
             protokmsponydir = protokmspony[:protokmspony.rindex('/')]
             kmsponydir      =      kmspony[:     kmspony.rindex('/')]
             
+            ## Change file names to be shell friendly
             _protokmspony = '\'' + protokmspony.replace('\'', '\'\\\'\'') + '\''
             _kmspony      = '\'' +      kmspony.replace('\'', '\'\\\'\'') + '\''
             _pony         = '\'' +         pony.replace('\'', '\'\\\'\'') + '\''
             
+            ## Create protokmspony is missing
             if not os.path.isfile(protokmspony):
                 if not os.path.isdir(protokmsponydir):
                     os.makedirs(protokmsponydir)
@@ -811,6 +844,7 @@ class Ponysay():
                     sys.stderr.write('Unable to run ponysay2ttyponysay successfully, you need util-say for KMS support\n')
                     exit(1)
             
+            ## Create kmspony
             if not os.path.isdir(kmsponydir):
                 os.makedirs(kmsponydir)
             if not os.system('tty2colourfultty -p ' + palette + ' < ' + _protokmspony + ' > ' + _kmspony) == 0:
-- 
cgit 


From 1ca607912af63cf0169c012692e8aa39b2cc2e68 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@operamail.com>
Date: Sun, 30 Sep 2012 02:55:48 +0200
Subject: cleaner code

---
 ponysay.py | 36 ++++++++++++++++++++++--------------
 1 file changed, 22 insertions(+), 14 deletions(-)

diff --git a/ponysay.py b/ponysay.py
index 790af7c..8241253 100755
--- a/ponysay.py
+++ b/ponysay.py
@@ -45,6 +45,14 @@ def print(text = '', end = '\n'):
     sys.stdout.buffer.write((str(text) + end).encode('utf-8'))
 
 
+'''
+Checks whether a text ends with a specific text, but has more
+'''
+def endswith(text, ending):
+    return text.endswith(ending) and not (text == ending);
+
+
+
 '''
 This is the mane class of ponysay
 '''
@@ -174,7 +182,7 @@ class Ponysay():
         map = {}
         stripset = ' \t' # must be string, wtf! and way doesn't python's doc say so
         for line in maplines:
-            if (len(line) > 0) and not (line[0] == '#'):
+            if not line.startswith('#'):
                 s = line.index('→')
                 ucs   = line[:s]    .strip(stripset)
                 ascii = line[s + 1:].strip(stripset)
@@ -361,7 +369,7 @@ class Ponysay():
             ## Remove .pony from all files and skip those that does not have .pony
             ponies = []
             for pony in _ponies:
-                if (len(pony) > 5) and (pony[-5:] == '.pony'):
+                if endswith(pony, '.pony'):
                     ponies.append(pony[:-5])
             
             ## UCS:ise pony names, they are already sorted
@@ -389,7 +397,7 @@ class Ponysay():
             ## Remove .pony from all files and skip those that does not have .pony
             ponies = []
             for pony in _ponies:
-                if (len(pony) > 5) and (pony[-5:] == '.pony'):
+                if endswith(pony, '.pony'):
                     ponies.append(pony[:-5])
             
             ## If there are no ponies in the directory skip to next directory, otherwise, print the directories name
@@ -478,7 +486,7 @@ class Ponysay():
         ## Remove .pony from all files and skip those that does not have .pony
         ponies = []
         for pony in _ponies:
-            if (len(pony) > 5) and (pony[-5:] == '.pony'):
+            if endswith(pony, '.pony'):
                 ponies.append(pony[:-5])
         
         ## USC:ise and sort
@@ -510,9 +518,9 @@ class Ponysay():
         for balloondir in balloondirs:
             for balloon in os.listdir(balloondir):
                 ## Use .think if running ponythink, otherwise .say
-                if isthink and (len(balloon) > 6) and (balloon[-6:] == '.think'):
+                if isthink and endswith(balloon, '.think'):
                     balloon = balloon[:-6]
-                elif (not isthink) and (len(balloon) > 4) and (balloon[-4:] == '.say'):
+                elif (not isthink) and endswith(balloon, '.say'):
                     balloon = balloon[:-4]
                 else:
                     continue
@@ -539,9 +547,9 @@ class Ponysay():
             for balloon in os.listdir(balloondir):
                 balloonfile = balloon
                 ## Use .think if running ponythink, otherwise .say
-                if isthink and (len(balloon) > 6) and (balloon[-6:] == '.think'):
+                if isthink and endswith(balloon, '.think'):
                     balloon = balloon[:-6]
-                elif (not isthink) and (len(balloon) > 4) and (balloon[-4:] == '.say'):
+                elif (not isthink) and endswith(balloon, '.say'):
                     balloon = balloon[:-4]
                 else:
                     continue
@@ -648,7 +656,7 @@ class Ponysay():
         pony = self.__getponypath(args.opts['-f'])
         
         ## Use PNG file as pony file
-        if (len(pony) > 4) and (pony[-4:].lower() == '.png'):
+        if endswith(pony.lower(), '.png'):
             pony = '\'' + pony.replace('\'', '\'\\\'\'') + '\''
             pngcmd = ('img2ponysay -p -- ' if linuxvt else 'img2ponysay -- ') + pony
             pngpipe = os.pipe()
@@ -675,7 +683,7 @@ class Ponysay():
         backend = Backend(message = msg, ponyfile = pony, wrapcolumn = messagewrap if messagewrap is not None else 40, width = widthtruncation, balloon = balloon)
         backend.parse()
         output = backend.output
-        if (len(output) > 0) and (output[-1] == '\n'):
+        if output.endswith('\n'):
             output = output[:-1]
         
         
@@ -711,7 +719,7 @@ class Ponysay():
         if len(args.opts['-q']) > 0:
             ponyset = {}
             for pony in args.opts['-q']:
-                if (len(pony) > 5) and (pony[-5:] == '.pony'):
+                if endswith(pony, '.pony'):
                     ponyname = pony[:-5]
                     if '/' in ponyname:
                         ponyname = ponyname[ponyname.rindex('/') + 1:]
@@ -1286,7 +1294,7 @@ class Backend():
                         name = dollar[:find('=')]
                         value = dollar[find('=') + 1:]
                         variables[name] = value
-                    elif (len(dollar) < 7) or not (dollar[:7] == 'balloon'):
+                    elif not dollar.startswith('balloon'):
                         data = variables[dollar].replace('$', '$$')
                         if data == '$$': # if not handled specially we will get an infinity loop
                             if (skip == 0) or (nonskip > 0):
@@ -1819,8 +1827,8 @@ opts.add_argumentless(['-l', '--list'],                                  help =
 opts.add_argumentless(['-L', '--symlist', '--altlist'],                  help = 'List pony names with alternatives.')
 opts.add_argumentless(['+l', '++list'],                                  help = 'List non-MLP:FiM pony names.')
 opts.add_argumentless(['+L', '++symlist', '++altlist'],                  help = 'List non-MLP:FiM pony names with alternatives.')
-opts.add_argumentless(['-A', '--all'],                                   help = 'List MLP:FIM and non-MLP:FiM pony names.')
-opts.add_argumentless(['+A', '++all'],                                   help = 'List MLP:FIM and non-MLP:FiM pony names with alternatives.')
+opts.add_argumentless(['-A', '--all'],                                   help = 'List all pony names.')
+opts.add_argumentless(['+A', '++all'],                                   help = 'List all pony names with alternatives.')
 opts.add_argumentless(['-B', '--bubblelist', '--balloonlist'],           help = 'List balloon styles.')
 opts.add_argumentless(['-c', '--compact'],                               help = 'Compress messages.')
 opts.add_argumented(  ['-W', '--wrap'],                  arg = 'COLUMN', help = 'Specify column where the message should be wrapped.')
-- 
cgit 


From e6bdf2e5ba92b4f01178a1d1bd1d601da38375da Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@operamail.com>
Date: Thu, 4 Oct 2012 04:46:55 +0200
Subject: word wrapping bug fixed + word wrapper supports soft hythens and
 non-breaking space + word wrapper colour inserted hyphens red

---
 CHANGELOG               | 10 ++++++++
 manuals/ponysay.texinfo | 14 ++++++++++-
 ponysay.py              | 62 +++++++++++++++++++++++++++++++++++--------------
 3 files changed, 67 insertions(+), 19 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 28c9f01..d6b6b41 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,14 @@
+Version 2.8
+
+	Support for explicit hypthenation using soft hyphens had been added to the word wrapper.
+
+	Support for explicit non-word wrapping using non-breaking space had been added to the word wrapper.
+
+	The word wrapper colours the inserted hyphens in red.
+
+
 Version 2.7
+
 	New ponies: basil, cloudkicker, cerberus, cow, derpysad, flowertrio, frederickhorseshoepin,
 	            horsemd, jeffletroski, jesuspezuna, joe, joetuxedo, manticore, meadowsong,
 	            meliot, pinkiegummydisguise, seaswirl, theodoredonaldkerabatsos, turf,
diff --git a/manuals/ponysay.texinfo b/manuals/ponysay.texinfo
index 737aba8..bbae2a9 100644
--- a/manuals/ponysay.texinfo
+++ b/manuals/ponysay.texinfo
@@ -8,7 +8,7 @@
 @documentlanguage en
 @finalout
 @c %**end of header
-@set VERSION 2.7
+@set VERSION 2.8
 
 @defindex op
 @synindex op vr
@@ -1848,6 +1848,18 @@ sequences.
 @cindex versions
 @cindex previous releases
 
+
+@heading Version 2.8
+@itemize @bullet
+@item
+Support for explicit hypthenation using soft hyphens had been added to the word wrapper.
+@item
+Support for explicit non-word wrapping using non-breaking space had been added to the word wrapper.
+@item
+The word wrapper colours the inserted hyphens in red.
+@end itemize
+
+
 @heading Version 2.7
 @itemize @bullet
 @item
diff --git a/ponysay.py b/ponysay.py
index 8241253..1ebd9c3 100755
--- a/ponysay.py
+++ b/ponysay.py
@@ -1473,6 +1473,8 @@ class Backend():
     Wraps the message
     '''
     def __wrapMessage(self, message, wrap):
+        AUTO_PUSH = '\033[01010~'
+        AUTO_POP  = '\033[10101~'
         lines = message.split('\n')
         buf = ''
         for line in lines:
@@ -1484,10 +1486,11 @@ class Backend():
             (i, n) = (0, len(line))
             while i <= n:
                 d = None
-                if i != n:
+                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]
@@ -1513,6 +1516,7 @@ class Backend():
                                 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):
@@ -1520,27 +1524,46 @@ class Backend():
                                 indentc += 1
                     b[bi] = d
                     bi += 1
-                    if not UCS.isCombining(d):
+                    if (not UCS.isCombining(d)) and (d != '­'):
                         cols += 1
                     map[cols] = bi
                 else:
+                    ## Wrap?
                     mm = 0
-                    while (w > 8) and (cols > w + 3):
-                        mm += w - 1
-                        m = map[mm]
-                        for bb in b[:m]:
+                    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 hythenated 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'
-                        cols -= w - 1
-                        m += w -1
-                        bi -= m
-                        bb = b[m:]
-                        for j in range(0, bi):
-                            b[j] = bb[j]
-                        w = wrap
+                        buf += '\n' if nbsp else '\0\n'
+                        cols -= x - (0 if nbsp else 1)
+                        bisub = m
+                        
+                        w = iwrap
                         if indent != -1:
                             buf += line[:indent]
-                            w -= indentc
+                    
+                    for j in range(bisub, bi):
+                        b[j - bisub] = b[j]
+                    bi -= bisub
+                    
                     if cols > w:
                         buf += '\n'
                         w = wrap
@@ -1552,7 +1575,7 @@ class Backend():
                     w -= cols
                     cols = 0
                     bi = 0
-                    if d == -1:
+                    if d is None:
                         i += 1
                     else:
                         if w > 0:
@@ -1564,9 +1587,12 @@ class Backend():
                             if indent != -1:
                                 buf + line[:indent]
                                 w -= indentc
-            
             buf += '\n'
-        return '\n'.join(line.rstrip() for line in buf[:-1].split('\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, '\033[31m-', AUTO_POP)) # TODO make configurable
+        return rc
 
 
 '''
-- 
cgit