On Mon, Mar 08, 1999 at 09:41:11AM -0500, Petr Hlustik wrote: SNIP > > There is also a perl script to convert Pine's .addressbook to mutt > format. I have to admit, I still miss in Mutt the ability of Pine to "take" > addresses from the body of the e-mails (it can do the From: field). I was also annoyed by this. So I decided to make a little script that adds an extended alias function. You can select any mail address from a mail file and add it to a mutt-alias file. The script is called aladalcurm.py. [ALl ADresses ALiassed with CURses Menu] It is written using Python (http://www.python.org) and curses. This is the first public release. Please mail me if you have any problems, improvements, bug reports, comments.... Actually this is my first program, so be kind :-) Maybe somebody could add it to the contrib directory of future mutt releases? Thanks, Moritz -- Moritz Moeller-Herrmann [EMAIL PROTECTED] ICQ# 3585990 Why do you pay BIG BUCKS for "micro" software with BIG BUGS?
#!/usr/bin/env python ## # Needs python 1.5 and cursesmodule by [EMAIL PROTECTED] (Oliver Andrich) # available somewhere on http://www.python.org :-) # # Copyright by Moritz Moeller-Herrmann <[EMAIL PROTECTED]> # # Use this any way you want. Please inform me of any error/improvement # and leave the copyright notice untouched. # No warranties, as this is my first program :-) # Works for me on Linux with Python 1.5+ ## ## INSTRUCTIONS # This script reads all mail adresses from a piped mailfile and # offers to write them to the specified ALIASFILE below # append the following lines or similiar to your muttrc(without #!): # # macro pager A |'path to script'/aladalcurm.py\n # source 'path to ALIASFILE specified below" # # Then press A if you want to add an adress to your milefile ## ##**** User CONFIGURATION here: ******* # Warning: This file should NOT be your .muttc # as everything but aliasses are deleted!! # uncomment and adapt the following line # ALIASFILE="/home/moritz/testalias" # ##************************************* # Known bugs: # If name containing @ is quoted("") before a mail adress, # the program will not catch the whole name, # but who would alias such an idiot? # # Probably some more, mail me if you find one! import re import string, sys, os import curses, traceback ###Thanks for the follwing go to Michael P. Reilly if not sys.stdin.isatty(): # is it not attached to a terminal? file = sys.stdin.read() sys.stdin = _dev_tty = open('/dev/tty', 'w+') # close the file descriptor for stdin thru the posix module os.close(0) # now we duplicate the opened _dev_tty file at file descriptor 0 (fd0) # really, dup creates the duplicate at the lowest numbered, closed file # descriptor, but since we just closed fd0, we know we will dup to fd0 os.dup(_dev_tty.fileno()) # on UNIX, the fileno() method returns the # file object's file descriptor else: # is a terminal print "Please use as a pipe!" print "Aborting..." sys.exit() # now standard input points to the terminal again, at the C level, not just # at the Python level. print "Looking for mail adresses, this may take a while..." #### define functions class screenC: "Class to create a simple to use menu using curses" def __init__(self): import curses, traceback self.MAXSECT = 0 self.LSECT = 1 # Initialize curses self.stdscr=curses.initscr() # Turn off echoing of keys, and enter cbreak mode, # where no buffering is performed on keyboard input curses.noecho() curses.cbreak() # In keypad mode, escape sequences for special keys # (like the cursor keys) will be interpreted and # a special value like curses.KEY_LEFT will be returned self.stdscr.keypad(1) def titel(self,TITEL="Title - test",HELP="Use up and down arrows + Return"): "Draws Title and Instructions" self.stdscr.addstr(0,0,TITEL,curses.A_UNDERLINE) # Title + Help self.stdscr.addstr(self.Y -2 ,0,HELP,curses.A_REVERSE) def refresh(self): self.stdscr.refresh() def size(self): "Returns screen size and cursor position" #Y, X = 0, 0 self.Y, self.X = self.stdscr.getmaxyx() #self.y, self.x = 0, 0 self.y, self.x = self.stdscr.getyx() #print Y, X return self.Y, self.X, self.y, self.x def showlist(self,LISTE,LSECT=1): "Analyzes list, calculates screen, draws current part of list on screen " s = self.Y -3 #space on screen self.MAXSECT=1 while len(LISTE) > self.MAXSECT * s : # how many times do we need the screen? self.MAXSECT = self.MAXSECT +1 if self.LSECT > self.MAXSECT: #Ende der Liste pr�fen self.LSECT = LSECT -1 if self.LSECT <= 0: # self.LSECT =1 if len(LISTE) <= s: self.LISTPART=LISTE else : self.LISTPART=LISTE[s * ( self.LSECT -1 ) : s * self.LSECT ] # part of the List is shown self.stdscr.addstr(self.Y -2, self.X -len(`self.LSECT`+`self.MAXSECT`) -5, "(" + `self.LSECT` + "/" + `self.MAXSECT` + ")") #if len(LISTE) > self.Y - 3: # return 1 for i in range (1, self.Y -2): # clear screen between title and help text self.stdscr.move(i , 0) self.stdscr.clrtoeol() for i in range (0,len(self.LISTPART)): # print out current part of list Item = self.LISTPART[i] self.stdscr.addstr(i +1, 0, Item[:self.X]) def getresult(self,HOEHE): "Get Result from cursor position" RESULT= self.LISTPART[(HOEHE -1)] return RESULT def showresult(self, HOEHE, DICT={}): "Look up result to show in dictionary if provided, return list member otherwise" if DICT=={}: return self.getresult(HOEHE) else : return string.join(DICT[self.getresult(HOEHE)], " || ") def menucall(self, LISTE, DICT={}, TITEL="",HELP="Use up and down arrows, Return to select"): "Takes a list and offers a menu where you can choose one item, optionally, look up result in dictionary if provided" REFY=1 self.__init__() self.size() self.titel(TITEL,HELP) self.showlist(LISTE) self.refresh() self.stdscr.move(1,0) while 1: # read Key events c = self.stdscr.getch() self.size() #if c == curses.KEY_LEFT and self.x > 0: # self.stdscr.move(self.y, self.x -1); REFY = 1 # REFY == refresh: yes #if c == curses.KEY_RIGHT and self.x < self.X -1: # #if x < 4 and LENGTH - ZAHLY > y - 1: # self.stdscr.move(self.y, self.x + 1); REFY = 1 if c == curses.KEY_UP: if self.y > 1: self.stdscr.move(self.y -1, self.x); REFY = 1 else : self.LSECT=self.LSECT-1 self.showlist(LISTE,self.LSECT) self.stdscr.move(len(self.LISTPART), 0) REFY = 1 if c == curses.KEY_DOWN: if self.y < len(self.LISTPART) : self.stdscr.move(self.y +1, self.x); REFY = 1 else : self.LSECT=self.LSECT+1 self.showlist(LISTE,self.LSECT) self.stdscr.move(1,0) REFY = 1 if c == 10 : # \n (new line) ERG = self.getresult(self.y ) self.end() return ERG if c == 113: # "q" self.end() print "BREAK" return 0 if REFY == 1: REFY = 0 self.size() self.stdscr.move(self.Y -1, 0) self.stdscr.clrtoeol() self.stdscr.addstr(self.Y -1, 0, self.showresult(self.y,DICT)[:self.X -1 ], curses.A_BOLD) self.stdscr.move(self.y, self.x) self.refresh() def end(self): "Return terminal" # In the event of an error, restore the terminal # to a sane state. self.Y, self.X, self.y, self.x = 0, 0, 0, 0 self.LISTPART=[] self.stdscr.keypad(0) curses.echo() curses.nocbreak() curses.endwin() #traceback.print_exc() def input(self, promptstr): "raw_input equivalent in curses, asks for Input and returns it" self.size() curses.echo() self.stdscr.move(0,0) self.stdscr.clear() self.stdscr.addstr(promptstr) self.refresh() INPUT=self.stdscr.getstr() curses.noecho() return INPUT def printoutnwait(self, promptstr): "Print out Text, wait for key" curses.noecho() self.stdscr.move(0,0) self.stdscr.clear() self.stdscr.addstr(promptstr+"\n\n(press key)") self.refresh() while 1: # read Key events c = self.stdscr.getch() if c == None: self.refresh() else: return 1 def analyzealiasfile(ALIASFILE): "Looks at a mutt aliasfile, returns a list of aliasses and of mail adresses" f=open(ALIASFILE, "r") Aliasline= " " AL_Adlist=[] AL_Namlist=[] while Aliasline != "": Aliasline=f.readline() Aliassplit =string.split(Aliasline) if len(Aliassplit) > 1: if Aliassplit[0] == "alias": AL_Namlist.append(Aliassplit[1]) for i in range(len(Aliassplit)): if "@" in Aliassplit[i]: Aliasadress = Aliassplit[i] AL_Adlist.append(Aliasadress) f.close() return AL_Adlist, AL_Namlist def listrex (str, rgx): # Return a list of all regexes matched in string "Search string for all occurences of regex and return a list of them." result = [] start = 0 # set counter to zero ende =len (str) #set end position suchadress = re.compile(rgx,re.LOCALE)#compile regular expression, with LOCALE while 1: einzelerg = suchadress.search(str, start,ende) #create Match Object einzelerg if einzelerg == None:#until none is found break result.append(einzelerg.group()) #add to result start = einzelerg.end() return result def strrex (str): # Return first occurence of regular exp as string "Search string for first occurence of regular expression and return it" muster = re.compile(r"<?[\w\b.����-]+\@[\w.-]+>?", re.LOCALE) #compile re matobj = muster.search(str) #get Match Objekt from string if muster.search(str) == None: #if none found return "" return matobj.group() #return string def stripempty (str):#Looks for all empty charcters and replaces them with a space "Looks for all empty charcters and replaces them with a space,kills trailing" p = re.compile( "\s+") #shorten shrt = p.sub(" ", str) q = re.compile("^\s+|\s+$") #strip strp = q.sub("", shrt) return strp def getmailadressfromstring(str): "Takes str and gets the first word containing @ == mail adress" StringSplit=string.split(str) for i in range(len(StringSplit)): if "@" in StringSplit[i]: return StringSplit[i] return None ### main program ExAlAdr,ExAlNam = analyzealiasfile(ALIASFILE) OCCLIST = listrex(file, '"?[\s\w\�\�\�\-\�\_.]*"?\s*<?[\w.-]+\@[\w.-]+>?')#get list, RE gets all Email adresses + prepending words if OCCLIST: print len(OCCLIST),"possible adresses found!." else: print"ERROR, no mails found" sys.exit() for i in range(len(OCCLIST)): #strip all whitespaces + trailing from each list member OCCLIST[i] = string.strip(OCCLIST [i]) OCCDIC={} # Dictionary created to avoid duplicates for i in range(len(OCCLIST)): # iterate over d = OCCLIST[i] Mail = getmailadressfromstring(OCCLIST[i]) #strrex(OCCLIST[i]) #Mailadresse Schnitt = - len(Mail) #cut off mail adress Mail = string.replace(Mail, "<", "")#remove <> Mail = string.replace(Mail, ">", "") if "<"+Mail+">" in ExAlAdr: continue # remove already aliassed mail adresses Name = string.replace (stripempty (d[:Schnitt]), '"', '') #leaves name if not OCCDIC.get(Mail): # if new Emailadress Liste = [] # create list for names Liste.append(Name) # append name OCCDIC[Mail] = Liste # assign list to adress else : Liste = OCCDIC[Mail] # otherwise get list Liste.append(Name) # append name to list of names OCCDIC[Mail] = Liste # and assign KEYS = OCCDIC.keys() #iterate over dictionary, sort names #KEYS are all the adresses for z in range( len(KEYS) ): NAMLIST = OCCDIC[KEYS[z]] # Get list of possible names d = {} # sort out duplicates and # remove bad names + empty strings from adresses for x in NAMLIST: if x in ["", "<"]: continue d[x]=x NAMLIST = d.values() NAMLIST.sort() # sort namelist alphabetically print z, KEYS[z], "had possible names:", NAMLIST # Debugging output OCCDIC[KEYS[z]] = NAMLIST # print "\n" ###sorting def Comparelength(x, y): "Compare number of names in OCCDIC, if equal sort alphabetically." if len(OCCDIC[y]) == len(OCCDIC[x]): return cmp(x, y) if len(OCCDIC[y]) < len(OCCDIC[x]): return -1 else: return 1 KEYS.sort(Comparelength) # Keys sort ###menu ScreenObject=screenC() # initialize curses menu try: ZIELADRESS = ScreenObject.menucall(KEYS, OCCDIC, "Choose adress to alias") if OCCDIC[ZIELADRESS]: LISTNAM=["***ENTER own NAME"] #add option to edit name LISTNAM= LISTNAM + OCCDIC[ZIELADRESS] ZIELNAME = ScreenObject.menucall(LISTNAM, {}, ZIELADRESS + " has which of the possible names?") # empty Dictionary {} means show list member itself, not looked up result else : ZIELNAME="" except: T=ScreenObject.size() ScreenObject.end() traceback.print_exc() print T ### enter new names/aliases if ZIELNAME == "***ENTER own NAME" or ZIELNAME == "": ZIELNAME = ScreenObject.input(`ZIELADRESS` + " = " + `OCCDIC[ZIELADRESS]` + "\n" + `ZIELADRESS` + " gets which name? ") ALIAS = ScreenObject.input("Alias for " + '"' + ZIELNAME + '" <' + ZIELADRESS +"> ?\n") if ALIAS in ExAlNam: #sort out existing aliasses ScreenObject.printoutnwait(ALIAS + " exists already, terminating!") ScreenObject.end() sys.exit() if ALIAS == "": ScreenObject.printoutnwait("Aborted") ScreenObject.end() sys.exit() WRITEALIAS = "alias " + ALIAS +" " + ZIELNAME + " <" + ZIELADRESS + ">" if ScreenObject.menucall(["Yes","No"], {}, "Write: '" + WRITEALIAS +"'?" , ALIASFILE) == "No" : ScreenObject.printoutnwait("Aborted") ScreenObject.end() sys.exit() f = open(ALIASFILE, 'r') ALFILEH=f.readlines() f.close() ALFILEH.append(WRITEALIAS) #append new alias ALFILEH.sort() #sort by alias f = open(ALIASFILE, "w") f.writelines(ALFILEH) f.close() ScreenObject.printoutnwait("OK,\n" + WRITEALIAS + " was added to "+ ALIASFILE + "\nProgam terminated") ScreenObject.end()
