bfuse.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       bfuse.cc
00003 ///             FUSE filesystem for Blackberry databases, using Barry.
00004 ///
00005 
00006 /*
00007     Copyright (C) 2008-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #define FUSE_USE_VERSION 25
00023 #include <fuse.h>
00024 #include <fuse_opt.h>
00025 
00026 #include <barry/barry.h>
00027 #include <sstream>
00028 #include <vector>
00029 #include <list>
00030 #include <string>
00031 #include <stdexcept>
00032 #include <memory>
00033 #include <tr1/memory>
00034 #include <errno.h>
00035 #include <sys/types.h>
00036 #include <fcntl.h>
00037 #include <string.h>
00038 #include <stdlib.h>
00039 #include "i18n.h"
00040 
00041 #include "barrygetopt.h"
00042 
00043 using namespace std;
00044 using namespace std::tr1;
00045 using namespace Barry;
00046 
00047 // Global filenames
00048 const char *error_log_filename = "error.log";
00049 
00050 // Global command line args
00051 string cmdline_pin;
00052 string cmdline_password;
00053 
00054 //
00055 // Data from the command line
00056 //
00057 
00058 /////////////////////////////////////////////////////////////////////////////
00059 // Command line option handling, through fuse
00060 
00061 //struct opt {
00062 //      char
00063 //};
00064 
00065 void Blurb()
00066 {
00067    int logical, major, minor;
00068    const char *Version = Barry::Version(logical, major, minor);
00069 
00070    cerr
00071    << "bfuse - FUSE filesystem for Blackberry databases\n"
00072    << "        Copyright 2008-2012, Net Direct Inc. (http://www.netdirect.ca/)\n"
00073    << "        Using: " << Version << "\n"
00074    << endl;
00075 }
00076 
00077 void Usage()
00078 {
00079    cerr
00080    << "\n"
00081    << "Barry specific options:\n"
00082    << "   -p pin    PIN of device to talk with\n"
00083    << "             If only one device is plugged in, this flag is optional\n"
00084    << "   -P pass   Simplistic method to specify device password\n"
00085    << endl;
00086 /*
00087    << "   -d db     Specify which database to mount.  If no -d options exist\n"
00088    << "             then all databases will be mounted.\n"
00089    << "             Can be used multiple times to mount more than one DB\n"
00090    << "   -h        This help\n"
00091    << "   -n        Use null parser on all databases.\n"
00092 */
00093 }
00094 
00095 /////////////////////////////////////////////////////////////////////////////
00096 // FUSE specific exception
00097 
00098 class fuse_error : public std::runtime_error
00099 {
00100         int m_errno;
00101 public:
00102         fuse_error(int errno_, const std::string &msg)
00103                 : std::runtime_error(msg), m_errno(errno_)
00104         {}
00105 
00106         int get_errno() const { return m_errno; }
00107 };
00108 
00109 
00110 /////////////////////////////////////////////////////////////////////////////
00111 // Barry record parsers
00112 
00113 class DataDumpParser : public Barry::Parser
00114 {
00115         uint32_t m_id;
00116         std::ostream &m_os;
00117 
00118 public:
00119         explicit DataDumpParser(std::ostream &os)
00120                 : m_os(os)
00121         {
00122         }
00123 
00124         virtual void ParseRecord(const Barry::DBData &data,
00125                                  const Barry::IConverter *ic)
00126         {
00127                 m_id = data.GetUniqueId();
00128                 m_os << "Raw record dump for record: "
00129                         << std::hex << m_id << std::endl;
00130                 m_os << data.GetData() << std::endl;
00131         }
00132 };
00133 
00134 template <class Record>
00135 struct Store
00136 {
00137         std::ostream &m_os;
00138 
00139         explicit Store(std::ostream &os)
00140                 : m_os(os)
00141         {
00142         }
00143 
00144         // storage operator
00145         void operator()(const Record &rec)
00146         {
00147                 m_os << rec;
00148         }
00149 };
00150 
00151 typedef std::auto_ptr<Barry::Parser>            ParserPtr;
00152 
00153 ParserPtr GetParser(const string &name, std::ostream &os, bool null_parser)
00154 {
00155         if( null_parser ) {
00156                 // use null parser
00157                 return ParserPtr( new DataDumpParser(os) );
00158         }
00159         // check for recognized database names
00160         else if( name == Contact::GetDBName() ) {
00161                 return ParserPtr(
00162                         new RecordParser<Contact, Store<Contact> > (
00163                                 new Store<Contact>(os)));
00164         }
00165         else if( name == Message::GetDBName() ) {
00166                 return ParserPtr(
00167                         new RecordParser<Message, Store<Message> > (
00168                                 new Store<Message>(os)));
00169         }
00170         else if( name == Calendar::GetDBName() ) {
00171                 return ParserPtr(
00172                         new RecordParser<Calendar, Store<Calendar> > (
00173                                 new Store<Calendar>(os)));
00174         }
00175         else if( name == CalendarAll::GetDBName() ) {
00176                 return ParserPtr(
00177                         new RecordParser<CalendarAll, Store<CalendarAll> > (
00178                                 new Store<CalendarAll>(os)));
00179         }
00180         else if( name == ServiceBook::GetDBName() ) {
00181                 return ParserPtr(
00182                         new RecordParser<ServiceBook, Store<ServiceBook> > (
00183                                 new Store<ServiceBook>(os)));
00184         }
00185 
00186         else if( name == Memo::GetDBName() ) {
00187                 return ParserPtr(
00188                         new RecordParser<Memo, Store<Memo> > (
00189                                 new Store<Memo>(os)));
00190         }
00191         else if( name == Task::GetDBName() ) {
00192                 return ParserPtr(
00193                         new RecordParser<Task, Store<Task> > (
00194                                 new Store<Task>(os)));
00195         }
00196         else if( name == PINMessage::GetDBName() ) {
00197                 return ParserPtr(
00198                         new RecordParser<PINMessage, Store<PINMessage> > (
00199                                 new Store<PINMessage>(os)));
00200         }
00201         else if( name == SavedMessage::GetDBName() ) {
00202                 return ParserPtr(
00203                         new RecordParser<SavedMessage, Store<SavedMessage> > (
00204                                 new Store<SavedMessage>(os)));
00205         }
00206         else if( name == Folder::GetDBName() ) {
00207                 return ParserPtr(
00208                         new RecordParser<Folder, Store<Folder> > (
00209                                 new Store<Folder>(os)));
00210         }
00211         else if( name == TimeZone::GetDBName() ) {
00212                 return ParserPtr(
00213                         new RecordParser<TimeZone, Store<TimeZone> > (
00214                                 new Store<TimeZone>(os)));
00215         }
00216         else {
00217                 // unknown database, use null parser
00218                 return ParserPtr( new DataDumpParser(os) );
00219         }
00220 }
00221 
00222 /////////////////////////////////////////////////////////////////////////////
00223 // PathSplit class
00224 
00225 class PathSplit
00226 {
00227         std::string m_pin, m_db, m_record, m_field, m_remainder;
00228 
00229         int m_level;            // the number of slashes, minus the first
00230                                 // i.e. first level is 0
00231         bool m_is_root;
00232 
00233 public:
00234         explicit PathSplit(const char *path)
00235                 : m_level(-1)
00236                 , m_is_root(false)
00237         {
00238                 if( *path != '/' )
00239                         return;         // return in a failed state
00240 
00241                 if( *(path+1) == 0 ) {
00242                         m_is_root = true;
00243                         return;
00244                 }
00245 
00246                 const char *s = path, *e = path;
00247                 while( *e ) {
00248                         while( *e && *e != '/' )
00249                                 e++;
00250 
00251                         m_level++;
00252 
00253                         if( s != e && (s+1) != e ) {
00254                                 string token(s+1, e);
00255 
00256                                 switch( m_level )
00257                                 {
00258                                 case 0: // root level, should not have token here
00259                                         m_level = -1;
00260                                         return; // failed state
00261 
00262                                 case 1: // have pin
00263                                         m_pin = token;
00264                                         break;
00265 
00266                                 case 2: // have db
00267                                         m_db = token;
00268                                         break;
00269 
00270                                 case 3: // have record
00271                                         m_record = token;
00272                                         break;
00273 
00274                                 case 4: // have field
00275                                         m_field = token;
00276                                         break;
00277 
00278                                 default:        // too many, store remainder and done
00279                                         m_remainder = s;        // keeps slash
00280                                         return;
00281                                 }
00282 
00283                                 // next
00284                                 s = e;
00285                                 if( *e )
00286                                         e++;
00287                         }
00288                         else if( *e ) {
00289                                 // next
00290                                 e++;
00291                         }
00292                 }
00293         }
00294 
00295         bool IsRoot() const { return m_is_root; }
00296         const std::string& Pin() const { return m_pin; }
00297         const std::string& DB() const { return m_db; }
00298         const std::string& Record() const { return m_record; }
00299         const std::string& Field() const { return m_field; }
00300         const std::string& Remainder() const { return m_remainder; }
00301         int Level() const { return m_level; }
00302 };
00303 
00304 
00305 /////////////////////////////////////////////////////////////////////////////
00306 // API classes
00307 
00308 class Entry
00309 {
00310 public:
00311         virtual ~Entry() {}
00312 };
00313 
00314 class Directory : public Entry
00315 {
00316 public:
00317         virtual int ReadDir(void *buf, fuse_fill_dir_t filler) = 0;
00318         virtual void FillDirStat(struct stat *st)
00319         {
00320                 st->st_mode = S_IFDIR | 0555;
00321                 st->st_nlink = 2;
00322         }
00323 };
00324 
00325 class File : public Entry
00326 {
00327 public:
00328         virtual void FillFileStat(const char *path, struct stat *st) = 0;
00329         virtual bool AccessOk(int flags)
00330         {
00331                 // default to readonly files
00332                 return (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY;
00333         }
00334         virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset) = 0;
00335 };
00336 
00337 typedef Directory*                              DirectoryPtr;
00338 typedef File*                                   FilePtr;
00339 typedef std::string                             NameT;
00340 typedef std::map<NameT, DirectoryPtr>           DirMap;
00341 typedef std::map<NameT, FilePtr>                FileMap;
00342 
00343 static DirMap g_dirmap;
00344 static FileMap g_filemap;
00345 
00346 static Directory* FindDir(const NameT &name)
00347 {
00348         DirMap::iterator di = g_dirmap.find(name);
00349         return di == g_dirmap.end() ? 0 : di->second;
00350 }
00351 
00352 static File* FindFile(const NameT &name)
00353 {
00354         FileMap::iterator fi = g_filemap.find(name);
00355         return fi == g_filemap.end() ? 0 : fi->second;
00356 }
00357 
00358 /////////////////////////////////////////////////////////////////////////////
00359 // Context classes
00360 
00361 class Database : public Directory, public File
00362 {
00363 public:
00364         Barry::Mode::Desktop &m_desk;
00365         std::string m_name;
00366         const Barry::DatabaseItem *m_pdb;
00367 
00368 public:
00369         Database(Barry::Mode::Desktop &desktop,
00370                 const std::string &pin, const Barry::DatabaseItem *pdb)
00371                 : m_desk(desktop)
00372                 , m_pdb(pdb)
00373         {
00374                 m_name = string("/") + pin + "/" + m_pdb->Name;
00375 
00376                 // add to directory list
00377                 g_dirmap[ m_name ] = this;
00378         }
00379 
00380         ~Database()
00381         {
00382                 // remove any entries that point to us
00383                 FileMap::iterator b = g_filemap.begin(), e = g_filemap.end();
00384                 for( ; b != e; ++b ) {
00385                         if( b->second == this ) {
00386                                 g_filemap.erase(b);
00387                         }
00388                 }
00389 
00390                 // erase ourselves from the directory list
00391                 g_dirmap.erase( m_name );
00392         }
00393 
00394         void AddFile(const std::string &recordId)
00395         {
00396                 // FIXME - this is a hack to redirect all record files
00397                 // to this database class... next step is to possibly
00398                 // split out records into field files if we have a
00399                 // parser, or just dump the hex if we don't
00400                 string name = m_name + "/" + recordId;
00401                 g_filemap[ name ] = this;
00402         }
00403 
00404         virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
00405         {
00406                 filler(buf, ".", NULL, 0);
00407                 filler(buf, "..", NULL, 0);
00408 
00409                 // list all records in database, by recordId
00410                 Barry::RecordStateTable rst;
00411                 m_desk.GetRecordStateTable(m_pdb->Number, rst);
00412 
00413                 Barry::RecordStateTable::StateMapType::iterator
00414                         b = rst.StateMap.begin(),
00415                         e = rst.StateMap.end();
00416                 for( ; b != e; ++ b ) {
00417                         ostringstream oss;
00418                         oss << hex << b->second.RecordId;
00419                         filler(buf, oss.str().c_str(), NULL, 0);
00420 
00421                         AddFile(oss.str());
00422                 }
00423                 return 0;
00424         }
00425 
00426         virtual void FillFileStat(const char *path, struct stat *st)
00427         {
00428                 // use the path to find the proper record
00429                 PathSplit ps(path);
00430 
00431                 string constructed = string("/") + ps.Pin() + "/" + ps.DB();
00432                 if( constructed != m_name ) {
00433                         // FIXME - this is shoddy error handling
00434                         throw std::logic_error("Constructed != name");
00435                 }
00436 
00437                 string data = GetRecordData(ps.Record());
00438 
00439                 st->st_mode = S_IFREG | 0444;
00440                 st->st_nlink = 1;
00441                 st->st_size = data.size();
00442         }
00443 
00444         virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset)
00445         {
00446                 // use the path to find the proper record
00447                 PathSplit ps(path);
00448 
00449                 string constructed = string("/") + ps.Pin() + "/" + ps.DB();
00450                 if( constructed != m_name ) {
00451                         // FIXME - this is shoddy error handling
00452                         throw std::logic_error("Constructed != name");
00453                 }
00454 
00455                 string data = GetRecordData(ps.Record());
00456 
00457                 size_t len = data.size();
00458                 if( offset >= 0 && offset < (off_t)len ) {
00459                         if( (offset + size) > len )
00460                                 size = len - offset;
00461                         memcpy(buf, data.data() + offset, size);
00462                 }
00463                 else {
00464                         size = 0;
00465                 }
00466                 return size;
00467         }
00468 
00469         const std::string& GetDBName() const { return m_pdb->Name; }
00470 
00471         std::string GetRecordData(const std::string &recordId)
00472         {
00473                 string data;
00474 
00475                 Barry::RecordStateTable rst;
00476                 m_desk.GetRecordStateTable(m_pdb->Number, rst);
00477 
00478                 uint32_t recid = strtoul(recordId.c_str(), NULL, 16);
00479                 RecordStateTable::IndexType index;
00480                 if( rst.GetIndex(recid, &index) ) {
00481                         ostringstream oss;
00482                         ParserPtr parser = GetParser(m_pdb->Name, oss, false);
00483                         m_desk.GetRecord(m_pdb->Number, index, *parser);
00484                         data = oss.str();
00485                 }
00486 
00487                 return data;
00488         }
00489 };
00490 
00491 class DesktopCon : public Directory
00492 {
00493 public:
00494         typedef std::tr1::shared_ptr<Database>                  DatabasePtr;
00495         typedef std::list<DatabasePtr>                          DBList;
00496 public:
00497         Barry::Controller m_con;
00498         Barry::Mode::Desktop m_desk;
00499         std::string m_pin;
00500         DBList m_dblist;
00501 
00502         DesktopCon(const Barry::ProbeResult &result, const std::string &pin)
00503                 : m_con(result)
00504                 , m_desk(m_con)
00505                 , m_pin(pin)
00506         {
00507                 // add to directory list
00508                 g_dirmap[ string("/") + pin ] = this;
00509         }
00510 
00511         ~DesktopCon()
00512         {
00513                 // remove from directory list
00514                 g_dirmap.erase( string("/") + m_pin );
00515         }
00516 
00517         virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
00518         {
00519                 filler(buf, ".", NULL, 0);
00520                 filler(buf, "..", NULL, 0);
00521 
00522                 // list all databases in list
00523                 DBList::const_iterator b = m_dblist.begin(), e = m_dblist.end();
00524                 for( ; b != e; ++ b ) {
00525                         filler(buf, (*b)->GetDBName().c_str(), NULL, 0);
00526                 }
00527                 return 0;
00528         }
00529 
00530         void Open(const char *password = 0)
00531         {
00532                 // open our device
00533                 m_desk.Open(password);
00534 
00535                 // add all databases as directories
00536                 DatabaseDatabase::DatabaseArrayType::const_iterator
00537                         dbi = m_desk.GetDBDB().Databases.begin(),
00538                         dbe = m_desk.GetDBDB().Databases.end();
00539                 for( ; dbi != dbe; ++dbi ) {
00540                         DatabasePtr db = DatabasePtr(
00541                                 new Database(m_desk, m_pin, &(*dbi)) );
00542                         m_dblist.push_back(db);
00543                 }
00544         }
00545 };
00546 
00547 class Context : public Directory, public File
00548 {
00549 public:
00550         typedef std::auto_ptr<Barry::Probe>                     ProbePtr;
00551         typedef std::tr1::shared_ptr<DesktopCon>                DesktopConPtr;
00552         typedef std::string                                     PinT;
00553         typedef std::map<PinT, DesktopConPtr>                   PinMap;
00554 
00555         ProbePtr m_probe;
00556         PinMap m_pinmap;
00557 
00558         string m_error_log;
00559 
00560         string m_limit_pin;             // only mount device with this pin
00561         string m_password;              // use this password when connecting
00562 
00563 public:
00564         Context(const string &limit_pin = "", const string &password = "")
00565                 : m_limit_pin(limit_pin)
00566                 , m_password(password)
00567         {
00568                 g_dirmap["/"] = this;
00569                 g_filemap[string("/") + error_log_filename] = this;
00570 
00571                 m_error_log = "Hello FUSE world.  This is Barry.  Pleased to meet you.\n";
00572         }
00573 
00574         ~Context()
00575         {
00576                 g_dirmap.erase("/");
00577                 g_filemap.erase(string("/") + error_log_filename);
00578         }
00579 
00580         virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
00581         {
00582                 filler(buf, ".", NULL, 0);
00583                 filler(buf, "..", NULL, 0);
00584                 filler(buf, error_log_filename, NULL, 0);
00585 
00586                 // list all pins in map
00587                 PinMap::const_iterator b = m_pinmap.begin(), e = m_pinmap.end();
00588                 for( ; b != e; ++ b ) {
00589                         filler(buf, b->first.c_str(), NULL, 0);
00590                 }
00591                 return 0;
00592         }
00593 
00594         virtual void FillFileStat(const char *path, struct stat *st)
00595         {
00596                 st->st_mode = S_IFREG | 0444;
00597                 st->st_nlink = 1;
00598                 st->st_size = m_error_log.size();
00599         }
00600 
00601         virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset)
00602         {
00603                 size_t len = m_error_log.size();
00604                 if( offset >= 0 && offset < (off_t)len ) {
00605                         if( (offset + size) > len )
00606                                 size = len - offset;
00607                         memcpy(buf, m_error_log.data() + offset, size);
00608                 }
00609                 else {
00610                         size = 0;
00611                 }
00612                 return size;
00613         }
00614 
00615         void Log(const std::string &msg)
00616         {
00617                 m_error_log += msg;
00618                 m_error_log += "\n";
00619         }
00620 
00621         const std::string& GetLog() const { return m_error_log; }
00622 
00623         void ProbeAll()
00624         {
00625                 // probe the USB bus for Blackberry devices
00626                 m_probe.reset( new Probe );
00627 
00628                 // connect to all PINs found, and add them to our map
00629                 for( int i = 0; i < m_probe->GetCount(); i++ ) {
00630                         string curpin = m_probe->Get(i).m_pin.Str();
00631 
00632                         // don't add a blank or pre-existing pin
00633                         if( !curpin.size() || m_pinmap.find(curpin) != m_pinmap.end() ) {
00634                                 continue;
00635                         }
00636 
00637                         // don't add non-PIN device if pin specified
00638                         if( m_limit_pin.size() && curpin != m_limit_pin ) {
00639                                 continue;
00640                         }
00641 
00642                         DesktopConPtr dev = DesktopConPtr (
00643                                 new DesktopCon(m_probe->Get(i), curpin) );
00644                         dev->Open(m_password.c_str());
00645                         m_pinmap[ curpin ] = dev;
00646                 }
00647         }
00648 
00649         DesktopCon* FindPin(PinT pin)
00650         {
00651                 PinMap::iterator pi = m_pinmap.find(pin);
00652                 return pi == m_pinmap.end() ? 0 : pi->second.get();
00653         }
00654 };
00655 
00656 
00657 /////////////////////////////////////////////////////////////////////////////
00658 // FUSE API hooks
00659 
00660 static void* bfuse_init()
00661 {
00662         // Initialize the barry library.  Must be called before
00663         // anything else.
00664         Barry::Init(false);
00665 
00666         Context *ctx = 0;
00667 
00668         try {
00669                 ctx = new Context(cmdline_pin, cmdline_password);
00670                 ctx->ProbeAll();
00671         }
00672         catch( std::exception &e ) {
00673                 if( ctx ) {
00674                         ctx->Log(e.what());
00675                 }
00676         }
00677 
00678         return ctx;
00679 }
00680 
00681 static void bfuse_destroy(void *data)
00682 {
00683         if( data ) {
00684                 Context *ctx = (Context*) data;
00685                 delete ctx;
00686         }
00687 }
00688 
00689 static int bfuse_getattr(const char *path, struct stat *st)
00690 {
00691         memset(st, 0, sizeof(*st));
00692 
00693         if( Directory *dir = FindDir(path) ) {
00694                 dir->FillDirStat(st);
00695                 return 0;
00696         }
00697         else if( File *file = FindFile(path) ) {
00698                 file->FillFileStat(path, st);
00699                 return 0;
00700         }
00701         else
00702                 return -ENOENT;
00703 }
00704 
00705 static int bfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
00706                          off_t /*offset*/, struct fuse_file_info * /*fi*/)
00707 {
00708         Directory *dir = FindDir(path);
00709         if( !dir )
00710                 return -ENOENT;
00711         return dir->ReadDir(buf, filler);
00712 }
00713 
00714 static int bfuse_open(const char *path, struct fuse_file_info *fi)
00715 {
00716         File *file = FindFile(path);
00717         if( !file )
00718                 return -ENOENT;
00719 
00720         if( !file->AccessOk(fi->flags) )
00721                 return -EACCES;
00722 
00723         return 0;
00724 }
00725 
00726 static int bfuse_read(const char *path, char *buf, size_t size, off_t offset,
00727                       struct fuse_file_info *fi)
00728 {
00729         File *file = FindFile(path);
00730         if( !file )
00731                 return -ENOENT;
00732 
00733         return file->ReadFile(path, buf, size, offset);
00734 }
00735 
00736 // static struct here automatically zeros data
00737 static struct fuse_operations bfuse_oper;
00738 
00739 
00740 /////////////////////////////////////////////////////////////////////////////
00741 // main
00742 
00743 int main(int argc, char *argv[])
00744 {
00745         INIT_I18N(PACKAGE);
00746 
00747         cout.sync_with_stdio(true);     // leave this on, since libusb uses
00748                                         // stdio for debug messages
00749 
00750         Blurb();
00751 
00752         // initialize the operation hooks
00753         bfuse_oper.init         = bfuse_init;
00754         bfuse_oper.destroy      = bfuse_destroy;
00755         bfuse_oper.getattr      = bfuse_getattr;
00756         bfuse_oper.readdir      = bfuse_readdir;
00757         bfuse_oper.open         = bfuse_open;
00758         bfuse_oper.read         = bfuse_read;
00759 
00760         // process command line options before FUSE does
00761         // FUSE does its own command line processing, and
00762         // doesn't seem to have a way to plug into it,
00763         // so do our own first
00764         int fuse_argc = 0;
00765         char **fuse_argv = new char*[argc];
00766 
00767         for( int i = 0; i < argc; i++ ) {
00768                 if( argv[i][0] == '-' ) {
00769 
00770                         switch( argv[i][1] )
00771                         {
00772 //                      case 'd':       // mount dbname
00773 //                              dbNames.push_back(string(optarg));
00774 //                              break;
00775 
00776 //                      case 'n':       // use null parser
00777 //                              null_parser = true;
00778 //                              break;
00779 
00780                         case 'p':       // Blackberry PIN
00781                                 if( i+1 < argc ) {
00782                                         cmdline_pin = argv[++i];
00783                                 }
00784                                 continue;
00785 
00786                         case 'P':       // Device password
00787                                 if( i+1 < argc ) {
00788                                         cmdline_password = argv[++i];
00789                                 }
00790                                 continue;
00791 
00792                         case 'h':       // help
00793                                 Usage();
00794                                 break;
00795                         }
00796                 }
00797 
00798                 // if we get here, add this option to FUSE's
00799                 fuse_argv[fuse_argc] = argv[i];
00800                 fuse_argc++;
00801         }
00802 
00803         int ret = fuse_main(fuse_argc, fuse_argv, &bfuse_oper);
00804         delete [] fuse_argv;
00805         return ret;
00806 }
00807