diff --git a/mailfmt b/mailfmt new file mode 100644 index 0000000..d8b3596 --- /dev/null +++ b/mailfmt @@ -0,0 +1,353 @@ +#!/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) +