#!/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)