| | #!/usr/bin/env python |
---|
| | # mailfmt - Copyright (c) 2002, TundraWare Inc., All Rights Reserved |
---|
| | # Reformat mail/news to cleanup quotation characters and maintain |
---|
| | # reasonable line widths. |
---|
| | |
---|
| | PROGNAME = "MAILFMT" |
---|
| | RCSID = "$Id: mailfmt,v 1.3 2002/10/09 19:34:16 tundra Exp $" |
---|
| | VERSION = RCSID.split()[2] |
---|
| | |
---|
| | ########## |
---|
| | # Imports |
---|
| | ########## |
---|
| | |
---|
| | import getopt |
---|
| | import os |
---|
| | import sys |
---|
| | |
---|
| | |
---|
| | ########## |
---|
| | # Booleans |
---|
| | ########## |
---|
| | |
---|
| | FALSE = 0==1 |
---|
| | TRUE = not FALSE |
---|
| | |
---|
| | |
---|
| | ########## |
---|
| | # Identify The OS And Import OS-Specific Modules |
---|
| | ########## |
---|
| | |
---|
| | if os.name == 'nt': |
---|
| | WIN32 = TRUE |
---|
| | import win32clipboard |
---|
| | from win32ui import MessageBox |
---|
| | else: |
---|
| | WIN32 = FALSE |
---|
| | |
---|
| | |
---|
| | ########## |
---|
| | # Constants & Tables |
---|
| | ########## |
---|
| | |
---|
| | # Constants |
---|
| | |
---|
| | NOQUOTEMATCH = "!" + PROGNAME + "_NOMATCH!" |
---|
| | PMARK = "!" + PROGNAME + "_PARA!" |
---|
| | |
---|
| | |
---|
| | # Error Messages |
---|
| | |
---|
| | eBADWIDTH = "Bogus Output Width!" |
---|
| | eNOCBDATA = "No Data In Clipboard!" |
---|
| | eNOTHING = "Nothing To Do!" |
---|
| | |
---|
| | |
---|
| | ########## |
---|
| | # Function Defintions |
---|
| | ########## |
---|
| | |
---|
| | ##### |
---|
| | # Display An Error |
---|
| | ##### |
---|
| | |
---|
| | def dsply_error(error="Unknown Error"): |
---|
| | if WIN32 and CLIPOUT: |
---|
| | MessageBox(error, PROGNAME + " " + VERSION) |
---|
| | else: |
---|
| | print PROGNAME + " " + VERSION +": " + error |
---|
| | |
---|
| | # End of 'dsply_error()' |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Get input from appropriate location |
---|
| | ##### |
---|
| | |
---|
| | def get_input(): |
---|
| | |
---|
| | if USECB: |
---|
| | win32clipboard.OpenClipboard() |
---|
| | try: |
---|
| | cb = win32clipboard.GetClipboardData() |
---|
| | except: |
---|
| | dsply_error(eNOCBDATA) |
---|
| | sys.exit(1) |
---|
| | win32clipboard.CloseClipboard() |
---|
| | else: |
---|
| | cb = sys.stdin.read() |
---|
| | |
---|
| | return(cb) |
---|
| | |
---|
| | # End of 'get_input()' |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Split line into its quotation portion and its |
---|
| | # content portion, returning both in a list |
---|
| | ##### |
---|
| | |
---|
| | def line_quot(line): |
---|
| | content = line |
---|
| | while content[0] == ATTCHAR: |
---|
| | content = content[1:] |
---|
| | quotetxt = line.split(content)[0] |
---|
| | content = " ".join(content.split()) |
---|
| | return([quotetxt, content]) |
---|
| | |
---|
| | |
---|
| | |
---|
| | |
---|
| | # End of 'line_quot()' |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Output the results |
---|
| | ##### |
---|
| | |
---|
| | def output(results): |
---|
| | |
---|
| | # Add CRs for Win32 systems |
---|
| | if WIN32: |
---|
| | results= results.replace("\n", "\r\n") |
---|
| | |
---|
| | if CLIPOUT: |
---|
| | win32clipboard.OpenClipboard() |
---|
| | win32clipboard.EmptyClipboard() # Gotta do this to make writes globally visible |
---|
| | win32clipboard.SetClipboardText(results) |
---|
| | win32clipboard.CloseClipboard() |
---|
| | else: |
---|
| | print results |
---|
| | |
---|
| | # End of 'output()' |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Print Usage Information |
---|
| | ##### |
---|
| | |
---|
| | def usage(): |
---|
| | print "mailfmt " + VERSION + " - Copyright 2002, TundraWare Inc., All Rights Reserved\n" |
---|
| | print "usage: mailfmt [-a] [-c char] [-h] [-i] [-o] [-r] [-v] [-w num] where," |
---|
| | print " -a do not remove quotation characters" |
---|
| | print " -c char sets the quotation character to remove" |
---|
| | print " -h print this help information" |
---|
| | print " -i read input from stdin instead of clipboard" |
---|
| | print " (automatic on non-Win32 systems)" |
---|
| | print " -o print output instead of sending to clipboard" |
---|
| | print " (automatic on non-Win32 systems)" |
---|
| | print " -r do not wrap lines" |
---|
| | print " -v print detailed version information" |
---|
| | print " -w val sets output line width" |
---|
| | |
---|
| | # End of 'usage()' |
---|
| | |
---|
| | |
---|
| | ########## |
---|
| | # Program Entry Point |
---|
| | ########## |
---|
| | |
---|
| | # Program option defaults |
---|
| | |
---|
| | ATTCHAR = '>' |
---|
| | CLIPOUT = TRUE |
---|
| | RMVQUOT = TRUE |
---|
| | USECB = TRUE |
---|
| | WIDTH = 75 |
---|
| | WRAP = TRUE |
---|
| | |
---|
| | |
---|
| | # Command line processing |
---|
| | |
---|
| | try: |
---|
| | opts, args = getopt.getopt(sys.argv[1:], '-ac:hiorvw:') |
---|
| | except getopt.GetoptError: |
---|
| | usage() |
---|
| | sys.exit(1) |
---|
| | |
---|
| | |
---|
| | for opt, val in opts: |
---|
| | if opt == "-a": |
---|
| | RMVQUOT = FALSE |
---|
| | if opt == "-c": |
---|
| | ATTCHAR = val |
---|
| | if opt == "-h": |
---|
| | usage() |
---|
| | sys.exit(0) |
---|
| | if opt == "-i": |
---|
| | USECB = FALSE |
---|
| | if opt == "-o": |
---|
| | CLIPOUT = FALSE |
---|
| | if opt == "-r": |
---|
| | WRAP = FALSE |
---|
| | if opt == "-v": |
---|
| | print RCSID |
---|
| | sys.exit(0) |
---|
| | if opt == "-w": |
---|
| | try: # Need this to catch silly user input for -w |
---|
| | WIDTH = int(val) |
---|
| | except: |
---|
| | dsply_error(eBADWIDTH) |
---|
| | sys.exit(1) |
---|
| | |
---|
| | if WIDTH < 1: |
---|
| | dsply_error(eBADWIDTH) |
---|
| | sys.exit(1) |
---|
| | |
---|
| | # Condition variables based on OS |
---|
| | |
---|
| | if not WIN32: |
---|
| | USECB = FALSE |
---|
| | CLIPOUT = FALSE |
---|
| | |
---|
| | # Make sure we have something to do |
---|
| | |
---|
| | if (not RMVQUOT) and (not WRAP): # nothing to do |
---|
| | dsply_error(eNOTHING) |
---|
| | sys.exit(1) |
---|
| | |
---|
| | |
---|
| | # Get text to process |
---|
| | |
---|
| | cb = get_input() |
---|
| | |
---|
| | # Remove any nulls - these get embedded if the copy to |
---|
| | # the clipboard was done in a DOS command line box. |
---|
| | |
---|
| | cb = cb.replace("\x00", "") |
---|
| | |
---|
| | # Build work buffer - walk through the input |
---|
| | # splitting each line into a list - the first entry |
---|
| | # contains the quotation string, if any, the second |
---|
| | # the content of the line. |
---|
| | |
---|
| | buffer = [] |
---|
| | for line in cb.splitlines(): |
---|
| | buffer.append(line_quot(line + " ")) # Trailing blank guarantees non-null |
---|
| | # string needed by line_quot() |
---|
| | |
---|
| | # Mark the blank lines for special processing |
---|
| | |
---|
| | for entry in buffer: |
---|
| | if entry[1] == "": |
---|
| | entry[1] = PMARK |
---|
| | |
---|
| | |
---|
| | # Remove leading quotation characters, unless told not to |
---|
| | |
---|
| | if RMVQUOT: |
---|
| | for line in buffer: |
---|
| | line[0] = "" |
---|
| | |
---|
| | |
---|
| | # Wrap lines to specified width, unless told not to. |
---|
| | # This is tricky because there may or may not be leading quotation |
---|
| | # strings. If they are present, we want to preserve them in the |
---|
| | # wrapping process. |
---|
| | |
---|
| | if WRAP: |
---|
| | |
---|
| | # Walk through the current work buffer and concatenate |
---|
| | # adjacent lines with identical quotations |
---|
| | |
---|
| | l="" |
---|
| | i = -1 |
---|
| | lastquot = NOQUOTEMATCH # Make sure no match on first record |
---|
| | |
---|
| | tmp = buffer |
---|
| | buffer=[] |
---|
| | lastquot = tmp[0][0] |
---|
| | content = tmp[0][1] |
---|
| | tmp = tmp[1:] |
---|
| | |
---|
| | for entry in tmp: |
---|
| | if entry[1] == PMARK: # Blank lines output separately |
---|
| | buffer.append([lastquot, content]) # Output pending work |
---|
| | lastquot = entry[0] # Setup to output next time through |
---|
| | content = entry[1] |
---|
| | elif (entry[0] != lastquot) or (content == PMARK): |
---|
| | buffer.append([lastquot, content]) |
---|
| | lastquot = entry[0] |
---|
| | content = entry[1] |
---|
| | else: |
---|
| | content += " " + entry[1] # make sure at least one space always |
---|
| | # separates concatenated lines |
---|
| | |
---|
| | if content != "": |
---|
| | buffer.append([lastquot, content]) |
---|
| | |
---|
| | # Iterate across the results producing lines of appropriate length |
---|
| | # with correct quotation text, if any. |
---|
| | |
---|
| | tmp = [] |
---|
| | for entry in buffer: |
---|
| | # Figure out allowable width with quotation, if any |
---|
| | quot = entry[0] |
---|
| | width = WIDTH - len(quot) |
---|
| | |
---|
| | if quot != "": # Adjust width if we will add pad later |
---|
| | width -= 1 |
---|
| | |
---|
| | # Format each output line to be WIDTH characters or less ending with |
---|
| | # a newline. The one exception happens if a single word is too |
---|
| | # long for the specified width. In this case, the quotation and word |
---|
| | # get output without regards to specified width. |
---|
| | |
---|
| | s = "" |
---|
| | for word in entry[1].split(): |
---|
| | |
---|
| | if len(s + word) < width: # Normal case |
---|
| | s += (word + " ") |
---|
| | elif len(word) > width: # Single word too wide - output as is |
---|
| | if s != "": # Process any previous pending line |
---|
| | tmp.append([quot, s]) |
---|
| | s = "" |
---|
| | tmp.append([quot, word]) |
---|
| | elif len(s + word) == width: # Ends right on width boundary |
---|
| | tmp.append([quot, s + word]) |
---|
| | s = "" |
---|
| | else: # New word makes line too long |
---|
| | tmp.append([quot, s]) |
---|
| | s = word + " " |
---|
| | |
---|
| | if s != "": # Process any last pending line |
---|
| | tmp.append([quot, s]) |
---|
| | |
---|
| | buffer = tmp |
---|
| | |
---|
| | |
---|
| | # Cleanup the final output buffer |
---|
| | |
---|
| | for entry in buffer: |
---|
| | entry[1] = " ".join(entry[1].split()) # Delete trailing spaces |
---|
| | if entry[1] == PMARK: # Remove paragraph markers |
---|
| | entry[1] = "" |
---|
| | |
---|
| | |
---|
| | # Collapse the final work buffer into a single string. |
---|
| | # If there is an quotation string, place a space between |
---|
| | # it and the actual line contents |
---|
| | |
---|
| | tmp = "" |
---|
| | for entry in buffer: |
---|
| | if entry[0] == "": |
---|
| | pad = "" |
---|
| | else: |
---|
| | pad = " " |
---|
| | tmp += entry[0] + pad + entry[1] + '\n' |
---|
| | |
---|
| | |
---|
| | # Send the output as user has specified |
---|
| | |
---|
| | output(tmp) |
---|
| | |
---|
| | |
---|
| | |