úÎZ…WW,      !"#$%&'()*+=5convert a Haskell string to a string that Lisp likes ALispAble is how we convert from our internal representation of a G BBDB record, to one that will make Lisp and Emacs happy. (Sans bugs)   testInverse = do $ let inFile = "/home/henry/.bbdb" % actualBBDBFile <- readFile inFile % parsedBBDBdata <- readBBDB inFile + let bbdbDataOut = asLisp parsedBBDBdata ) print $ actualBBDBFile == bbdbDataOut  should print True IAt the beginning of a BBDB file are a variable number of comments, which B specify the encoding type and the version. We just ignore them.  Comments starts with a ;* (semi-colon) and continue to end of line Bthe first name. Why is this a Maybe? Because sometimes you just / have a company, and not a specific first name  Aaka = Also Known As. Sometimes the same email address can match < several users, so BBDB gives you the option of remembering & different names for the same address )The company if any CA list of phone numbers, either in US Style or International Style 'A list of addresses, keyed by location A list of email addresses. G BBDB uses the first element of this field when you create a new email 8Any number of key, value pairs. Great for random data. @The Note field of a BBDB record is just a list of associations.  If you don'5t provide a your own key, the BBDB will use the word "note" <An Alist is an Association List. Lisp writes these as (key B . value) We convert these to a tuple in haskell where fst is key  and snd is value. BAn Address must have a location, and may have associated streets, - a city, a state, a zipcode, and an country. >For some unknow reason, BBDB can have phones in two different  formats. In USStyle-, the phone is list of integers, in the form 4 of Area code, Prefix, Number, and Extension. I don' t bother to 7 convert the strings of digits to actual integers. In  InternationalStyle%, the phone number is just a String. /A Symbol is just a String, but Lisp only wants > alphanumerics and the characters _ (underscore) and - (dash) @A Street is also a synonym for String. Each Address may have a % list of Streets associated with it. @A Location is just a synonym for String. Each BBDB Address and 9 Phone field must be associated with a location, such as home or  work ,-.!Given an Alist, return the key "!Given an Alist, return the value /%return Nothing if parsing the string "nil" 0123456789:$>The Parser for a BBDB file, as it is written on disk. If you  read a .bbdb file with:  < testParse :: FilePath -> IO (Either ParseError [BBDBFile])  testParse filename = do  b <- readFile filename * return $ parse bbdbFileParse "bbdb" b  You will get IO (Right [BBDBFile]) if the parse went ok %Gconverts a BBDB comment to nothing, and a BBDB entry to just the entry &Greturns a list of only the actual bbdb entries, removing the comments ;<2surround a string with the given two characters '8read the given file and call error if the parse failed, A otherwise return the entire file as a list of BBDBFile records. (?Notes inside a BBDB record are awkward to get at. This helper > function digs into the record and applies a function to each @ Alist element of the record. It returns true if it any of the / Alists in the note return true. For example:   hasBirthday :: BBDB -> Bool 4 hasBirthday = wantNote (\x -> key x == "birthday") 0will return True for any BBDB record that has a "birthday" key  in it's notes field )CLookup the value whose key is the given string. If found returns 1 Just the value, otherwise Nothing For example: % getBirthday :: BBDB -> Maybe String " getBirthday = getNote "birthday" *=This and filterBBDB are the main functions you should use to ? manipulate a set of BBDB entries. You supply a function that C applies a transformation on a BBDB record, and this function will > apply that transformation to every BBDBEntry in a BBDB file.  Sample usage:   starCompanies = do % b <- readBBDB "/home/henry/.bbdb" H writeFile "/home/henry/.bbdb-new" $ asLisp . mapBBDB starCompany $ b  where ) starCompany x = case (company x) of  Nothing -> x 1 Just y -> x { company = Just ("*" ++ y) } Prepend a star ("*") to each company + field of a BBDB file and write the result  out as a new bbdb file. +AJust like mapBBDB except it filters. You supply a function that A takes a BBDB record to a Bool, and filterBBDB will return a new > list of BBDBFile that satisfy that condition. Sample usage:   import Text.Regex.Posix D -- do regex matching while ignoring case, so "reno" matches "Reno" L matches x = match (makeRegexOpts compIgnoreCase defaultExecOpt x :: Regex)   getReno = do % b <- readBBDB "/home/henry/.bbdb" 0 let c = justEntries . filterBBDB hasReno $ b D mapM_ print $ map (\a -> (firstName a, lastName a, address a)) c  where $ isReno :: Maybe String -> Bool + isReno = maybe False (matches "reno") , anyAddressHasReno :: [Address] -> Bool 6 anyAddressHasReno = any id . map (isReno . city)  hasReno :: BBDB -> Bool 7 hasReno = maybe False anyAddressHasReno . address <print the name and all addresses of anyone in the BBDB file  who live in Reno. =the inverse of bbdbFileParse ,  !"#$%&'()*+,  #!"$%&'()*++    !"$%&'()*+>      !"#$%&'()*+,-./0123456789:;<=bbdb-0.3 Database.BBDBLispAbleasLispBBDBFile BBDBEntry BBDBCommentBBDB firstNamelastNameakacompanyphoneaddressnetnotesNoteunnoteAlistAddresslocationstreetscitystatezipcodecountryPhoneInternationalStyleUSStyleSymbolStreetLocationkeyvalue bbdbDefault bbdbFileParse justEntry justEntriesreadBBDBwantNotegetNotemapBBDB filterBBDB betweenParens quotedString quotedCharnil stringOrNil stringsOrNil listOfInts phoneParser phonesParser singleAddressaddressesParser lispSymbolalist notesParser bbdbEntryjoin surroundWith $fLispAble[]