Newer
Older
mailfmt / mailfmt
#!/usr/bin/env python2
# mailfmt  - Copyright (c) 2002-2022, 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)