lib: added convenience Add function to Restore that takes a DBList
[barry.git] / tools / bio.cc
blobf151adbff65f75aa30b97785a4096056fcdfff83
1 ///
2 /// \file bio.cc
3 /// Barry Input / Output
4 ///
6 /*
7 Copyright (C) 2010, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include <barry/barry.h>
23 #include <barry/barrysync.h>
24 #include <barry/barrybackup.h>
26 #include "mimedump.h"
27 #include "brecsum.h"
29 #include <iomanip>
30 #include <iostream>
31 #include <sstream>
32 #include <fstream>
33 #include <string>
34 #include <vector>
35 #include <stdexcept>
36 #include <tr1/memory>
37 #include <getopt.h>
38 #include <strings.h>
40 using namespace std;
41 using namespace std::tr1;
42 using namespace Barry;
44 void Usage()
46 int major, minor;
47 const char *Version = Barry::Version(major, minor);
49 cerr
50 << "bio - Barry Input / Output\n"
51 << " Copyright 2010, Net Direct Inc. (http://www.netdirect.ca/)\n"
52 << " Using: " << Version << "\n"
53 << " Compiled "
54 #ifdef __BARRY_BOOST_MODE__
55 << "with"
56 #else
57 << "without"
58 #endif
59 << " Boost support\n"
60 << "\n"
61 << " Usage: bio -i <type> [options...] -o <type> [options...]\n"
62 << "\n"
63 << " -i type The input type (Builder) to use for producing records\n"
64 << " Can be one of: device, tar"
65 #ifdef __BARRY_BOOST_MODE__
66 << ", boost"
67 #endif
68 << ", ldif, mime\n"
69 << " -o type The output type (Parser) to use for processing records.\n"
70 << " Multiple outputs are allowed, as long as they don't\n"
71 << " conflict (such as two outputs writing to the same file\n"
72 << " or device).\n"
73 << " Can be one of: device, tar"
74 #ifdef __BARRY_BOOST_MODE__
75 << ", boost"
76 #endif
77 << ", ldif, mime, dump, sha1\n"
78 << "\n"
79 << " Options to use for 'device' type:\n"
80 << " -d db Name of input database. Can be used multiple times.\n"
81 << " -A Add all available device databases, instead of specifying\n"
82 << " them manually via -d\n"
83 << " -p pin PIN of device to talk to\n"
84 << " If only one device is plugged in, this flag is optional\n"
85 << " -P pass Simplistic method to specify device password\n"
86 << " -w mode Set write mode when using 'device' for output. Must be\n"
87 << " specified, or will not write anything.\n"
88 << " Can be one of: erase, overwrite, addonly, addnew\n"
90 // FIXME - modifiers not yet implemented
91 << "\n"
92 << " Input database modifiers: (can be used multiple times for more than 1 record)\n"
93 << "\n"
94 << " -r # Record index number as seen in the -T state table.\n"
95 << " This overrides the default -d behaviour, and only\n"
96 << " downloads the one specified record, sending to stdout.\n"
97 << " -R # Same as -r, but also clears the record's dirty flags.\n"
98 << " -D # Record index number as seen in the -T state table,\n"
99 << " which indicates the record to delete. Used with the -d\n"
100 << " command to specify the database.\n"
102 << "\n"
103 << " Options to use for 'tar' backup type:\n"
104 << " -d db Name of input database. Can be used multiple times.\n"
105 << " Not available in output mode. Note that by default,\n"
106 << " all databases in the backup are selected, when reading,\n"
107 << " unless at least one -d is specified.\n"
108 << " -f file Tar backup file to read from or write to\n"
109 #ifdef __BARRY_BOOST_MODE__
110 << "\n"
111 << " Options to use for 'boost' type:\n"
112 << " -f file Boost serialization filename to read from or write to\n"
113 << " Can use - to specify stdin/stdout\n"
114 #endif
115 << "\n"
116 << " Options to use for 'ldif' type:\n"
117 << " -c dn Convert address book database to LDIF format, using the\n"
118 << " specified baseDN\n"
119 << " -C dnattr LDIF attribute name to use when building the FQDN\n"
120 << " Defaults to 'cn'\n"
122 LDIF options?
124 << " -L List Contact field names\n"
125 << " -m Map LDIF name to Contact field / Unmap LDIF name\n"
126 << " Map: ldif,read,write - maps ldif to read/write Contact fields\n"
127 << " Unmap: ldif name alone\n"
128 << " -M List current LDIF mapping\n"
130 << "\n"
131 << " Options to use for 'mime' type:\n"
132 << " -f file Filename to read from or write to. Use - to explicitly\n"
133 << " specify stdin/stdout, which is default.\n"
134 << "\n"
135 << " Options to use for 'dump' to stdout output type:\n"
136 << " -n Use hex dump parser on all databases.\n"
137 << "\n"
138 << " Options to use for 'sha1' sum stdout output type:\n"
139 << " -t Include DB Name, Type, and Unique record IDs in the checksums\n"
140 << "\n"
141 << " Standalone options:\n"
142 << " -h This help\n"
143 << " -I cs International charset for string conversions\n"
144 << " Valid values here are available with 'iconv --list'\n"
145 << " -S Show list of supported database parsers and builders\n"
146 << " -v Dump protocol data during operation\n"
147 << "\n"
148 << endl;
151 class ModeBase
153 public:
154 virtual ~ModeBase() {}
156 virtual bool ProbeNeeded() const { return false; }
158 virtual void SetFilename(const std::string &name)
160 throw runtime_error("Filename not applicable for this mode");
163 virtual void AddDB(const std::string &dbname)
165 throw runtime_error("DB not applicable for this mode");
168 virtual void AddAllDBs()
170 throw runtime_error("DBs not applicable for this mode");
173 virtual void SetPIN(const std::string &pin)
175 throw runtime_error("PIN not applicable for this mode");
178 virtual void SetPassword(const std::string &password)
180 throw runtime_error("Password not applicable for this mode");
183 virtual void SetWriteMode(DeviceParser::WriteMode mode)
185 throw runtime_error("Device write behaviour not applicable for this mode");
188 virtual void SetDN(const std::string &dn)
190 throw runtime_error("DN not applicable for this mode");
193 virtual void SetAttribute(const std::string &attr)
195 throw runtime_error("Attribute not applicable for this mode");
198 virtual void SetHexDump()
200 throw runtime_error("No hex dump option in this mode");
203 virtual void IncludeIDs()
205 throw runtime_error("Including record IDs in the SHA1 sum is not applicable in this mode");
209 class DeviceBase : public virtual ModeBase
211 protected:
212 Barry::Pin m_pin;
213 std::string m_password;
215 public:
216 bool ProbeNeeded() const { return true; }
218 void SetPIN(const std::string &pin)
220 istringstream iss(pin);
221 iss >> m_pin;
222 if( !m_pin.Valid() )
223 throw runtime_error("Invalid PIN: " + pin);
226 void SetPassword(const std::string &password)
228 m_password = password;
232 //////////////////////////////////////////////////////////////////////////////
233 // Base class for Input Mode
235 class InputBase : public virtual ModeBase
237 public:
238 virtual Builder& GetBuilder(Barry::Probe *probe, IConverter &ic) = 0;
241 class DeviceInputBase : public DeviceBase, public InputBase
245 //////////////////////////////////////////////////////////////////////////////
246 // Mode: Input, Type: device
248 class DeviceInput : public DeviceInputBase
250 auto_ptr<Controller> m_con;
251 auto_ptr<Mode::Desktop> m_desktop;
252 auto_ptr<DeviceBuilder> m_builder;
253 vector<string> m_dbnames;
254 bool m_add_all;
256 public:
257 DeviceInput()
258 : m_add_all(false)
262 void AddDB(const std::string &dbname)
264 m_dbnames.push_back(dbname);
267 void AddAllDBs()
269 m_add_all = true;
272 Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
274 int i = probe->FindActive(m_pin);
275 if( i == -1 ) {
276 if( m_pin.Valid() )
277 throw runtime_error("PIN not found: " + m_pin.Str());
278 else
279 throw runtime_error("PIN not specified, and more than one device exists.");
282 m_con.reset( new Controller(probe->Get(i)) );
283 m_desktop.reset( new Mode::Desktop(*m_con, ic) );
284 m_desktop->Open(m_password.c_str());
285 m_builder.reset( new DeviceBuilder(*m_desktop) );
287 if( m_add_all ) {
288 m_builder->Add(m_desktop->GetDBDB());
290 else {
291 for( size_t i = 0; i < m_dbnames.size(); i++ ) {
292 m_builder->Add(m_dbnames[i]);
296 return *m_builder;
300 //////////////////////////////////////////////////////////////////////////////
301 // Mode: Input, Type: tar
303 class TarInput : public InputBase
305 auto_ptr<Restore> m_restore;
306 string m_tarpath;
307 vector<string> m_dbnames;
309 public:
310 void SetFilename(const std::string &name)
312 m_tarpath = name;
313 if( name == "-" )
314 throw runtime_error("Cannot use stdin as tar source file, sorry.");
317 void AddDB(const std::string &dbname)
319 m_dbnames.push_back(dbname);
322 Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
324 m_restore.reset( new Restore(m_tarpath, true) );
325 for( size_t i = 0; i < m_dbnames.size(); i++ ) {
326 m_restore->AddDB(m_dbnames[i]);
329 return *m_restore;
333 //////////////////////////////////////////////////////////////////////////////
334 // Mode: Input, Type: boost
336 #ifdef __BARRY_BOOST_MODE__
337 class BoostInput : public InputBase
339 auto_ptr<BoostBuilder> m_builder;
340 string m_filename;
342 public:
343 BoostInput()
344 : m_filename("-") // default to stdin/stdout
348 void SetFilename(const std::string &name)
350 m_filename = name;
353 Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
355 if( m_filename == "-" ) {
356 // use stdin
357 m_builder.reset( new BoostBuilder(cin) );
359 else {
360 m_builder.reset( new BoostBuilder(m_filename) );
362 return *m_builder;
366 #endif
368 //////////////////////////////////////////////////////////////////////////////
369 // Mode: Input, Type: ldif
371 class LdifInput : public InputBase
373 auto_ptr<Builder> m_builder;
374 string m_filename;
376 public:
377 LdifInput()
378 : m_filename("-")
382 void SetFilename(const std::string &name)
384 m_filename = name;
387 Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
389 if( m_filename == "-" ) {
390 // use stdin
391 m_builder.reset(
392 new RecordBuilder<Contact, LdifStore>(
393 new LdifStore(cin)) );
395 else {
396 m_builder.reset(
397 new RecordBuilder<Contact, LdifStore>(
398 new LdifStore(m_filename)) );
400 return *m_builder;
406 //////////////////////////////////////////////////////////////////////////////
407 // Mode: Input, Type: mime
409 class MimeInput : public InputBase
411 auto_ptr<MimeBuilder> m_builder;
412 string m_filename;
414 public:
415 MimeInput()
416 : m_filename("-")
420 void SetFilename(const std::string &name)
422 m_filename = name;
425 Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
427 if( m_filename == "-" ) {
428 // use stdin
429 m_builder.reset( new MimeBuilder(cin) );
431 else {
432 m_builder.reset( new MimeBuilder(m_filename) );
434 return *m_builder;
439 //////////////////////////////////////////////////////////////////////////////
440 // Base class for Output Mode
442 class OutputBase : public virtual ModeBase
444 public:
445 virtual Parser& GetParser(Barry::Probe *probe, IConverter &ic) = 0;
448 class DeviceOutputBase : public DeviceBase, public OutputBase
452 //////////////////////////////////////////////////////////////////////////////
453 // Mode: Output, Type: device
455 class DeviceOutput : public DeviceOutputBase
457 auto_ptr<Controller> m_con;
458 auto_ptr<Mode::Desktop> m_desktop;
459 auto_ptr<DeviceParser> m_parser;
460 DeviceParser::WriteMode m_mode;
462 public:
463 DeviceOutput()
464 : m_mode(DeviceParser::DROP_RECORD)
468 void SetWriteMode(DeviceParser::WriteMode mode)
470 m_mode = mode;
473 Parser& GetParser(Barry::Probe *probe, IConverter &ic)
475 int i = probe->FindActive(m_pin);
476 if( i == -1 ) {
477 if( m_pin.Valid() )
478 throw runtime_error("PIN not found: " + m_pin.Str());
479 else
480 throw runtime_error("PIN not specified, and more than one device exists.");
483 m_con.reset( new Controller(probe->Get(i)) );
484 m_desktop.reset( new Mode::Desktop(*m_con, ic) );
485 m_desktop->Open(m_password.c_str());
486 m_parser.reset( new DeviceParser(*m_desktop, m_mode) );
488 return *m_parser;
492 //////////////////////////////////////////////////////////////////////////////
493 // Mode: Output, Type: tar
495 class TarOutput : public OutputBase
497 auto_ptr<Backup> m_backup;
498 string m_tarpath;
500 public:
501 void SetFilename(const std::string &name)
503 m_tarpath = name;
504 if( name == "-" )
505 throw runtime_error("Cannot use stdout as tar backup file, sorry.");
508 Parser& GetParser(Barry::Probe *probe, IConverter &ic)
510 m_backup.reset( new Backup(m_tarpath) );
511 return *m_backup;
515 //////////////////////////////////////////////////////////////////////////////
516 // Mode: Output, Type: boost
518 #ifdef __BARRY_BOOST_MODE__
519 class BoostOutput : public OutputBase
521 auto_ptr<BoostParser> m_parser;
522 string m_filename;
524 public:
525 void SetFilename(const std::string &name)
527 m_filename = name;
530 Parser& GetParser(Barry::Probe *probe, IConverter &ic)
532 if( !m_filename.size() )
533 throw runtime_error("Boost output requires a specific output file (-f switch)");
535 if( m_filename == "-" ) {
536 // use stdout
537 m_parser.reset( new BoostParser(cout) );
539 else {
540 m_parser.reset( new BoostParser(m_filename) );
542 return *m_parser;
546 #endif
548 //////////////////////////////////////////////////////////////////////////////
549 // Mode: Output, Type: ldif
551 class LdifOutput : public OutputBase
553 auto_ptr<Parser> m_parser;
554 string m_filename;
555 string m_baseDN;
556 string m_dnattr;
558 public:
559 LdifOutput()
560 : m_filename("-")
564 void SetFilename(const std::string &name)
566 m_filename = name;
569 void SetDN(const std::string &dn)
571 m_baseDN = dn;
574 void SetAttribute(const std::string &attr)
576 m_dnattr = attr;
579 Parser& GetParser(Barry::Probe *probe, IConverter &ic)
581 if( m_filename == "-" ) {
582 // use stdin
583 m_parser.reset(
584 new RecordParser<Contact, LdifStore>(
585 new LdifStore(cout, m_baseDN,
586 m_dnattr)) );
588 else {
589 m_parser.reset(
590 new RecordParser<Contact, LdifStore>(
591 new LdifStore(m_filename, m_baseDN,
592 m_dnattr)) );
594 return *m_parser;
598 //////////////////////////////////////////////////////////////////////////////
599 // Mode: Output, Type: mime
601 class MimeStore : public AllRecordStore
603 std::ostream &m_os;
605 public:
606 MimeStore(std::ostream &os)
607 : m_os(os)
611 #undef HANDLE_PARSER
612 #define HANDLE_PARSER(tname) \
613 void operator() (const Barry::tname &r) \
615 MimeDump<tname>::Dump(m_os, r); \
618 ALL_KNOWN_PARSER_TYPES
621 class MimeOutput : public OutputBase
623 auto_ptr<std::ofstream> m_file;
624 auto_ptr<Parser> m_parser;
625 std::string m_filename;
627 public:
628 MimeOutput()
629 : m_filename("-") // default to stdout
633 void SetFilename(const std::string &name)
635 m_filename = name;
638 Parser& GetParser(Barry::Probe *probe, IConverter &ic)
640 if( m_filename == "-" ) {
641 m_parser.reset( new AllRecordParser(cout,
642 new HexDumpParser(cout),
643 new MimeStore(cout)) );
645 else {
646 m_file.reset( new std::ofstream(m_filename.c_str()) );
647 m_parser.reset( new AllRecordParser(*m_file,
648 new HexDumpParser(*m_file),
649 new MimeStore(*m_file)) );
651 return *m_parser;
655 //////////////////////////////////////////////////////////////////////////////
656 // Mode: Output, Type: dump
658 class DumpOutput : public OutputBase
660 auto_ptr<Parser> m_parser;
661 bool m_hex_only;
663 public:
664 DumpOutput()
665 : m_hex_only(false)
669 void SetHexDump()
671 m_hex_only = true;
674 Parser& GetParser(Barry::Probe *probe, IConverter &ic)
676 if( m_hex_only ) {
677 m_parser.reset( new HexDumpParser(cout) );
679 else {
680 m_parser.reset( new AllRecordParser(cout,
681 new HexDumpParser(cout),
682 new AllRecordDumpStore(cout)) );
684 return *m_parser;
688 //////////////////////////////////////////////////////////////////////////////
689 // Mode: Output, Type: sha1
691 class Sha1Output : public OutputBase
693 auto_ptr<Parser> m_parser;
694 bool m_include_ids;
696 public:
697 Sha1Output()
698 : m_include_ids(false)
702 void IncludeIDs()
704 m_include_ids = true;
707 Parser& GetParser(Barry::Probe *probe, IConverter &ic)
709 m_parser.reset( new ChecksumParser(m_include_ids) );
710 return *m_parser;
716 //////////////////////////////////////////////////////////////////////////////
717 // Main application class
719 class App
721 public:
722 typedef shared_ptr<OutputBase> OutputPtr;
723 typedef vector<OutputPtr> OutputsType;
725 private:
726 auto_ptr<InputBase> Input;
727 OutputsType Outputs;
729 public:
731 bool ParseInMode(const string &mode);
732 bool ParseOutMode(const string &mode);
733 DeviceParser::WriteMode ParseWriteMode(const std::string &mode);
734 static void ShowParsers();
735 // returns true if any of the items in Outputs needs a probe
736 bool OutputsProbeNeeded();
737 int main(int argc, char *argv[]);
740 bool App::ParseInMode(const string &mode)
742 if( mode == "device" ) {
743 Input.reset( new DeviceInput );
744 return true;
746 else if( mode == "tar" ) {
747 Input.reset( new TarInput );
748 return true;
750 #ifdef __BARRY_BOOST_MODE__
751 else if( mode == "boost" ) {
752 Input.reset( new BoostInput );
753 return true;
755 #endif
756 else if( mode == "ldif" ) {
757 Input.reset( new LdifInput );
758 return true;
760 else if( mode == "mime" ) {
761 Input.reset( new MimeInput );
762 return true;
764 else
765 return false;
768 bool App::ParseOutMode(const string &mode)
770 if( mode == "device" ) {
771 Outputs.push_back( OutputPtr(new DeviceOutput) );
772 return true;
774 else if( mode == "tar" ) {
775 Outputs.push_back( OutputPtr(new TarOutput) );
776 return true;
778 #ifdef __BARRY_BOOST_MODE__
779 else if( mode == "boost" ) {
780 Outputs.push_back( OutputPtr(new BoostOutput) );
781 return true;
783 #endif
784 else if( mode == "ldif" ) {
785 Outputs.push_back( OutputPtr(new LdifOutput) );
786 return true;
788 else if( mode == "mime" ) {
789 Outputs.push_back( OutputPtr(new MimeOutput) );
790 return true;
792 else if( mode == "dump" ) {
793 Outputs.push_back( OutputPtr(new DumpOutput) );
794 return true;
796 else if( mode == "sha1" ) {
797 Outputs.push_back( OutputPtr(new Sha1Output) );
798 return true;
800 else
801 return false;
804 DeviceParser::WriteMode App::ParseWriteMode(const std::string &mode)
806 if( mode == "erase" )
807 return DeviceParser::ERASE_ALL_WRITE_ALL;
808 else if( mode == "overwrite" )
809 return DeviceParser::INDIVIDUAL_OVERWRITE;
810 else if( mode == "addonly" )
811 return DeviceParser::ADD_BUT_NO_OVERWRITE;
812 else if( mode == "addnew" )
813 return DeviceParser::ADD_WITH_NEW_ID;
814 else
815 throw runtime_error("Unknown device output mode. Must be one of: erase, overwrite, addonly, addnew");
818 void App::ShowParsers()
820 cout << "Supported Database parsers:\n"
821 << " (* = can display in vformat MIME mode)\n"
823 #undef HANDLE_PARSER
824 #define HANDLE_PARSER(tname) \
825 << " " << tname::GetDBName() \
826 << (MimeDump<tname>::Supported() ? " *" : "") << "\n"
828 ALL_KNOWN_PARSER_TYPES
830 << "\n"
831 << "Supported Database builders:\n"
833 #undef HANDLE_BUILDER
834 #define HANDLE_BUILDER(tname) \
835 << " " << tname::GetDBName() << "\n"
837 ALL_KNOWN_BUILDER_TYPES
839 << endl;
842 bool App::OutputsProbeNeeded()
844 for( OutputsType::iterator i = Outputs.begin();
845 i != Outputs.end();
846 ++i )
848 if( (*i)->ProbeNeeded() )
849 return true;
851 return false;
854 int App::main(int argc, char *argv[])
856 bool verbose = false;
857 string iconvCharset;
859 // process command line options
860 ModeBase *current = 0;
861 for(;;) {
862 int cmd = getopt(argc, argv, "i:o:nvIf:p:P:d:c:C:ASw:t");
863 if( cmd == -1 )
864 break;
866 // first option must be in or out
867 if( !current ) {
868 if( cmd != 'i' && cmd != 'o' && cmd != 'S' ) {
869 Usage();
870 return 1;
874 switch( cmd )
876 case 'i': // set input mode
877 // must be first time used
878 if( Input.get() || !ParseInMode(optarg) ) {
879 Usage();
880 return 1;
882 current = Input.get();
883 break;
885 case 'o': // set output mode
886 // can be used multiple times
887 if( !ParseOutMode(optarg) ) {
888 Usage();
889 return 1;
891 current = Outputs[Outputs.size() - 1].get();
892 break;
895 case 'c': // set ldif dn
896 current->SetDN(optarg);
897 break;
899 case 'C': // set ldif attr
900 current->SetAttribute(optarg);
901 break;
903 case 'd': // database name
904 current->AddDB(optarg);
905 break;
907 case 'f': // filename
908 current->SetFilename(optarg);
909 break;
911 case 'p': // device PIN
912 current->SetPIN(optarg);
913 break;
915 case 'P': // password
916 current->SetPassword(optarg);
917 break;
919 case 'w': // device write mode
920 current->SetWriteMode(ParseWriteMode(optarg));
921 break;
923 case 'A': // add all DB names to the device builder
924 current->AddAllDBs();
925 break;
927 case 't': // include type and IDs in sha1 mode
928 current->IncludeIDs();
929 break;
931 case 'S': // show parsers and builders
932 ShowParsers();
933 return 0;
935 case 'I': // international charset (iconv)
936 iconvCharset = optarg;
937 break;
939 case 'n': // use null hex dump parser only
940 current->SetHexDump();
941 break;
943 case 'v': // verbose
944 verbose = true;
945 break;
947 case 'h': // help
948 default:
949 Usage();
950 return 0;
954 if( !Input.get() || !Outputs.size() ) {
955 Usage();
956 return 0;
959 // Initialize the Barry library
960 Barry::Init(verbose);
962 // Create an IConverter object if needed
963 auto_ptr<IConverter> ic;
964 if( iconvCharset.size() ) {
965 ic.reset( new IConverter(iconvCharset.c_str(), true) );
968 // Probe for devices only if needed
969 auto_ptr<Probe> probe;
970 if( Input->ProbeNeeded() || OutputsProbeNeeded() ) {
971 // Probe for available devices
972 probe.reset( new Probe );
975 // Setup the input first (builder)
976 Builder &builder = Input->GetBuilder(probe.get(), *ic);
978 // Setup a TeeParser with all Outputs
979 TeeParser tee;
980 for( OutputsType::iterator i = Outputs.begin(); i != Outputs.end(); ++i ) {
981 Parser &parser = (*i)->GetParser(probe.get(), *ic);
982 tee.Add(parser);
985 // Setup the pipe
986 Pipe pipe(builder);
987 pipe.PumpFile(tee, ic.get());
989 return 0;
992 int main(int argc, char *argv[])
994 try {
995 App app;
996 return app.main(argc, argv);
998 catch( std::exception &e ) {
999 cerr << "Exception: " << e.what() << endl;
1000 return 1;