]> git.deb.at Git - pkg/abook.git/blobdiff - contrib/bbdb2xx.cc
Imported Upstream version 0.6.0~pre1
[pkg/abook.git] / contrib / bbdb2xx.cc
diff --git a/contrib/bbdb2xx.cc b/contrib/bbdb2xx.cc
new file mode 100644 (file)
index 0000000..f785edd
--- /dev/null
@@ -0,0 +1,1158 @@
+// ---------------------------------------------------------- -*- c++ -*-
+// bbdb parser and converter
+// supported output format: vcards abook gnokii csv
+//
+// Copyright (C) 1994-2003: Pierre Aubert pierre.aubert@free.fr
+// Code below is dirty. Don't take model on it.
+// 
+// V1.21 add abook format
+// V1.20 adapt for g++-3
+// ----------------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+//
+// ----------------------------------------------------------------------
+
+#include <iostream>
+#include <map>
+#include <algorithm>
+#include <fstream>
+#include <vector>
+#include <string>
+
+// horrible global variable
+size_t counter = 0;
+
+// hold datas from a bbdb record
+struct Id {
+    enum { NAME, FORNAME, AKA, COMPANY, PHONES, ADRESSES, NET, NOTES };
+    // brute force
+    typedef std::vector<std::string> storage_t;
+    storage_t my_d;
+};
+
+
+namespace trace 
+{
+  enum  { 
+      NONE = 0, 
+      SOME = 1 , 
+      VERBOSE = 2 
+  };
+
+  void 
+  print( std::string const& l, int jb, int je ) {
+    char wl[5] = { 27, '[' , '5' , 'm', 0 };
+    char wr[5] = { 27, '[' , '0' , 'm', 0 };
+    int i;
+    for( i=0 ; i<jb ; ++i )
+       std::cerr << l[i];
+    std::cerr << wl;
+    for( i=jb ; i<je ; ++i )
+       std::cerr << l[i];
+    std::cerr << wr;
+    for( i=je ; i<l.size() ; ++i )
+       std::cerr << l[i];
+
+    std::cerr << std::endl;
+  }
+}
+
+// used for translation between format
+struct two_strings {
+    const char * const my_val;
+    const char * const my_trans;
+};
+
+// parse 1 line of bbdb format, of course it is easier in elisp :)
+Id * 
+parse( int const debug, int const  tokmax, char const first_char,  std::string const& line ) {
+
+    Id * id = new Id;
+
+    int jb=0;
+    int je=0;
+    int l=line.size();
+    int toknb=0;
+    
+    // sanity check
+    if( line[jb] == first_char )
+       ++jb;
+    
+    // loop over line
+    // invariant
+    //   jb pointe sur le premier caractere du token
+    //   je pointe sur le premier caractere apres la fin du token
+    while( jb < l ) {
+       // look for beginning of token
+       char c = line[jb];
+       while( jb < l && c != '(' && c != '[' && c != '"' && c != ' ' && !isalpha(c) )
+           ++jb;
+       if( jb == l )
+           break;
+       // current state
+       char state = line[jb];
+       // look for end of token
+       switch( state ) {
+       case '(': 
+       {
+           int cnt_lpar = 1;
+           je = jb+1;
+           for( ; je < l ; ++je ) {
+               if( debug >= 2 )
+                   trace::print(line,jb,je);
+               if( line[je] == ')' ) {
+                   --cnt_lpar;
+                   if( cnt_lpar == 0 ) {
+                       ++je;
+                       break;
+                   }
+               }
+               if( line[je] == '(' )
+                   ++cnt_lpar;
+           }
+           break;
+       }
+       case '[': 
+       {
+           int cnt_lbra = 1;
+           je = jb+1;
+           for( ; je < l ; ++je ) {
+               if( debug >= 2 ) 
+                   trace::print(line,jb,je);
+               if( line[je] == ']' ) {
+                   --cnt_lbra;
+                   if( cnt_lbra == 0 ) {
+                       ++je;
+                       break;
+                   }
+               }
+               if( line[je] == '[' )
+                   ++cnt_lbra;
+           }
+           break;
+       }
+       case '"': 
+       {
+           je = jb+1;
+           while( je < l && line[je] != state )
+               ++je;
+           ++je;
+           break;
+       }
+       case ' ': 
+       {
+           je = jb+1;
+           while( je < l && line[je] != state )
+               ++je;
+           ++je;
+           break;
+       }
+       case 'n': // nil && notes
+       { 
+           je = jb;
+           ++je;
+           if( line[je] == 'i' ) 
+               while( je < l && line[je] != ' ' ) 
+                   ++je;
+           else {
+               while( je < l && line[je] != '.' ) 
+                   ++je;
+               ++je;
+           }
+           break;
+       }
+       case 'c': // creation-date
+       case 't': // timestamp
+       case 'h': // http
+       default:
+       {
+           je = jb;
+           while( je < l && line[je] != '.' )  
+               ++je;
+           ++je;
+           break;
+       }
+       } // end case
+       if( debug >= 1 )
+           trace::print(line,jb,je);
+       // fill structure
+       if( line[jb] == '"' ) 
+           id->my_d.push_back( line.substr(jb+1,je-jb-2) ); 
+       else 
+           id->my_d.push_back( line.substr(jb,je-jb) ); 
+       // loop
+       ++toknb;
+       jb = je + 1;
+    }
+
+    if( toknb < tokmax )
+    {
+       delete id;
+       return NULL;
+    }
+    
+    return id;
+}
+
+// ----------------------------------------------------------------------
+// GNOKII
+// ----------------------------------------------------------------------
+void tognokii( std::string const& l )
+{
+    Id * id = parse( trace::NONE, 9, '[', l);
+    if( id == NULL ) {
+       if( l.size() > 2 )
+           std::cerr << "Broken line ==>" << l << "<==" << std::endl;
+       return;
+    }
+    
+    Id::storage_t const& s = id->my_d;
+    
+    // check phone(s)
+    if( s[Id::PHONES].compare( "nil" ) != 0 ) {
+       Id * phones = new Id;
+       phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
+       if( phones == NULL ) {
+           std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       std::cout << s[Id::NAME] << '\t' << s[Id::FORNAME] << '\t';
+
+       Id * phone = new Id;
+       for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
+           phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
+
+           if( phone != NULL ) {
+               std::cout << phone->my_d[1];
+               std::cout << '\t';
+           }
+       }
+       std::cout << std::endl;
+       delete phone;
+       delete phones;
+    }
+    
+    delete id;
+}
+
+
+
+// ----------------------------------------------------------------------
+// VCARD
+// ----------------------------------------------------------------------
+static two_strings VCARDPhoneTypes [] =
+{
+    // PERSONAL       VCARD
+    { "home",         "home"  },
+    { "tel",          "home"  },
+    { "perso",        "home"  },
+    { "personnel",    "home"  },
+    { "personel",     "home"  },
+    { "monchat",      "home"  },
+    { "msg",          "msg"   },
+    { "mesg",         "msg"   },
+    { "work",         "work"  },
+    { "travail",      "work"  },
+    { "paris",        "work"  },
+    { "lyon",         "work"  },
+    { "issy",         "work"  },
+    { "insa",         "work"  },
+    { "woo",          "work"  },
+    { "fac",          "work"  },
+    { "voice",        "voice" },
+    { "voie",         "voice" },
+    { "fax",          "fax"   },
+    { "cell",         "cell"  },
+    { "portable",     "cell"  },
+    { "gsm",          "cell"  },
+    { "video",        "video" },
+    { "pager",        "pager" },
+    { "tatoo",        "pager" },
+    { "voiture",      "car"   },
+    { "modem",        "modem" },
+    { "isdn",         "isdn"  },
+    { "pcs",          "pcs"   },
+    { 0,              0       }
+};
+
+struct VCARDPhoneTypesMap {
+    static two_strings const my_default [];
+    struct ltstr {
+       inline bool 
+       operator()(const char* s1, const char* s2) const {
+           return strcmp(s1, s2) < 0;
+       }
+    };
+    
+    VCARDPhoneTypesMap() {
+       unsigned short i = 0;
+       while( VCARDPhoneTypes[i].my_val != 0 ) {
+           my_h[ VCARDPhoneTypes[i].my_val ] = VCARDPhoneTypes[i].my_trans;
+           ++i;
+       }
+    }
+    
+    typedef std::map<const char*,const char*,ltstr> hash_t;
+    hash_t my_h;
+};
+
+two_strings const VCARDPhoneTypesMap::my_default [] = 
+    {
+       { "work", "work" },
+       { "fax", "fax" },
+       { "cell", "cell" },
+       { 0, 0 }
+    };
+
+static VCARDPhoneTypesMap vcard_phone_types_map;
+
+void tovcard( std::string const& l )
+{
+    Id * id = parse( trace::NONE, 9,'[',l);
+    if( id == NULL ) {
+       if( l.size() > 2 )
+           std::cerr << "Broken line ==>" << l << "<==" << std::endl;
+       return;
+    }
+    
+    Id::storage_t const& s = id->my_d;
+    
+    // std::string filename( s[Id::FORNAME] + "_" + s[Id::NAME] + ".vcf" );
+    // std::ofstream os( filename.c_str() );
+
+    std::cout << "BEGIN:VCARD" << std::endl;
+    
+    std::cout << "FN:" << s[Id::NAME] << ' ' << s[Id::FORNAME] << std::endl;
+    std::cout << "N:" << s[Id::FORNAME] << ';' << s[Id::NAME] << std::endl;
+
+    if( s[Id::PHONES].compare( "nil" ) != 0 ) {
+       Id * phones = new Id;
+       phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
+       if( phones == NULL ) {
+           std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       Id * phone = new Id;
+       for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
+           phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
+           if( phone != NULL ) {
+               std::cout << "TEL";
+               if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos ) {
+                   VCARDPhoneTypesMap::hash_t::iterator it;
+                   if( (it=vcard_phone_types_map.my_h.find( const_cast<char*>(phone->my_d[0].c_str()))) != vcard_phone_types_map.my_h.end() ) {
+                       std::cout << ";" << it->second;
+                   } else {
+                       std::cout << ";" << vcard_phone_types_map.my_default[0].my_trans;
+                   }
+               }
+               if( phone->my_d.size() > 1 )
+                   std::cout << ':' << phone->my_d[1] << std::endl;
+               else 
+                   std::cerr << "phone size is less than 2" << std::endl;
+           }
+       }
+       delete phone;
+       delete phones;
+    }
+
+    if( s[Id::ADRESSES].compare( "nil" ) != 0 ) {
+       Id * adresses = new Id;
+       adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
+       if( adresses == NULL ) {
+           std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       Id * adresse = new Id;
+       for( int cur_adresse = 0 ; cur_adresse < adresses->my_d.size() ; ++cur_adresse ) {
+           adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );
+           if( adresse != NULL ) {
+               // split again 
+               Id * fields = new Id;
+               fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
+               if( fields != NULL ) {
+                   std::string const& town    = adresse->my_d[2];
+                   std::string const& state   = adresse->my_d[3];
+                   std::string const& postal  = adresse->my_d[4];
+                   std::string const& country = adresse->my_d[5];
+                   std::cout << "ADR";
+                   if( adresse->my_d[0].size() > 0 )
+                       if( adresse->my_d[0].find_first_of( "0123456789 _-:;," ) == adresse->my_d[0].npos ) 
+                           std::cout << ";TYPE=" << adresse->my_d[0];
+                   std::cout << ':' << ';' << ';' << fields->my_d[0];
+                   for( int i=1 ; i<fields->my_d.size() ; ++i )
+                       std::cout << ',' << fields->my_d[i] ;
+                   std::cout << ';' << town << ';' << state << ';' << postal << ';' << country << ';' << std::endl;
+               }
+               else
+                   std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] << "<<=" << std::endl;
+               
+               delete fields;
+           }
+       }
+       delete adresse;
+       delete adresses;
+    }
+
+    if( s[Id::NET].compare("nil") != 0 ) {
+       Id * nets = new Id;
+       nets = parse( trace::NONE, 1, '(', s[Id::NET] );
+       if( nets == NULL ) {
+           std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       std::cout << "EMAIL;INTERNET;PREF:" << nets->my_d[0] << std::endl;
+       for( int cur_net = 1 ; cur_net < nets->my_d.size() ; ++cur_net )
+           std::cout << "EMAIL;INTERNET:" << nets->my_d[cur_net] << std::endl;
+
+       delete nets;
+    }
+    
+    if( s[Id::COMPANY].compare("nil") != 0 )
+       std::cout << "ORG:" << s[Id::COMPANY] << std::endl;
+    
+    if( s[Id::AKA].compare("nil") != 0 ) {
+       Id * akas = new Id;
+       if( s[Id::AKA].find('(') == s[Id::AKA].npos )
+           std::cout << "NICKNAME:" << s[Id::AKA] << std::endl;
+       else
+       {
+           akas = parse( trace::NONE, 1, '(', s[Id::AKA] );
+           if( akas == NULL ) {
+               std::cerr << "can't parse akas ==>" << s[Id::AKA] << "<==" << std::endl;
+               delete id;
+               return;
+           }
+           
+           Id * aka = new Id;
+           for( int cur_aka = 0 ; cur_aka < akas->my_d.size() ; ++cur_aka ) {
+               aka = parse( trace::NONE, 1, '(', akas->my_d[cur_aka] );
+               if( aka != NULL )
+                   std::cout << "NICKNAME:" << aka->my_d[0] << std::endl;
+           }
+           delete aka;
+       }
+       delete akas;
+    }
+    
+    if( s[Id::NOTES].compare("nil") != 0 ) {
+       Id * notes = new Id;
+       if( s[Id::NOTES].find('(') == s[Id::NOTES].npos ) 
+           std::cout << "NOTE:" << s[Id::NOTES] << std::endl;
+       else {
+           notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
+           if( notes == NULL ) {
+               std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" << std::endl;
+               delete id;
+               return;
+           }
+           
+           Id * note = new Id;
+           for( int cur_note = 0 ; cur_note < notes->my_d.size() ; ++cur_note ) {
+               note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
+               if( note != NULL ) {
+                   std::string const& t = note->my_d[0];
+                   std::string const& r = note->my_d[1];
+
+                   if( t.find("creation-date") != t.npos || t.find("timestamp") != t.npos ) {
+                       ; // drop 
+                   } else if( t.find("www") != t.npos || t.find("http") != t.npos ) {
+                       std::cout << "URL:" << r << std::endl;
+                   } else if( t.find("notes") != t.npos ) {
+                       std::cout << "NOTE:" << r << std::endl;
+                   } else if( t.find("icq") != t.npos ) {
+                       std::cout << "NOTE: ICQ #" << r << std::endl;
+                   } else if( t.find("msn") != t.npos ) {
+                       std::cout << "NOTE: MSN " << r << std::endl;
+                   } else {
+                       std::cerr << "drop notes: " << t << " for " << s[Id::NAME] << std::endl;
+                   }
+               }
+           }
+           delete note;
+       }
+       delete notes;
+    }
+    
+    std::cout << "END:VCARD" << std::endl << std::endl;
+    delete id;
+    // os.close();
+}
+
+
+// ----------------------------------------------------------------------
+// ABOOK
+// ----------------------------------------------------------------------
+
+static two_strings ABOOKPhoneTypes [] =
+{
+    { "home",         "phone" },
+    { "tel",          "phone" },
+    { "perso",        "phone" },
+    { "personnel",    "phone" },
+    { "personel",     "phone" },
+    { "monchat",      "phone" },
+    { "msg",          "phone" },
+    { "mesg",         "phone" },
+    { "work",         "workphone" },
+    { "travail",      "workphone" },
+    { "paris",        "workphone" },
+    { "lyon",         "workphone" },
+    { "issy",         "workphone" },
+    { "insa",         "workphone" },
+    { "woo",          "workphone" },
+    { "fac",          "workphone" },
+    { "voice",        "phone" },
+    { "voie",         "phone" },
+    { "fax",          "fax"   },
+    { "cell",         "mobile" },
+    { "portable",     "mobile" },
+    { "gsm",          "mobile" },
+    { "video",        "phone" },
+    { "pager",        "phone" },
+    { "tatoo",        "phone" },
+    { "voiture",      "phone" },
+    { "modem",        "phone" },
+    { "isdn",         "phone" },
+    { "pcs",          "phone" },
+    { 0,              0       }
+};
+
+struct ABOOKPhoneTypesMap {
+    static two_strings const my_default [];
+    struct ltstr {
+       inline bool 
+       operator()(const char* s1, const char* s2) const {
+           return strcmp(s1, s2) < 0;
+       }
+    };
+    
+    ABOOKPhoneTypesMap() {
+       unsigned short i = 0;
+       while( ABOOKPhoneTypes[i].my_val != 0 ) {
+           my_h[ ABOOKPhoneTypes[i].my_val ] = ABOOKPhoneTypes[i].my_trans;
+           ++i;
+       }
+    }
+    
+    typedef std::map<const char*,const char*,ltstr> hash_t;
+    hash_t my_h;
+};
+
+two_strings const ABOOKPhoneTypesMap::my_default [] = {
+    // VCARD   ABOOK
+    { "*",  "phone" },
+    { 0, 0 }
+};
+
+static ABOOKPhoneTypesMap abook_phone_types_map;
+
+void toabook( std::string const& l ) {
+    Id * id = parse( trace::NONE, 9,'[',l);
+    if( id == NULL ) {
+       if( l.size() > 2 )
+           std::cerr << "Broken line ==>" << l << "<==" << std::endl;
+       return;
+    }
+    
+    Id::storage_t const& s = id->my_d;
+    
+    std::cout << '[' << counter << ']' << std::endl;
+    ++counter;
+    
+    std::cout << "name=" << s[Id::FORNAME] << ' ' << s[Id::NAME] << std::endl;
+
+    if( s[Id::NET].compare("nil") != 0 ) {
+       Id * nets = new Id;
+       nets = parse( trace::NONE, 1, '(', s[Id::NET] );
+       if( nets == NULL ) {
+           std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       std::cout << "email=" << nets->my_d[0];
+       for( int cur_net = 1 ; cur_net < nets->my_d.size() ; ++cur_net ) {
+           std::cout << "," << nets->my_d[cur_net];
+       }
+       std::cout << std::endl;
+
+       delete nets;
+    }
+    
+    if( s[Id::PHONES].compare( "nil" ) != 0 ) {
+       Id * phones = new Id;
+       phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
+       if( phones == NULL ) {
+           std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       Id * phone = new Id;
+       for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
+           phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
+           if( phone != NULL ) {
+               if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos ) {
+                   ABOOKPhoneTypesMap::hash_t::iterator it;
+                   if( (it=abook_phone_types_map.my_h.find( const_cast<char*>(phone->my_d[0].c_str())))
+                       != abook_phone_types_map.my_h.end() ) {
+                       std::cout << it->second;
+                   } else {
+                       std::cout << abook_phone_types_map.my_default[0].my_trans;
+                   }
+               }
+               if( phone->my_d.size() > 1 )
+                   std::cout << '=' << phone->my_d[1] << std::endl;
+               else
+                   std::cerr << "phone size is less than 2" << std::endl;
+           }
+       }
+       delete phone;
+       delete phones;
+    }
+
+    if( s[Id::ADRESSES].compare( "nil" ) != 0 ) {
+       Id * adresses = new Id;
+       adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
+       if( adresses == NULL ) {
+           std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       Id * adresse = new Id;
+       for( int cur_adresse = 0 ; cur_adresse < adresses->my_d.size() ; ++cur_adresse ) {
+           adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );
+           if( adresse != NULL ) {
+               // split again 
+               Id * fields = new Id;
+               fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
+               if( fields != NULL ) {
+                   std::string const& town    = adresse->my_d[2];
+                   std::string const& state   = adresse->my_d[3];
+                   std::string const& postal  = adresse->my_d[4];
+                   std::string const& country = adresse->my_d[5];
+                       
+                   std::cout << "address=";
+                   for( int i=0 ; i<fields->my_d.size() ; ++i )
+                       std::cout << fields->my_d[i] << ' ';
+                   std::cout << std::endl;
+                   
+                   std::cout << "city=" << town << std::endl;
+                   std::cout << "state=" << state << std::endl;
+                   std::cout << "zip="   << postal << std::endl;
+                   std::cout << "country=" << country << std::endl;
+               } else {
+                   std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] << "<<=" << std::endl;
+               }
+               delete fields;
+           }
+       }
+       delete adresse;
+       delete adresses;
+    }
+
+    if( s[Id::AKA].compare("nil") != 0 )
+    {
+       Id * akas = new Id;
+       if( s[Id::AKA].find('(') == s[Id::AKA].npos )
+       {
+           std::cout << "nick=" << s[Id::AKA] << std::endl;
+       }
+       delete akas;
+    }
+    
+    if( s[Id::NOTES].compare("nil") != 0 ) {
+       Id * notes = new Id;
+       if( s[Id::NOTES].find('(') == s[Id::NOTES].npos ) {
+           std::cout << "notes=" << s[Id::NOTES] << std::endl;
+       } else {
+           notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
+           if( notes == NULL ) {
+               std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" << std::endl;
+               delete id;
+               return;
+           }
+           
+           Id * note = new Id;
+           for( int cur_note = 0 ; cur_note < notes->my_d.size() ; ++cur_note ) {
+               note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
+               
+               if( note != NULL ) {
+                   std::string const& t = note->my_d[0];
+                   std::string const& r = note->my_d[1];
+
+                   if( t.find("creation-date") != t.npos || t.find("timestamp") != t.npos ) {
+                       ; // drop 
+                   } else if( t.find("www") != t.npos || t.find("http") != t.npos ) {
+                       std::cout << "url=" << r << std::endl;
+                   } else if( t.find("notes") != t.npos ) {
+                       std::cout << "note=" << r << std::endl;
+                   } else if( t.find("icq") != t.npos ) {
+                       std::cout << "note=icq #" << r << std::endl;
+                   } else if( t.find("msn") != t.npos ) {
+                       std::cout << "note=msn" << r << std::endl;
+                   } else {
+                       std::cerr << "drop notes: " << t << " for " << s[Id::NAME] << std::endl;
+                   }
+               }
+           }
+           delete note;
+           delete notes;
+       }
+    }
+    
+    std::cout << std::endl;
+    delete id;
+}
+
+// ----------------------------------------------------------------------
+// CSV
+// ----------------------------------------------------------------------
+void tocsv( std::string const& l )
+{
+    char const sep = ',';
+    char const quo = '"';
+
+    Id * id = parse( trace::NONE, 9,'[',l);
+    if( id == NULL )
+    {
+       if( l.size() > 2 )
+           std::cerr << "Broken line ==>" << l << "<==" << std::endl;
+       return;
+    }
+    
+    Id::storage_t const& s = id->my_d;
+
+    // "First Name","Last Name","Display Name"
+    std::cout << quo << s[Id::NAME] << quo << sep;
+    std::cout << quo << s[Id::FORNAME] << quo << sep;
+    std::cout << quo << s[Id::FORNAME] << ' ' << s[Id::NAME] << quo << sep;
+
+    // "Nickname Name"
+    std::string s_aka;
+    if( s[Id::AKA].compare("nil") != 0 )
+    {
+       Id * akas = new Id;
+       if( s[Id::AKA].find('(') == s[Id::AKA].npos )
+       {
+           std::cout << quo << s[Id::AKA] << quo << sep ;
+       }
+       else
+       {
+           akas = parse( trace::NONE, 1, '(', s[Id::AKA] );
+           if( akas == NULL ) 
+           {
+               std::cerr << "can't parse akas ==>" << s[Id::AKA] << "<==" << std::endl;
+               delete id;
+               return;
+           }
+           
+           Id * aka = new Id;
+           aka = parse( trace::NONE, 1, '(', akas->my_d[0] );
+           if( aka != NULL )
+           {
+               s_aka = quo + aka->my_d[0] + quo;
+           }
+           delete aka;
+           delete akas;
+       }
+    }
+    std::cout << s_aka << sep;
+    
+    // "E-mail","Secondary E-mail"
+    std::string s_email1;
+    std::string s_email2;
+    if( s[Id::NET].compare("nil") != 0 )
+    {
+       Id * nets = new Id;
+       nets = parse( trace::NONE, 1, '(', s[Id::NET] );
+       if( nets == NULL ) 
+       {
+           std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       s_email1 = quo + nets->my_d[0] + quo;
+       if( nets->my_d.size() > 1 )
+       {
+           s_email2 = quo + nets->my_d[1] + quo;
+       }
+       delete nets;
+    }
+    std::cout << s_email1 << sep << s_email2 << sep;
+
+    // "Business Phone","Home Phone","Fax Phone","Pager","Mobile Phone"
+    std::string p_business;
+    std::string p_home;
+    std::string p_fax;
+    std::string p_pager;
+    std::string p_mobile;
+
+    if( s[Id::PHONES].compare( "nil" ) != 0 )
+    {
+       Id * phones = new Id;
+       phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
+       if( phones == NULL ) 
+       {
+           std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       Id * phone = new Id;
+       int cur_phone;
+       for( cur_phone = 0 ; cur_phone < std::min(size_t(5),phones->my_d.size()) ; ++cur_phone )
+       {
+           phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
+
+           if( phone != NULL )
+           {
+               std::string trans;
+               if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos )
+               {
+                   ABOOKPhoneTypesMap::hash_t::iterator it;
+                   if( (it=abook_phone_types_map.my_h.find( const_cast<char*>(phone->my_d[0].c_str())))
+                       != abook_phone_types_map.my_h.end() )
+                   {
+                       trans = it->second;
+                   }
+                   else
+                   {
+                       trans = abook_phone_types_map.my_default[0].my_trans;
+                   }
+               }
+               if( trans.find("home") != trans.npos )
+               {
+                   p_home = quo + phone->my_d[1] + quo ;
+               }
+               else if( trans.find("work") != trans.npos )
+               {
+                   p_business = quo + phone->my_d[1] + quo ;
+               }
+               else if( trans.find("cell") != trans.npos )
+               {
+                   p_mobile = quo + phone->my_d[1] + quo ;
+               }
+               else if( trans.find("fax") != trans.npos )
+               {
+                   p_fax = quo + phone->my_d[1] + quo ;
+               }
+               else if( trans.find("pager") != trans.npos )
+               {
+                   p_pager = quo + phone->my_d[1] + quo ;
+               }
+               else 
+               {
+                   if( p_home.size() == 0 )
+                   {
+                       p_home = quo + phone->my_d[1] + quo ;
+                   }
+               }
+           }
+       }
+       delete phone;
+       delete phones;
+    }
+    std::cout << p_business << sep 
+             << p_home << sep 
+             << p_fax << sep 
+             << p_pager << sep 
+             << p_mobile << sep;
+
+    // "Home Street 1","Home Street 2","Home City","Home State","Home Postal Code","Home Country",
+    // "Business Street 1","Business Street 2","Business City","Business State","Business Postal Code","Business Country"
+    if( s[Id::ADRESSES].compare( "nil" ) != 0 )
+    {
+       Id * adresses = new Id;
+       adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
+       if( adresses == NULL ) 
+       {
+           std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
+           delete id;
+           return;
+       }
+
+       Id * adresse = new Id;
+       int cur_adresse;
+       for( cur_adresse = 0 ; cur_adresse < std::min(size_t(2),adresses->my_d.size()) ; ++cur_adresse )
+       {
+           adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );
+
+           if( adresse != NULL )
+           {
+               // split again 
+               Id * fields = new Id;
+               fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
+               if( fields != NULL )
+               {
+                   std::string const& town    = adresse->my_d[2];
+                   std::string const& state   = adresse->my_d[3];
+                   std::string const& postal  = adresse->my_d[4];
+                   std::string const& country = adresse->my_d[5];
+                       
+//                 if( adresse->my_d[0].size() > 0 ) 
+//                 {
+//                     if( adresse->my_d[0].find_first_of( "0123456789 _-:;," ) == 
+//                         adresse->my_d[0].npos ) 
+//                     {
+//                         std::cout << ";TYPE=" << adresse->my_d[0];
+//                     }
+//                 }
+                   int i;
+                   for( i=0 ; i<std::min(size_t(2),fields->my_d.size()) ; ++i )
+                       std::cout << quo << fields->my_d[i] << quo << sep;
+                   for( i=std::min(size_t(2),fields->my_d.size()) ; i<2; ++i )
+                       std::cout << sep;
+                   std::cout << quo << town << quo << sep
+                             << quo << state << quo << sep
+                             << quo << postal << quo << sep
+                             << quo << country << quo << sep;
+               }
+               else
+               {
+                   std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] 
+                             << "<<=" << std::endl;
+               }
+               delete fields;
+           }
+       }
+       for( cur_adresse = std::min(size_t(2),adresses->my_d.size()) ; cur_adresse<2; ++cur_adresse ) 
+       {
+           std::cout << sep << sep << sep << sep << sep << sep;
+       }
+       delete adresse;
+       delete adresses;
+    }
+    else
+    {
+       std::cout << sep << sep << sep << sep << sep << sep
+                 << sep << sep << sep << sep << sep << sep;
+    }
+
+    // "Job Title","Department","Company"
+    std::string s_title;
+    std::string s_departement;
+    std::string s_company;
+    if( s[Id::COMPANY].compare("nil") != 0 )
+    {
+       s_company =  quo + s[Id::COMPANY] + quo;
+    }
+    std::cout << s_title << sep
+             << s_departement << sep
+             << s_company << sep;
+
+    // "Web Page 1","Web Page 2","Birth Year","Birth Month","Birth Day","User Field 1","User Field 2","User Field 3","User Field 4","Notes" 
+    std::string n_web1;
+    std::string n_web2;
+    std::string n_byear;
+    std::string n_bmonth;
+    std::string n_bday;
+    std::string n_user1; // icq
+    std::string n_user2; // im
+    std::string n_user3;
+    std::string n_user4;
+    std::string n_note;
+    
+    if( s[Id::NOTES].compare("nil") != 0 )
+    {
+       Id * notes = new Id;
+       if( s[Id::NOTES].find('(') == s[Id::NOTES].npos )
+       {
+           n_note = quo + s[Id::NOTES] + quo;
+       }
+       else
+       {
+           notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
+           if( notes == NULL ) 
+           {
+               std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" ;
+               delete id;
+               return;
+           }
+           
+           Id * note = new Id;
+           int cur_note;
+           for( cur_note = 0 ; cur_note < std::min(size_t(10),notes->my_d.size()) ; ++cur_note )
+           {
+               note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
+               
+               if( note != NULL )
+               {
+                   std::string const& t = note->my_d[0];
+                   std::string const& r = note->my_d[1];
+
+                   if( t.find("www") != t.npos || t.find("http") != t.npos )
+                   {
+                       if( n_web1.size() == 0 )
+                       {
+                           n_web1 = quo + r + quo;
+                       }
+                       else
+                       {
+                           n_web2 = quo + r + quo;
+                       }
+                   }
+                   else if( t.find("icq") != t.npos )
+                   {
+                       n_user1 = quo + r + quo;
+                   }
+                   else if( t.find("notes") != t.npos )
+                   {
+                       n_note = quo + r + quo;
+                   }
+               }
+           }
+           delete note;
+           delete notes;
+       }
+    }
+
+    std::cout << n_web1  << sep << n_web2   << sep 
+             << n_byear << sep << n_bmonth << sep << n_bday  << sep 
+             << n_user1 << sep << n_user2  << sep << n_user3 << sep << n_user4 << sep 
+             << n_note;
+    
+    std::cout << std::endl;
+    delete id;
+}
+
+
+// ----------------------------------------------------------------------
+// USAGE
+// ----------------------------------------------------------------------
+int 
+usage(int argc, char * argv[] )
+{
+    std::cerr << argv[0] << " -t (vcard|gnokii|csv|abook) -l (fr|en) bbdb.txt" << std::endl;
+    std::cerr << "argc=" << argc << std::endl;
+    
+    return 1;
+}
+
+
+// ----------------------------------------------------------------------
+// main
+// ----------------------------------------------------------------------
+int
+main( int argc, char * argv[] )
+{
+    if( argc < 4 ) 
+    {
+       return usage(argc,argv);
+    }
+
+    char t;
+    char * lg = "en";
+    if( argc == 4 && !strcmp(argv[1],"-t") )
+    {
+       if( !strcmp(argv[2],"vcard") )
+       {
+           t = 'v';
+       } 
+       else if( !strcmp(argv[2],"csv") )
+       {
+           t = 'c';
+       } 
+       else if( !strcmp(argv[2],"gnokii") )
+       {
+           t = 'g';
+       } 
+       else if( !strcmp(argv[2],"abook") )
+       {
+           t = 'a';
+       } 
+       else 
+       {
+           return usage(argc,argv);    
+       }
+    }
+    else if( argc == 6 && !strcmp(argv[3],"-l") )
+    {
+       lg = argv[4];
+       if( strcmp(lg,"fr") && strcmp(argv[4],"en") )
+       {
+           return usage(argc,argv);    
+       }
+    }
+    else
+    {
+       return usage(argc,argv);    
+    }
+     
+    std::vector<std::string> v;
+
+    // open file
+    std::ifstream fs;
+    fs.open( argv[argc-1], std::ios::in );
+    if( ! fs.is_open() )
+    {
+       std::cerr << "can't open " << argv[argc-1] << std::endl;
+       return 1;
+    }
+    while( ! fs.eof() )
+    {
+       char data[1024];
+       fs.getline(data,1024);
+       if( data[0] != ';' )
+       {
+           v.push_back( data );
+       }
+    }
+    fs.close();
+
+    // parse v
+    switch( t )
+    {
+    case 'g':
+       std::for_each( v.begin(), v.end(), tognokii );
+       break;
+    case 'v':
+       std::for_each( v.begin(), v.end(), tovcard );
+       break;
+    case 'a':
+       std::for_each( v.begin(), v.end(), toabook );
+       break;
+    case 'c': {
+       if( !strcmp( lg, "en" ))
+       {
+           std::cout << "\"First Name\",\"Last Name\",\"Display Name\",\"Nickname Name\",\"E-mail\",\"Secondary E-mail\",\"Business Phone\",\"Home Phone\",\"Fax Phone\",\"Pager\",\"Mobile Phone\",\"Home Street 1\",\"Home Street 2\",\"Home City\",\"Home State\",\"Home Postal Code\",\"Home Country\",\"Business Street 1\",\"Business Street 2\",\"Business City\",\"Business State\",\"Business Postal Code\",\"Business Country\",\"Job Title\",\"Department\",\"Company\",\"Web Page 1\",\"Web Page 2\",\"Birth Year\",\"Birth Month\",\"Birth Day\",\"User Field 1\",\"User Field 2\",\"User Field 3\",\"User Field 4\",\"Notes\"" << std::endl;
+       }
+       else 
+       {
+           std::cout << "\"Prénom\",\"Nom\",\"Display Name\",\"Nickname Name\",\"E-mail\",\"Secondary E-mail\",\"Business Phone\",\"Téléphone\",\"Fax Phone\",\"Pager\",\"Mobile Phone\",\"Home Street 1\",\"Home Street 2\",\"Home City\",\"Home State\",\"Home Postal Code\",\"Home Country\",\"Business Street 1\",\"Business Street 2\",\"Business City\",\"Business State\",\"Business Postal Code\",\"Business Country\",\"Job Title\",\"Department\",\"Company\",\"Web Page 1\",\"Web Page 2\",\"Birth Year\",\"Birth Month\",\"Birth Day\",\"User Field 1\",\"User Field 2\",\"User Field 3\",\"User Field 4\",\"Notes\"" << std::endl;
+       }
+       
+       std::for_each( v.begin(), v.end(), tocsv );
+       break;
+    }
+    } // end switch
+
+    // open output file
+
+    return 0;
+}
+