]> git.deb.at Git - pkg/abook.git/blob - contrib/bbdb2xx.cc
f785eddb54a30753a08083a4eb2dabab2f36293e
[pkg/abook.git] / contrib / bbdb2xx.cc
1 // ---------------------------------------------------------- -*- c++ -*-
2 // bbdb parser and converter
3 // supported output format: vcards abook gnokii csv
4 //
5 // Copyright (C) 1994-2003: Pierre Aubert pierre.aubert@free.fr
6 // Code below is dirty. Don't take model on it.
7 // 
8 // V1.21 add abook format
9 // V1.20 adapt for g++-3
10 // ----------------------------------------------------------------------
11 //
12 // This library is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU Library General Public
14 // License as published by the Free Software Foundation; either
15 // version 2 of the License, or (at your option) any later version.
16 //
17 // This library is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 // Library General Public License for more details.
21 //
22 // You should have received a copy of the GNU Library General Public
23 // License along with this library; if not, write to the
24 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 // Boston, MA 02111-1307, USA.
26 //
27 // ----------------------------------------------------------------------
28
29 #include <iostream>
30 #include <map>
31 #include <algorithm>
32 #include <fstream>
33 #include <vector>
34 #include <string>
35
36 // horrible global variable
37 size_t counter = 0;
38
39 // hold datas from a bbdb record
40 struct Id {
41     enum { NAME, FORNAME, AKA, COMPANY, PHONES, ADRESSES, NET, NOTES };
42     // brute force
43     typedef std::vector<std::string> storage_t;
44     storage_t my_d;
45 };
46
47
48 namespace trace 
49 {
50   enum  { 
51       NONE = 0, 
52       SOME = 1 , 
53       VERBOSE = 2 
54   };
55
56   void 
57   print( std::string const& l, int jb, int je ) {
58     char wl[5] = { 27, '[' , '5' , 'm', 0 };
59     char wr[5] = { 27, '[' , '0' , 'm', 0 };
60     int i;
61     for( i=0 ; i<jb ; ++i )
62         std::cerr << l[i];
63     std::cerr << wl;
64     for( i=jb ; i<je ; ++i )
65         std::cerr << l[i];
66     std::cerr << wr;
67     for( i=je ; i<l.size() ; ++i )
68         std::cerr << l[i];
69
70     std::cerr << std::endl;
71   }
72 }
73
74 // used for translation between format
75 struct two_strings {
76     const char * const my_val;
77     const char * const my_trans;
78 };
79
80 // parse 1 line of bbdb format, of course it is easier in elisp :)
81 Id * 
82 parse( int const debug, int const  tokmax, char const first_char,  std::string const& line ) {
83
84     Id * id = new Id;
85
86     int jb=0;
87     int je=0;
88     int l=line.size();
89     int toknb=0;
90     
91     // sanity check
92     if( line[jb] == first_char )
93         ++jb;
94     
95     // loop over line
96     // invariant
97     //   jb pointe sur le premier caractere du token
98     //   je pointe sur le premier caractere apres la fin du token
99     while( jb < l ) {
100         // look for beginning of token
101         char c = line[jb];
102         while( jb < l && c != '(' && c != '[' && c != '"' && c != ' ' && !isalpha(c) )
103             ++jb;
104         if( jb == l )
105             break;
106         // current state
107         char state = line[jb];
108         // look for end of token
109         switch( state ) {
110         case '(': 
111         {
112             int cnt_lpar = 1;
113             je = jb+1;
114             for( ; je < l ; ++je ) {
115                 if( debug >= 2 )
116                     trace::print(line,jb,je);
117                 if( line[je] == ')' ) {
118                     --cnt_lpar;
119                     if( cnt_lpar == 0 ) {
120                         ++je;
121                         break;
122                     }
123                 }
124                 if( line[je] == '(' )
125                     ++cnt_lpar;
126             }
127             break;
128         }
129         case '[': 
130         {
131             int cnt_lbra = 1;
132             je = jb+1;
133             for( ; je < l ; ++je ) {
134                 if( debug >= 2 ) 
135                     trace::print(line,jb,je);
136                 if( line[je] == ']' ) {
137                     --cnt_lbra;
138                     if( cnt_lbra == 0 ) {
139                         ++je;
140                         break;
141                     }
142                 }
143                 if( line[je] == '[' )
144                     ++cnt_lbra;
145             }
146             break;
147         }
148         case '"': 
149         {
150             je = jb+1;
151             while( je < l && line[je] != state )
152                 ++je;
153             ++je;
154             break;
155         }
156         case ' ': 
157         {
158             je = jb+1;
159             while( je < l && line[je] != state )
160                 ++je;
161             ++je;
162             break;
163         }
164         case 'n': // nil && notes
165         { 
166             je = jb;
167             ++je;
168             if( line[je] == 'i' ) 
169                 while( je < l && line[je] != ' ' ) 
170                     ++je;
171             else {
172                 while( je < l && line[je] != '.' ) 
173                     ++je;
174                 ++je;
175             }
176             break;
177         }
178         case 'c': // creation-date
179         case 't': // timestamp
180         case 'h': // http
181         default:
182         {
183             je = jb;
184             while( je < l && line[je] != '.' )  
185                 ++je;
186             ++je;
187             break;
188         }
189         } // end case
190         if( debug >= 1 )
191             trace::print(line,jb,je);
192         // fill structure
193         if( line[jb] == '"' ) 
194             id->my_d.push_back( line.substr(jb+1,je-jb-2) ); 
195         else 
196             id->my_d.push_back( line.substr(jb,je-jb) ); 
197         // loop
198         ++toknb;
199         jb = je + 1;
200     }
201
202     if( toknb < tokmax )
203     {
204         delete id;
205         return NULL;
206     }
207     
208     return id;
209 }
210
211 // ----------------------------------------------------------------------
212 // GNOKII
213 // ----------------------------------------------------------------------
214 void tognokii( std::string const& l )
215 {
216     Id * id = parse( trace::NONE, 9, '[', l);
217     if( id == NULL ) {
218         if( l.size() > 2 )
219             std::cerr << "Broken line ==>" << l << "<==" << std::endl;
220         return;
221     }
222     
223     Id::storage_t const& s = id->my_d;
224     
225     // check phone(s)
226     if( s[Id::PHONES].compare( "nil" ) != 0 ) {
227         Id * phones = new Id;
228         phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
229         if( phones == NULL ) {
230             std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
231             delete id;
232             return;
233         }
234
235         std::cout << s[Id::NAME] << '\t' << s[Id::FORNAME] << '\t';
236
237         Id * phone = new Id;
238         for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
239             phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
240
241             if( phone != NULL ) {
242                 std::cout << phone->my_d[1];
243                 std::cout << '\t';
244             }
245         }
246         std::cout << std::endl;
247         delete phone;
248         delete phones;
249     }
250     
251     delete id;
252 }
253
254
255
256 // ----------------------------------------------------------------------
257 // VCARD
258 // ----------------------------------------------------------------------
259 static two_strings VCARDPhoneTypes [] =
260 {
261     // PERSONAL       VCARD
262     { "home",         "home"  },
263     { "tel",          "home"  },
264     { "perso",        "home"  },
265     { "personnel",    "home"  },
266     { "personel",     "home"  },
267     { "monchat",      "home"  },
268     { "msg",          "msg"   },
269     { "mesg",         "msg"   },
270     { "work",         "work"  },
271     { "travail",      "work"  },
272     { "paris",        "work"  },
273     { "lyon",         "work"  },
274     { "issy",         "work"  },
275     { "insa",         "work"  },
276     { "woo",          "work"  },
277     { "fac",          "work"  },
278     { "voice",        "voice" },
279     { "voie",         "voice" },
280     { "fax",          "fax"   },
281     { "cell",         "cell"  },
282     { "portable",     "cell"  },
283     { "gsm",          "cell"  },
284     { "video",        "video" },
285     { "pager",        "pager" },
286     { "tatoo",        "pager" },
287     { "voiture",      "car"   },
288     { "modem",        "modem" },
289     { "isdn",         "isdn"  },
290     { "pcs",          "pcs"   },
291     { 0,              0       }
292 };
293
294 struct VCARDPhoneTypesMap {
295     static two_strings const my_default [];
296     struct ltstr {
297         inline bool 
298         operator()(const char* s1, const char* s2) const {
299             return strcmp(s1, s2) < 0;
300         }
301     };
302     
303     VCARDPhoneTypesMap() {
304         unsigned short i = 0;
305         while( VCARDPhoneTypes[i].my_val != 0 ) {
306             my_h[ VCARDPhoneTypes[i].my_val ] = VCARDPhoneTypes[i].my_trans;
307             ++i;
308         }
309     }
310     
311     typedef std::map<const char*,const char*,ltstr> hash_t;
312     hash_t my_h;
313 };
314
315 two_strings const VCARDPhoneTypesMap::my_default [] = 
316     {
317         { "work", "work" },
318         { "fax", "fax" },
319         { "cell", "cell" },
320         { 0, 0 }
321     };
322
323 static VCARDPhoneTypesMap vcard_phone_types_map;
324
325 void tovcard( std::string const& l )
326 {
327     Id * id = parse( trace::NONE, 9,'[',l);
328     if( id == NULL ) {
329         if( l.size() > 2 )
330             std::cerr << "Broken line ==>" << l << "<==" << std::endl;
331         return;
332     }
333     
334     Id::storage_t const& s = id->my_d;
335     
336     // std::string filename( s[Id::FORNAME] + "_" + s[Id::NAME] + ".vcf" );
337     // std::ofstream os( filename.c_str() );
338
339     std::cout << "BEGIN:VCARD" << std::endl;
340     
341     std::cout << "FN:" << s[Id::NAME] << ' ' << s[Id::FORNAME] << std::endl;
342     std::cout << "N:" << s[Id::FORNAME] << ';' << s[Id::NAME] << std::endl;
343
344     if( s[Id::PHONES].compare( "nil" ) != 0 ) {
345         Id * phones = new Id;
346         phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
347         if( phones == NULL ) {
348             std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
349             delete id;
350             return;
351         }
352
353         Id * phone = new Id;
354         for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
355             phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
356             if( phone != NULL ) {
357                 std::cout << "TEL";
358                 if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos ) {
359                     VCARDPhoneTypesMap::hash_t::iterator it;
360                     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() ) {
361                         std::cout << ";" << it->second;
362                     } else {
363                         std::cout << ";" << vcard_phone_types_map.my_default[0].my_trans;
364                     }
365                 }
366                 if( phone->my_d.size() > 1 )
367                     std::cout << ':' << phone->my_d[1] << std::endl;
368                 else 
369                     std::cerr << "phone size is less than 2" << std::endl;
370             }
371         }
372         delete phone;
373         delete phones;
374     }
375
376     if( s[Id::ADRESSES].compare( "nil" ) != 0 ) {
377         Id * adresses = new Id;
378         adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
379         if( adresses == NULL ) {
380             std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
381             delete id;
382             return;
383         }
384
385         Id * adresse = new Id;
386         for( int cur_adresse = 0 ; cur_adresse < adresses->my_d.size() ; ++cur_adresse ) {
387             adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );
388             if( adresse != NULL ) {
389                 // split again 
390                 Id * fields = new Id;
391                 fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
392                 if( fields != NULL ) {
393                     std::string const& town    = adresse->my_d[2];
394                     std::string const& state   = adresse->my_d[3];
395                     std::string const& postal  = adresse->my_d[4];
396                     std::string const& country = adresse->my_d[5];
397                     std::cout << "ADR";
398                     if( adresse->my_d[0].size() > 0 )
399                         if( adresse->my_d[0].find_first_of( "0123456789 _-:;," ) == adresse->my_d[0].npos ) 
400                             std::cout << ";TYPE=" << adresse->my_d[0];
401                     std::cout << ':' << ';' << ';' << fields->my_d[0];
402                     for( int i=1 ; i<fields->my_d.size() ; ++i )
403                         std::cout << ',' << fields->my_d[i] ;
404                     std::cout << ';' << town << ';' << state << ';' << postal << ';' << country << ';' << std::endl;
405                 }
406                 else
407                     std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] << "<<=" << std::endl;
408                 
409                 delete fields;
410             }
411         }
412         delete adresse;
413         delete adresses;
414     }
415
416     if( s[Id::NET].compare("nil") != 0 ) {
417         Id * nets = new Id;
418         nets = parse( trace::NONE, 1, '(', s[Id::NET] );
419         if( nets == NULL ) {
420             std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
421             delete id;
422             return;
423         }
424
425         std::cout << "EMAIL;INTERNET;PREF:" << nets->my_d[0] << std::endl;
426         for( int cur_net = 1 ; cur_net < nets->my_d.size() ; ++cur_net )
427             std::cout << "EMAIL;INTERNET:" << nets->my_d[cur_net] << std::endl;
428
429         delete nets;
430     }
431     
432     if( s[Id::COMPANY].compare("nil") != 0 )
433         std::cout << "ORG:" << s[Id::COMPANY] << std::endl;
434     
435     if( s[Id::AKA].compare("nil") != 0 ) {
436         Id * akas = new Id;
437         if( s[Id::AKA].find('(') == s[Id::AKA].npos )
438             std::cout << "NICKNAME:" << s[Id::AKA] << std::endl;
439         else
440         {
441             akas = parse( trace::NONE, 1, '(', s[Id::AKA] );
442             if( akas == NULL ) {
443                 std::cerr << "can't parse akas ==>" << s[Id::AKA] << "<==" << std::endl;
444                 delete id;
445                 return;
446             }
447             
448             Id * aka = new Id;
449             for( int cur_aka = 0 ; cur_aka < akas->my_d.size() ; ++cur_aka ) {
450                 aka = parse( trace::NONE, 1, '(', akas->my_d[cur_aka] );
451                 if( aka != NULL )
452                     std::cout << "NICKNAME:" << aka->my_d[0] << std::endl;
453             }
454             delete aka;
455         }
456         delete akas;
457     }
458     
459     if( s[Id::NOTES].compare("nil") != 0 ) {
460         Id * notes = new Id;
461         if( s[Id::NOTES].find('(') == s[Id::NOTES].npos ) 
462             std::cout << "NOTE:" << s[Id::NOTES] << std::endl;
463         else {
464             notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
465             if( notes == NULL ) {
466                 std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" << std::endl;
467                 delete id;
468                 return;
469             }
470             
471             Id * note = new Id;
472             for( int cur_note = 0 ; cur_note < notes->my_d.size() ; ++cur_note ) {
473                 note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
474                 if( note != NULL ) {
475                     std::string const& t = note->my_d[0];
476                     std::string const& r = note->my_d[1];
477
478                     if( t.find("creation-date") != t.npos || t.find("timestamp") != t.npos ) {
479                         ; // drop 
480                     } else if( t.find("www") != t.npos || t.find("http") != t.npos ) {
481                         std::cout << "URL:" << r << std::endl;
482                     } else if( t.find("notes") != t.npos ) {
483                         std::cout << "NOTE:" << r << std::endl;
484                     } else if( t.find("icq") != t.npos ) {
485                         std::cout << "NOTE: ICQ #" << r << std::endl;
486                     } else if( t.find("msn") != t.npos ) {
487                         std::cout << "NOTE: MSN " << r << std::endl;
488                     } else {
489                         std::cerr << "drop notes: " << t << " for " << s[Id::NAME] << std::endl;
490                     }
491                 }
492             }
493             delete note;
494         }
495         delete notes;
496     }
497     
498     std::cout << "END:VCARD" << std::endl << std::endl;
499     delete id;
500     // os.close();
501 }
502
503
504 // ----------------------------------------------------------------------
505 // ABOOK
506 // ----------------------------------------------------------------------
507
508 static two_strings ABOOKPhoneTypes [] =
509 {
510     { "home",         "phone" },
511     { "tel",          "phone" },
512     { "perso",        "phone" },
513     { "personnel",    "phone" },
514     { "personel",     "phone" },
515     { "monchat",      "phone" },
516     { "msg",          "phone" },
517     { "mesg",         "phone" },
518     { "work",         "workphone" },
519     { "travail",      "workphone" },
520     { "paris",        "workphone" },
521     { "lyon",         "workphone" },
522     { "issy",         "workphone" },
523     { "insa",         "workphone" },
524     { "woo",          "workphone" },
525     { "fac",          "workphone" },
526     { "voice",        "phone" },
527     { "voie",         "phone" },
528     { "fax",          "fax"   },
529     { "cell",         "mobile" },
530     { "portable",     "mobile" },
531     { "gsm",          "mobile" },
532     { "video",        "phone" },
533     { "pager",        "phone" },
534     { "tatoo",        "phone" },
535     { "voiture",      "phone" },
536     { "modem",        "phone" },
537     { "isdn",         "phone" },
538     { "pcs",          "phone" },
539     { 0,              0       }
540 };
541
542 struct ABOOKPhoneTypesMap {
543     static two_strings const my_default [];
544     struct ltstr {
545         inline bool 
546         operator()(const char* s1, const char* s2) const {
547             return strcmp(s1, s2) < 0;
548         }
549     };
550     
551     ABOOKPhoneTypesMap() {
552         unsigned short i = 0;
553         while( ABOOKPhoneTypes[i].my_val != 0 ) {
554             my_h[ ABOOKPhoneTypes[i].my_val ] = ABOOKPhoneTypes[i].my_trans;
555             ++i;
556         }
557     }
558     
559     typedef std::map<const char*,const char*,ltstr> hash_t;
560     hash_t my_h;
561 };
562
563 two_strings const ABOOKPhoneTypesMap::my_default [] = {
564     // VCARD   ABOOK
565     { "*",  "phone" },
566     { 0, 0 }
567 };
568
569 static ABOOKPhoneTypesMap abook_phone_types_map;
570
571 void toabook( std::string const& l ) {
572     Id * id = parse( trace::NONE, 9,'[',l);
573     if( id == NULL ) {
574         if( l.size() > 2 )
575             std::cerr << "Broken line ==>" << l << "<==" << std::endl;
576         return;
577     }
578     
579     Id::storage_t const& s = id->my_d;
580     
581     std::cout << '[' << counter << ']' << std::endl;
582     ++counter;
583     
584     std::cout << "name=" << s[Id::FORNAME] << ' ' << s[Id::NAME] << std::endl;
585
586     if( s[Id::NET].compare("nil") != 0 ) {
587         Id * nets = new Id;
588         nets = parse( trace::NONE, 1, '(', s[Id::NET] );
589         if( nets == NULL ) {
590             std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
591             delete id;
592             return;
593         }
594
595         std::cout << "email=" << nets->my_d[0];
596         for( int cur_net = 1 ; cur_net < nets->my_d.size() ; ++cur_net ) {
597             std::cout << "," << nets->my_d[cur_net];
598         }
599         std::cout << std::endl;
600
601         delete nets;
602     }
603     
604     if( s[Id::PHONES].compare( "nil" ) != 0 ) {
605         Id * phones = new Id;
606         phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
607         if( phones == NULL ) {
608             std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
609             delete id;
610             return;
611         }
612
613         Id * phone = new Id;
614         for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
615             phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
616             if( phone != NULL ) {
617                 if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos ) {
618                     ABOOKPhoneTypesMap::hash_t::iterator it;
619                     if( (it=abook_phone_types_map.my_h.find( const_cast<char*>(phone->my_d[0].c_str())))
620                         != abook_phone_types_map.my_h.end() ) {
621                         std::cout << it->second;
622                     } else {
623                         std::cout << abook_phone_types_map.my_default[0].my_trans;
624                     }
625                 }
626                 if( phone->my_d.size() > 1 )
627                     std::cout << '=' << phone->my_d[1] << std::endl;
628                 else
629                     std::cerr << "phone size is less than 2" << std::endl;
630             }
631         }
632         delete phone;
633         delete phones;
634     }
635
636     if( s[Id::ADRESSES].compare( "nil" ) != 0 ) {
637         Id * adresses = new Id;
638         adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
639         if( adresses == NULL ) {
640             std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
641             delete id;
642             return;
643         }
644
645         Id * adresse = new Id;
646         for( int cur_adresse = 0 ; cur_adresse < adresses->my_d.size() ; ++cur_adresse ) {
647             adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );
648             if( adresse != NULL ) {
649                 // split again 
650                 Id * fields = new Id;
651                 fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
652                 if( fields != NULL ) {
653                     std::string const& town    = adresse->my_d[2];
654                     std::string const& state   = adresse->my_d[3];
655                     std::string const& postal  = adresse->my_d[4];
656                     std::string const& country = adresse->my_d[5];
657                         
658                     std::cout << "address=";
659                     for( int i=0 ; i<fields->my_d.size() ; ++i )
660                         std::cout << fields->my_d[i] << ' ';
661                     std::cout << std::endl;
662                     
663                     std::cout << "city=" << town << std::endl;
664                     std::cout << "state=" << state << std::endl;
665                     std::cout << "zip="   << postal << std::endl;
666                     std::cout << "country=" << country << std::endl;
667                 } else {
668                     std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] << "<<=" << std::endl;
669                 }
670                 delete fields;
671             }
672         }
673         delete adresse;
674         delete adresses;
675     }
676
677     if( s[Id::AKA].compare("nil") != 0 )
678     {
679         Id * akas = new Id;
680         if( s[Id::AKA].find('(') == s[Id::AKA].npos )
681         {
682             std::cout << "nick=" << s[Id::AKA] << std::endl;
683         }
684         delete akas;
685     }
686     
687     if( s[Id::NOTES].compare("nil") != 0 ) {
688         Id * notes = new Id;
689         if( s[Id::NOTES].find('(') == s[Id::NOTES].npos ) {
690             std::cout << "notes=" << s[Id::NOTES] << std::endl;
691         } else {
692             notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
693             if( notes == NULL ) {
694                 std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" << std::endl;
695                 delete id;
696                 return;
697             }
698             
699             Id * note = new Id;
700             for( int cur_note = 0 ; cur_note < notes->my_d.size() ; ++cur_note ) {
701                 note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
702                 
703                 if( note != NULL ) {
704                     std::string const& t = note->my_d[0];
705                     std::string const& r = note->my_d[1];
706
707                     if( t.find("creation-date") != t.npos || t.find("timestamp") != t.npos ) {
708                         ; // drop 
709                     } else if( t.find("www") != t.npos || t.find("http") != t.npos ) {
710                         std::cout << "url=" << r << std::endl;
711                     } else if( t.find("notes") != t.npos ) {
712                         std::cout << "note=" << r << std::endl;
713                     } else if( t.find("icq") != t.npos ) {
714                         std::cout << "note=icq #" << r << std::endl;
715                     } else if( t.find("msn") != t.npos ) {
716                         std::cout << "note=msn" << r << std::endl;
717                     } else {
718                         std::cerr << "drop notes: " << t << " for " << s[Id::NAME] << std::endl;
719                     }
720                 }
721             }
722             delete note;
723             delete notes;
724         }
725     }
726     
727     std::cout << std::endl;
728     delete id;
729 }
730
731 // ----------------------------------------------------------------------
732 // CSV
733 // ----------------------------------------------------------------------
734 void tocsv( std::string const& l )
735 {
736     char const sep = ',';
737     char const quo = '"';
738
739     Id * id = parse( trace::NONE, 9,'[',l);
740     if( id == NULL )
741     {
742         if( l.size() > 2 )
743             std::cerr << "Broken line ==>" << l << "<==" << std::endl;
744         return;
745     }
746     
747     Id::storage_t const& s = id->my_d;
748
749     // "First Name","Last Name","Display Name"
750     std::cout << quo << s[Id::NAME] << quo << sep;
751     std::cout << quo << s[Id::FORNAME] << quo << sep;
752     std::cout << quo << s[Id::FORNAME] << ' ' << s[Id::NAME] << quo << sep;
753
754     // "Nickname Name"
755     std::string s_aka;
756     if( s[Id::AKA].compare("nil") != 0 )
757     {
758         Id * akas = new Id;
759         if( s[Id::AKA].find('(') == s[Id::AKA].npos )
760         {
761             std::cout << quo << s[Id::AKA] << quo << sep ;
762         }
763         else
764         {
765             akas = parse( trace::NONE, 1, '(', s[Id::AKA] );
766             if( akas == NULL ) 
767             {
768                 std::cerr << "can't parse akas ==>" << s[Id::AKA] << "<==" << std::endl;
769                 delete id;
770                 return;
771             }
772             
773             Id * aka = new Id;
774             aka = parse( trace::NONE, 1, '(', akas->my_d[0] );
775             if( aka != NULL )
776             {
777                 s_aka = quo + aka->my_d[0] + quo;
778             }
779             delete aka;
780             delete akas;
781         }
782     }
783     std::cout << s_aka << sep;
784     
785     // "E-mail","Secondary E-mail"
786     std::string s_email1;
787     std::string s_email2;
788     if( s[Id::NET].compare("nil") != 0 )
789     {
790         Id * nets = new Id;
791         nets = parse( trace::NONE, 1, '(', s[Id::NET] );
792         if( nets == NULL ) 
793         {
794             std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
795             delete id;
796             return;
797         }
798
799         s_email1 = quo + nets->my_d[0] + quo;
800         if( nets->my_d.size() > 1 )
801         {
802             s_email2 = quo + nets->my_d[1] + quo;
803         }
804         delete nets;
805     }
806     std::cout << s_email1 << sep << s_email2 << sep;
807
808     // "Business Phone","Home Phone","Fax Phone","Pager","Mobile Phone"
809     std::string p_business;
810     std::string p_home;
811     std::string p_fax;
812     std::string p_pager;
813     std::string p_mobile;
814
815     if( s[Id::PHONES].compare( "nil" ) != 0 )
816     {
817         Id * phones = new Id;
818         phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
819         if( phones == NULL ) 
820         {
821             std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
822             delete id;
823             return;
824         }
825
826         Id * phone = new Id;
827         int cur_phone;
828         for( cur_phone = 0 ; cur_phone < std::min(size_t(5),phones->my_d.size()) ; ++cur_phone )
829         {
830             phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
831
832             if( phone != NULL )
833             {
834                 std::string trans;
835                 if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos )
836                 {
837                     ABOOKPhoneTypesMap::hash_t::iterator it;
838                     if( (it=abook_phone_types_map.my_h.find( const_cast<char*>(phone->my_d[0].c_str())))
839                         != abook_phone_types_map.my_h.end() )
840                     {
841                         trans = it->second;
842                     }
843                     else
844                     {
845                         trans = abook_phone_types_map.my_default[0].my_trans;
846                     }
847                 }
848                 if( trans.find("home") != trans.npos )
849                 {
850                     p_home = quo + phone->my_d[1] + quo ;
851                 }
852                 else if( trans.find("work") != trans.npos )
853                 {
854                     p_business = quo + phone->my_d[1] + quo ;
855                 }
856                 else if( trans.find("cell") != trans.npos )
857                 {
858                     p_mobile = quo + phone->my_d[1] + quo ;
859                 }
860                 else if( trans.find("fax") != trans.npos )
861                 {
862                     p_fax = quo + phone->my_d[1] + quo ;
863                 }
864                 else if( trans.find("pager") != trans.npos )
865                 {
866                     p_pager = quo + phone->my_d[1] + quo ;
867                 }
868                 else 
869                 {
870                     if( p_home.size() == 0 )
871                     {
872                         p_home = quo + phone->my_d[1] + quo ;
873                     }
874                 }
875             }
876         }
877         delete phone;
878         delete phones;
879     }
880     std::cout << p_business << sep 
881               << p_home << sep 
882               << p_fax << sep 
883               << p_pager << sep 
884               << p_mobile << sep;
885
886     // "Home Street 1","Home Street 2","Home City","Home State","Home Postal Code","Home Country",
887     // "Business Street 1","Business Street 2","Business City","Business State","Business Postal Code","Business Country"
888     if( s[Id::ADRESSES].compare( "nil" ) != 0 )
889     {
890         Id * adresses = new Id;
891         adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
892         if( adresses == NULL ) 
893         {
894             std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
895             delete id;
896             return;
897         }
898
899         Id * adresse = new Id;
900         int cur_adresse;
901         for( cur_adresse = 0 ; cur_adresse < std::min(size_t(2),adresses->my_d.size()) ; ++cur_adresse )
902         {
903             adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );
904
905             if( adresse != NULL )
906             {
907                 // split again 
908                 Id * fields = new Id;
909                 fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
910                 if( fields != NULL )
911                 {
912                     std::string const& town    = adresse->my_d[2];
913                     std::string const& state   = adresse->my_d[3];
914                     std::string const& postal  = adresse->my_d[4];
915                     std::string const& country = adresse->my_d[5];
916                         
917 //                  if( adresse->my_d[0].size() > 0 ) 
918 //                  {
919 //                      if( adresse->my_d[0].find_first_of( "0123456789 _-:;," ) == 
920 //                          adresse->my_d[0].npos ) 
921 //                      {
922 //                          std::cout << ";TYPE=" << adresse->my_d[0];
923 //                      }
924 //                  }
925                     int i;
926                     for( i=0 ; i<std::min(size_t(2),fields->my_d.size()) ; ++i )
927                         std::cout << quo << fields->my_d[i] << quo << sep;
928                     for( i=std::min(size_t(2),fields->my_d.size()) ; i<2; ++i )
929                         std::cout << sep;
930                     std::cout << quo << town << quo << sep
931                               << quo << state << quo << sep
932                               << quo << postal << quo << sep
933                               << quo << country << quo << sep;
934                 }
935                 else
936                 {
937                     std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] 
938                               << "<<=" << std::endl;
939                 }
940                 delete fields;
941             }
942         }
943         for( cur_adresse = std::min(size_t(2),adresses->my_d.size()) ; cur_adresse<2; ++cur_adresse ) 
944         {
945             std::cout << sep << sep << sep << sep << sep << sep;
946         }
947         delete adresse;
948         delete adresses;
949     }
950     else
951     {
952         std::cout << sep << sep << sep << sep << sep << sep
953                   << sep << sep << sep << sep << sep << sep;
954     }
955
956     // "Job Title","Department","Company"
957     std::string s_title;
958     std::string s_departement;
959     std::string s_company;
960     if( s[Id::COMPANY].compare("nil") != 0 )
961     {
962         s_company =  quo + s[Id::COMPANY] + quo;
963     }
964     std::cout << s_title << sep
965               << s_departement << sep
966               << s_company << sep;
967
968     // "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" 
969     std::string n_web1;
970     std::string n_web2;
971     std::string n_byear;
972     std::string n_bmonth;
973     std::string n_bday;
974     std::string n_user1; // icq
975     std::string n_user2; // im
976     std::string n_user3;
977     std::string n_user4;
978     std::string n_note;
979     
980     if( s[Id::NOTES].compare("nil") != 0 )
981     {
982         Id * notes = new Id;
983         if( s[Id::NOTES].find('(') == s[Id::NOTES].npos )
984         {
985             n_note = quo + s[Id::NOTES] + quo;
986         }
987         else
988         {
989             notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
990             if( notes == NULL ) 
991             {
992                 std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" ;
993                 delete id;
994                 return;
995             }
996             
997             Id * note = new Id;
998             int cur_note;
999             for( cur_note = 0 ; cur_note < std::min(size_t(10),notes->my_d.size()) ; ++cur_note )
1000             {
1001                 note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
1002                 
1003                 if( note != NULL )
1004                 {
1005                     std::string const& t = note->my_d[0];
1006                     std::string const& r = note->my_d[1];
1007
1008                     if( t.find("www") != t.npos || t.find("http") != t.npos )
1009                     {
1010                         if( n_web1.size() == 0 )
1011                         {
1012                             n_web1 = quo + r + quo;
1013                         }
1014                         else
1015                         {
1016                             n_web2 = quo + r + quo;
1017                         }
1018                     }
1019                     else if( t.find("icq") != t.npos )
1020                     {
1021                         n_user1 = quo + r + quo;
1022                     }
1023                     else if( t.find("notes") != t.npos )
1024                     {
1025                         n_note = quo + r + quo;
1026                     }
1027                 }
1028             }
1029             delete note;
1030             delete notes;
1031         }
1032     }
1033
1034     std::cout << n_web1  << sep << n_web2   << sep 
1035               << n_byear << sep << n_bmonth << sep << n_bday  << sep 
1036               << n_user1 << sep << n_user2  << sep << n_user3 << sep << n_user4 << sep 
1037               << n_note;
1038     
1039     std::cout << std::endl;
1040     delete id;
1041 }
1042
1043
1044 // ----------------------------------------------------------------------
1045 // USAGE
1046 // ----------------------------------------------------------------------
1047 int 
1048 usage(int argc, char * argv[] )
1049 {
1050     std::cerr << argv[0] << " -t (vcard|gnokii|csv|abook) -l (fr|en) bbdb.txt" << std::endl;
1051     std::cerr << "argc=" << argc << std::endl;
1052     
1053     return 1;
1054 }
1055
1056
1057 // ----------------------------------------------------------------------
1058 // main
1059 // ----------------------------------------------------------------------
1060 int
1061 main( int argc, char * argv[] )
1062 {
1063     if( argc < 4 ) 
1064     {
1065         return usage(argc,argv);
1066     }
1067
1068     char t;
1069     char * lg = "en";
1070     if( argc == 4 && !strcmp(argv[1],"-t") )
1071     {
1072         if( !strcmp(argv[2],"vcard") )
1073         {
1074             t = 'v';
1075         } 
1076         else if( !strcmp(argv[2],"csv") )
1077         {
1078             t = 'c';
1079         } 
1080         else if( !strcmp(argv[2],"gnokii") )
1081         {
1082             t = 'g';
1083         } 
1084         else if( !strcmp(argv[2],"abook") )
1085         {
1086             t = 'a';
1087         } 
1088         else 
1089         {
1090             return usage(argc,argv);    
1091         }
1092     }
1093     else if( argc == 6 && !strcmp(argv[3],"-l") )
1094     {
1095         lg = argv[4];
1096         if( strcmp(lg,"fr") && strcmp(argv[4],"en") )
1097         {
1098             return usage(argc,argv);    
1099         }
1100     }
1101     else
1102     {
1103         return usage(argc,argv);    
1104     }
1105      
1106     std::vector<std::string> v;
1107
1108     // open file
1109     std::ifstream fs;
1110     fs.open( argv[argc-1], std::ios::in );
1111     if( ! fs.is_open() )
1112     {
1113         std::cerr << "can't open " << argv[argc-1] << std::endl;
1114         return 1;
1115     }
1116     while( ! fs.eof() )
1117     {
1118         char data[1024];
1119         fs.getline(data,1024);
1120         if( data[0] != ';' )
1121         {
1122             v.push_back( data );
1123         }
1124     }
1125     fs.close();
1126
1127     // parse v
1128     switch( t )
1129     {
1130     case 'g':
1131         std::for_each( v.begin(), v.end(), tognokii );
1132         break;
1133     case 'v':
1134         std::for_each( v.begin(), v.end(), tovcard );
1135         break;
1136     case 'a':
1137         std::for_each( v.begin(), v.end(), toabook );
1138         break;
1139     case 'c': {
1140         if( !strcmp( lg, "en" ))
1141         {
1142             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;
1143         }
1144         else 
1145         {
1146             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;
1147         }
1148         
1149         std::for_each( v.begin(), v.end(), tocsv );
1150         break;
1151     }
1152     } // end switch
1153
1154     // open output file
1155
1156     return 0;
1157 }
1158