úÎb\éP      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOSafe9;<=$‡LispAble is how we convert from our internal representation of a 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ÍAt the beginning of a BBDB file are a variable number of comments, which specify the encoding type and the version. We just ignore them. Comments starts with a ; (semi-colon) and continue to end of line(The record fields of the BBDB data type pthe first name. Why is this a Maybe? Because sometimes you just have a company, and not a specific first name ¢aka = 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 BA list of phone numbers, either in US Style or International Style &A list of addresses, keyed by locationcA list of email addresses. BBDB uses the first element of this field when you create a new email7Any 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't provide a your own key, the BBDB will use the word "note"’An Alist is an Association List. Lisp writes these as (key . value) We convert these to a tuple in haskell where fst is key and snd is value. nAn Address must have a location, and may have associated streets, a city, a state, a zipcode, and an country.LFor some unknow reason, BBDB can have phones in two different formats. In USStyle¦, the phone is list of integers, in the form of Area code, Prefix, Number, and Extension. I don't bother to convert the strings of digits to actual integers. In InternationalStyle$, the phone number is just a String."Synonym for String#Synonym for String$€Since file-format 9, BBDB now includes there more fields, which | are always present and used internally. | Synonym for String%lA Symbol is just a String, but Lisp only wants alphanumerics and the characters _ (underscore) and - (dash)&dA Street is also a synonym for String. Each Address may have a list of Streets associated with it.'yA Location is just a synonym for String. Each BBDB Address and 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* A BBDB record containing no dataP*return Nothing if parsing the string "nil"+VThe 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" b7You will get IO (Right [BBDBFile]) if the parse went ok,Fconverts a BBDB comment to nothing, and a BBDB entry to just the entry-Freturns a list of only the actual bbdb entries, removing the commentsQ1surround a string with the given two characters R4convert a Haskell string to a string that Lisp likes.parse the string as a BBDB File/xread the given file and call error if the parse failed, otherwise return the entire file as a list of BBDBFile records.0ë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: NhasBirthday :: BBDB -> Bool hasBirthday = wantNote (\x -> key x == "birthday")Swill return True for any BBDB record that has a "birthday" key in it's notes field1sLookup the value whose key is the given string. If found returns Just the value, otherwise Nothing For example: DgetBirthday :: BBDB -> Maybe String getBirthday = getNote "birthday"2ÿ This and filterBBDB are the main functions you should use to manipulate a set of BBDB entries. You supply a function that 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" writeFile "/home/henry/.bbdb-new" $ asLisp . mapBBDB starCompany $ b where starCompany x = case (company x) of Nothing -> x Just y -> x { company = Just ("*" ++ y) }iPrepend a star ("*") to each company field of a BBDB file and write the result out as a new bbdb file.3¿Just like mapBBDB except it filters. You supply a function that 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 -- do regex matching while ignoring case, so "reno" matches "Reno" matches x = match (makeRegexOpts compIgnoreCase defaultExecOpt x :: Regex) ÿ©getReno = do b <- readBBDB "/home/henry/.bbdb" let c = justEntries . filterBBDB hasReno $ b mapM_ print $ map (\a -> (firstName a, lastName a, address a)) c where isReno :: Maybe String -> Bool isReno = maybe False (matches "reno") anyAddressHasReno :: [Address] -> Bool anyAddressHasReno = any id . map (isReno . city) hasReno :: BBDB -> Bool hasReno = maybe False anyAddressHasReno . addressPprint the name and all addresses of anyone in the BBDB file who live in Reno. 4the inverse of bbdbFileParseW  !"#$%&'STUV()*PWXYZ[\]^_`ab+,-QcdeR./0123456789:;<=>?@4  !"#$%&'()*+,-./01234'&% ! $#"*().+,-/0123<   !"#$%&'STUV()*PWXYZ[\]^_`ab+,-QcdeR./0123456789:;<=>?@f      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdebbdb-0.8-J8ZKrWd5hCZ47wiRFiAQUy Database.BBDBLispAbleasLispBBDBFile BBDBComment BBDBEntryBBDB firstNamelastNameaffixakacompanyphoneaddressnetnoteshashcreation modificationNoteunnoteAlistAddresslocationstreetscitystatezipcodecountryPhoneUSStyleInternationalStyleModificationTime CreationDateHashSymbolStreetLocationkeyvalue bbdbDefault bbdbFileParse justEntry justEntries parseBBDBreadBBDBwantNotegetNotemapBBDB filterBBDB $fLispAble[]$fLispAbleBBDBFile$fLispAbleBBDB$fLispAbleMaybe$fLispAbleNote $fLispAble(,)$fLispAbleMaybe0$fLispAbleAddress$fLispAbleMaybe1$fLispAblePhone$fLispAbleMaybe2$fLispAbleMaybe3 $fLispAble[]0 $fEqPhone $fOrdPhone $fShowPhone $fEqAddress $fOrdAddress $fShowAddress$fEqNote $fOrdNote $fShowNote$fEqBBDB $fOrdBBDB $fShowBBDB $fEqBBDBFile $fOrdBBDBFile$fShowBBDBFilenil surroundWith escapeLispdoubleQuoteChar betweenParens quotedString quotedCharstrings stringOrNil stringsOrNil listOfInts phoneParser phonesParser singleAddressaddressesParser lispSymbolalist notesParser bbdbEntrysurroundWithQuotessurroundWithBracketssurroundWithParens