colpage.py:
my thoughts for the future:
1. a generator version.
2. a means to have it print each finished page
3. headers/footers/sidings on each page, maybe with page numbers.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals """Organize strings into columns and columns into pages using separate coroutines. file colpage.py purpose organize strings into columns and columns into pages email 10054452614123394844460370234029112340408691 The intent is that this command works correctly under both Python 2 and Python 3. Please report failures or code improvement to the author. """ __license__ = """ Copyright (C) 2017, by Phil D. Howard - all other rights reserved Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The author may be contacted by decoding the number 10054452614123394844460370234029112340408691 (provu igi la numeron al duuma) """ import fcntl, os, struct, sys, termios def get_terminal_geometry(fd=1): """Get terminal geometry (width,heght) for a specified fd.""" import fcntl,os,struct,termios try: fd = os.open('/dev/tty',os.O_WRONLY) r = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[1::-1] os.close(fd) except: r = None return r class colpage: """class representing column formatted pages""" def __init__(self,gutter=None,width=None,height=None): """initialize class colpage""" self.page_num = 0 return self.geometry( gutter=gutter, width=width, height=height ) def flush(self): """flush pending columns to a completed page""" gutter = self.gutter page = ['']*self.height mc = len(self.columns)-1 for n,column in enumerate(self.columns): # add each column to the page on the right side width = self.widths[n] for m,line in enumerate(column): # append each line to the page being built page[m] += (line+' '*width)[:width] if n < mc: # append a gutter except after the last column page[m] += ' '*gutter self.pages.append(page) # add the new page to the list of completed pages self.columns = [] self.widths = [] return len(self.pages) def geometry(self,gutter=None,width=None,height=None): """set or get colpage geometry (gutter,width,height)""" if 'gutter' in dir(self) and gutter is None and width is None and height is None: self_gutter=self.gutter return (self.gutter,self.width,self.height) if width is None or height is None: term = get_terminal_geometry() if isinstance(gutter,int): self.gutter = gutter elif gutter is None: gutter = 1 if 'gutter' in dir(self): self.gutter = gutter if isinstance(width,int): self.width = width if width is None: width = term[0] if 'width' in dir(self): self.width = width if isinstance(height,int): self.height = height if height is None: height = term[1] if 'height' in dir(self): self.height = height if gutter < 0: raise ValueError if width < 2: raise ValueError if height < 2: raise ValueError self.gutter = gutter self.height = height self.width = width self.columns = [] self.lines = [] self.pages = [] self.widths = [] self.page_starter = ['']*self.height return def destruct(self): del self return def get(self): """get the next completed page (list of list of str) or None.""" if len(self.pages) > 0: return self.pages.pop(0) return None def print_ready_pages(self,**opts): """print the pages that are ready.""" while True: page = self.get() if page == None: break for line in page: print(line,**opts) return def put(self,*args): """put a new string as a line into a page column.""" if len(args) > 0: line = ' '.join(args) self.lines.append(line) # if this new line does not fill the height, then we are done if len(self.lines) < self.height: return # the working list is full or being flushed by put() with no arguments width = max(len(l) for l in self.lines) total_width = sum(this_width+self.gutter for this_width in self.widths)+width if total_width >= self.width: self.flush() self.columns.append(self.lines) self.widths.append(width) if len(args) > 0: self.lines = [] else: self.flush() return def main(args): """main: command line reads stdin, forms it into columns, outputs to stdout.""" if len(args) > 3: gutter, width, height = [int(arg) for arg in args[1:]] colz = colpage( gutter=gutter, width=width, height=height ) else: # let colpage try to get the geometry from the terminal size colz = colpage( gutter=1 ) # feed stdin to colpage, send its pages to stdout for line in sys.stdin: colz.put(line[:-1]) colz.print_ready_pages() # finish up at EOF colz.put() colz.print_ready_pages() return if __name__ == '__main__': try: result=main(sys.argv) sys.stdout.flush() except BrokenPipeError: result=99 except KeyboardInterrupt: print('') result=98 if result is 0 or result is None or result is True: exit(0) if result is 1 or result is False: exit(1) if isinstance(result,str): print(result,file=sys.stderr) exit(2) try: exit(int(result)) except ValueError: print(str(result),file=sys.stderr) exit(3) except TypeError: exit(4) # EOFthis is a script that implements a class and command to format a one-at-a-time sequence (the caller put()s each line one at a time with an empty put() call to indicate the end of the sequence of lines) into column oriented pages (the caller checks for a finished page via get() or print_ready_pages()). the command works on stdin and stdout. the geometry (3 numbers: gutter, width, height) is optional and it tries to use the controlling terminal geometry if not given. the order of forming the columns is top to bottom, on the left column first, adding each column on the right side until no more space is available for the width.
my thoughts for the future:
1. a generator version.
2. a means to have it print each finished page
3. headers/footers/sidings on each page, maybe with page numbers.
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.