3 /// Barry Input / Output
7 Copyright (C) 2010-2013, 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>
41 #include "barrygetopt.h"
45 using namespace std::tr1
;
46 using namespace Barry
;
48 // keeping a record of all the -i device / -o device pin numbers, so
49 // we can warn the poor user appropriately
50 std::vector
<Barry::Pin
> m_device_pins
;
52 bool IsPinUsed(const Barry::Pin
&pin
)
54 for( std::vector
<Barry::Pin
>::const_iterator b
= m_device_pins
.begin();
55 b
!= m_device_pins
.end(); ++b
)
65 int logical
, major
, minor
;
66 const char *Version
= Barry::Version(logical
, major
, minor
);
67 #ifdef __BARRY_BOOST_MODE__
68 string boost_mode
= _("Compiled with Boost support");
69 string boost_feature
= "boost, ";
70 string boost_options
= _("\n"
71 " Options to use for 'boost' type:\n"
72 " -f file Boost serialization filename to read from or write to\n"
73 " Can use - to specify stdin/stdout\n");
75 string boost_mode
= _("Compiled without Boost support");
80 cerr
<< string_vprintf(
81 // TRANSLATORS: the i/o types, such as device, tar, mime, etc. must
82 // be exact. They are commands that the code tests for. Do not
83 // translate those particular words. Same with the -w write mode
85 _("bio - Barry Input / Output\n"
86 " Copyright 2010-2013, Net Direct Inc. (http://www.netdirect.ca/)\n"
90 " Usage: bio -i <type> [options...] -o <type> [options...]\n"
92 " -i type The input type (Builder) to use for producing records\n"
93 " Can be one of: device, tar, %sldif, mime\n"
94 " -o type The output type (Parser) to use for processing records.\n"
95 " Multiple outputs are allowed, as long as they don't\n"
96 " conflict (such as two outputs writing to the same file\n"
98 " Can be one of: device, tar, %sldif, mime, dump, sha1, cstore\n"
100 " Options to use for 'device' type:\n"
101 " -d db Name of input database. Can be used multiple times.\n"
102 " -A Add all available device databases, instead of specifying\n"
103 " them manually via -d\n"
104 " -p pin PIN of device to talk to\n"
105 " If only one device is plugged in, this flag is optional\n"
106 " -P pass Simplistic method to specify device password\n"
107 " -w mode Set write mode when using 'device' for output. Must be\n"
108 " specified, or will not write anything.\n"
109 " Can be one of: erase, overwrite, addonly, addnew\n"
111 " Options to use for 'tar' backup type:\n"
112 " -d db Name of input database. Can be used multiple times.\n"
113 " Not available in output mode. Note that by default,\n"
114 " all databases in the backup are selected, when reading,\n"
115 " unless at least one -d is specified.\n"
116 " -D db Name of input database to skip. If no -d options are used,\n"
117 " then all databases are automatically selected. Using -D\n"
118 " allows a filtering selection. If -d and -D are used for\n"
119 " the same database, -D takes precedence.\n"
120 " -f file Tar backup file to read from or write to\n"
123 " Options to use for 'ldif' type:\n"
124 " -c dn Convert address book database to LDIF format, using the\n"
125 " specified baseDN\n"
126 " -C dnattr LDIF attribute name to use when building the FQDN\n"
127 " Defaults to 'cn'\n"
131 " -L List Contact field names\n"
132 " -m Map LDIF name to Contact field / Unmap LDIF name\n"
133 " Map: ldif,read,write - maps ldif to read/write Contact fields\n"
134 " Unmap: ldif name alone\n"
135 " -M List current LDIF mapping\n"
138 " Options to use for 'mime' type:\n"
139 " -f file Filename to read from or write to. Use - to explicitly\n"
140 " specify stdin/stdout, which is default.\n"
142 " Options to use for 'dump' to stdout output type:\n"
143 " -n Use hex dump parser on all databases.\n"
144 " -T Show only the names of the databases.\n"
146 " Options to use for 'sha1' sum stdout output type:\n"
147 " -t Include DB Name, Type, and Unique record IDs in the checksums\n"
149 " Options to use for 'cstore' output type:\n"
150 " -l List filenames only\n"
151 " -f file Filename from the above list, including path.\n"
152 " If found, the file will be written to the current\n"
153 " directory, using the base filename from the device.\n"
155 " Standalone options:\n"
157 " -I cs International charset for string conversions\n"
158 " Valid values here are available with 'iconv --list'\n"
159 " -S Show list of supported database parsers and builders.\n"
160 " Use twice to show field names as well.\n"
161 " -v Dump protocol data during operation\n"
163 Version
, boost_mode
.c_str(),
164 boost_feature
.c_str(), boost_feature
.c_str(),
165 boost_options
.c_str())
172 virtual ~ModeBase() {}
174 virtual bool ProbeNeeded() const { return false; }
176 virtual void SetFilename(const std::string
&name
)
178 throw runtime_error(_("Filename not applicable for this mode"));
181 virtual void AddDB(const std::string
&dbname
)
183 throw runtime_error(_("DB not applicable for this mode"));
186 virtual void AddSkipDB(const std::string
&dbname
)
188 throw runtime_error(_("DB skipping not applicable for this mode"));
191 virtual void AddAllDBs()
193 throw runtime_error(_("DBs not applicable for this mode"));
196 virtual void SetPIN(const std::string
&pin
)
198 throw runtime_error(_("PIN not applicable for this mode"));
201 virtual void SetPassword(const std::string
&password
)
203 throw runtime_error(_("Password not applicable for this mode"));
206 virtual void SetWriteMode(DeviceParser::WriteMode mode
)
208 throw runtime_error(_("Device write behaviour not applicable for this mode"));
211 virtual void SetDN(const std::string
&dn
)
213 throw runtime_error(_("DN not applicable for this mode"));
216 virtual void SetAttribute(const std::string
&attr
)
218 throw runtime_error(_("Attribute not applicable for this mode"));
221 virtual void SetHexDump()
223 throw runtime_error(_("No hex dump option in this mode"));
226 virtual void SetDBNamesOnly()
228 throw runtime_error(_("No name-only option in this mode"));
231 virtual void IncludeIDs()
233 throw runtime_error(_("Including record IDs in the SHA1 sum is not applicable in this mode"));
236 virtual void SetList()
238 throw runtime_error(_("List option not applicable for this mode"));
242 class DeviceBase
: public virtual ModeBase
246 std::string m_password
;
249 bool ProbeNeeded() const { return true; }
251 void SetPIN(const std::string
&pin
)
253 istringstream
iss(pin
);
256 throw runtime_error(_("Invalid PIN: ") + pin
);
259 void SetPassword(const std::string
&password
)
261 m_password
= password
;
265 //////////////////////////////////////////////////////////////////////////////
266 // Base class for Input Mode
268 class InputBase
: public virtual ModeBase
271 virtual Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
) = 0;
274 class DeviceInputBase
: public DeviceBase
, public InputBase
278 //////////////////////////////////////////////////////////////////////////////
279 // Mode: Input, Type: device
281 class DeviceInput
: public DeviceInputBase
283 auto_ptr
<Controller
> m_con
;
284 auto_ptr
<Mode::Desktop
> m_desktop
;
285 auto_ptr
<DeviceBuilder
> m_builder
;
286 vector
<string
> m_dbnames
;
295 void AddDB(const std::string
&dbname
)
297 m_dbnames
.push_back(dbname
);
305 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
307 int i
= probe
->FindActive(m_pin
);
310 throw runtime_error(_("PIN not found: ") + m_pin
.Str());
312 throw runtime_error(_("PIN not specified, and more than one device exists."));
315 if( IsPinUsed(probe
->Get(i
).m_pin
) ) {
316 throw runtime_error(_("It seems you are trying to use the same device for multiple input or outputs."));
318 m_device_pins
.push_back(probe
->Get(i
).m_pin
);
320 m_con
.reset( new Controller(probe
->Get(i
)) );
321 m_desktop
.reset( new Mode::Desktop(*m_con
, ic
) );
322 m_desktop
->Open(m_password
.c_str());
323 m_builder
.reset( new DeviceBuilder(*m_desktop
) );
326 m_builder
->Add(m_desktop
->GetDBDB());
329 for( size_t i
= 0; i
< m_dbnames
.size(); i
++ ) {
330 if( !m_builder
->Add(m_dbnames
[i
]) )
331 throw runtime_error(_("Database not found: ") + m_dbnames
[i
]);
339 //////////////////////////////////////////////////////////////////////////////
340 // Mode: Input, Type: tar
342 class TarInput
: public InputBase
344 auto_ptr
<Restore
> m_restore
;
346 vector
<string
> m_dbnames
, m_dbskips
;
349 void SetFilename(const std::string
&name
)
353 throw runtime_error(_("Cannot use stdin as tar source file, sorry."));
356 void AddDB(const std::string
&dbname
)
358 m_dbnames
.push_back(dbname
);
361 void AddSkipDB(const std::string
&dbname
)
363 m_dbskips
.push_back(dbname
);
366 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
368 m_restore
.reset( new Restore(m_tarpath
, true) );
369 for( size_t i
= 0; i
< m_dbnames
.size(); i
++ ) {
370 m_restore
->AddDB(m_dbnames
[i
]);
372 for( size_t i
= 0; i
< m_dbskips
.size(); i
++ ) {
373 m_restore
->AddSkipDB(m_dbskips
[i
]);
380 //////////////////////////////////////////////////////////////////////////////
381 // Mode: Input, Type: boost
383 #ifdef __BARRY_BOOST_MODE__
384 class BoostInput
: public InputBase
386 auto_ptr
<BoostBuilder
> m_builder
;
391 : m_filename("-") // default to stdin/stdout
395 void SetFilename(const std::string
&name
)
400 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
402 if( m_filename
== "-" ) {
404 m_builder
.reset( new BoostBuilder(cin
) );
407 m_builder
.reset( new BoostBuilder(m_filename
) );
415 //////////////////////////////////////////////////////////////////////////////
416 // Mode: Input, Type: ldif
418 class LdifInput
: public InputBase
420 auto_ptr
<Builder
> m_builder
;
429 void SetFilename(const std::string
&name
)
434 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
436 if( m_filename
== "-" ) {
439 new RecordBuilder
<Contact
, LdifStore
>(
440 new LdifStore(cin
)) );
444 new RecordBuilder
<Contact
, LdifStore
>(
445 new LdifStore(m_filename
)) );
453 //////////////////////////////////////////////////////////////////////////////
454 // Mode: Input, Type: mime
456 class MimeInput
: public InputBase
458 auto_ptr
<MimeBuilder
> m_builder
;
467 void SetFilename(const std::string
&name
)
472 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
474 if( m_filename
== "-" ) {
476 m_builder
.reset( new MimeBuilder(cin
) );
479 m_builder
.reset( new MimeBuilder(m_filename
) );
486 //////////////////////////////////////////////////////////////////////////////
487 // Base class for Output Mode
489 class OutputBase
: public virtual ModeBase
492 virtual Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
) = 0;
495 class DeviceOutputBase
: public DeviceBase
, public OutputBase
499 //////////////////////////////////////////////////////////////////////////////
500 // Mode: Output, Type: device
502 class DeviceOutput
: public DeviceOutputBase
504 auto_ptr
<Controller
> m_con
;
505 auto_ptr
<Mode::Desktop
> m_desktop
;
506 auto_ptr
<DeviceParser
> m_parser
;
507 DeviceParser::WriteMode m_mode
;
511 : m_mode(DeviceParser::DROP_RECORD
)
515 void SetWriteMode(DeviceParser::WriteMode mode
)
520 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
522 if( m_mode
== DeviceParser::DROP_RECORD
) {
523 cerr
<< _("Warning: the -w switch was not specified: no data will be written to the device.") << endl
;
526 int i
= probe
->FindActive(m_pin
);
529 throw runtime_error(_("PIN not found: ") + m_pin
.Str());
531 throw runtime_error(_("PIN not specified, and more than one device exists."));
534 if( IsPinUsed(probe
->Get(i
).m_pin
) ) {
535 throw runtime_error(_("It seems you are trying to use the same device for multiple input or outputs."));
537 m_device_pins
.push_back(probe
->Get(i
).m_pin
);
539 m_con
.reset( new Controller(probe
->Get(i
)) );
540 m_desktop
.reset( new Mode::Desktop(*m_con
, ic
) );
541 m_desktop
->Open(m_password
.c_str());
542 m_parser
.reset( new DeviceParser(*m_desktop
, m_mode
) );
548 //////////////////////////////////////////////////////////////////////////////
549 // Mode: Output, Type: tar
551 class TarOutput
: public OutputBase
553 auto_ptr
<Backup
> m_backup
;
557 void SetFilename(const std::string
&name
)
561 throw runtime_error(_("Cannot use stdout as tar backup file, sorry."));
564 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
566 m_backup
.reset( new Backup(m_tarpath
) );
571 //////////////////////////////////////////////////////////////////////////////
572 // Mode: Output, Type: boost
574 #ifdef __BARRY_BOOST_MODE__
575 class BoostOutput
: public OutputBase
577 auto_ptr
<BoostParser
> m_parser
;
581 void SetFilename(const std::string
&name
)
586 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
588 if( !m_filename
.size() )
589 throw runtime_error(_("Boost output requires a specific output file (-f switch)"));
591 if( m_filename
== "-" ) {
593 m_parser
.reset( new BoostParser(cout
) );
596 m_parser
.reset( new BoostParser(m_filename
) );
604 //////////////////////////////////////////////////////////////////////////////
605 // Mode: Output, Type: ldif
607 class LdifOutput
: public OutputBase
609 auto_ptr
<Parser
> m_parser
;
620 void SetFilename(const std::string
&name
)
625 void SetDN(const std::string
&dn
)
630 void SetAttribute(const std::string
&attr
)
635 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
637 if( m_filename
== "-" ) {
640 new RecordParser
<Contact
, LdifStore
>(
641 new LdifStore(cout
, m_baseDN
,
646 new RecordParser
<Contact
, LdifStore
>(
647 new LdifStore(m_filename
, m_baseDN
,
654 //////////////////////////////////////////////////////////////////////////////
655 // Mode: Output, Type: mime
657 class MimeStore
: public AllRecordStore
662 MimeStore(std::ostream
&os
)
668 #define HANDLE_PARSER(tname) \
669 void operator() (const Barry::tname &r) \
671 MimeDump<tname>::Dump(m_os, r); \
674 ALL_KNOWN_PARSER_TYPES
677 class MimeOutput
: public OutputBase
679 auto_ptr
<std::ofstream
> m_file
;
680 auto_ptr
<Parser
> m_parser
;
681 std::string m_filename
;
685 : m_filename("-") // default to stdout
689 void SetFilename(const std::string
&name
)
694 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
696 if( m_filename
== "-" ) {
697 m_parser
.reset( new AllRecordParser(cout
,
698 new HexDumpParser(cout
),
699 new MimeStore(cout
)) );
702 m_file
.reset( new std::ofstream(m_filename
.c_str()) );
703 m_parser
.reset( new AllRecordParser(*m_file
,
704 new HexDumpParser(*m_file
),
705 new MimeStore(*m_file
)) );
711 //////////////////////////////////////////////////////////////////////////////
712 // Mode: Output, Type: dump
714 class DumpOutput
: public OutputBase
716 auto_ptr
<Parser
> m_parser
;
723 , m_dbnames_only(false)
732 void SetDBNamesOnly()
734 m_dbnames_only
= true;
737 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
740 m_parser
.reset( new HexDumpParser(cout
) );
742 else if( m_dbnames_only
) {
743 m_parser
.reset( new DBNamesOnlyParser(cout
) );
746 m_parser
.reset( new AllRecordParser(cout
,
747 new HexDumpParser(cout
),
748 new AllRecordDumpStore(cout
)) );
754 //////////////////////////////////////////////////////////////////////////////
755 // Mode: Output, Type: sha1
757 class Sha1Output
: public OutputBase
759 auto_ptr
<Parser
> m_parser
;
764 : m_include_ids(false)
770 m_include_ids
= true;
773 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
775 m_parser
.reset( new ChecksumParser(m_include_ids
) );
780 //////////////////////////////////////////////////////////////////////////////
781 // Mode: Output, Type: cstore
783 class ContentStoreOutput
: public OutputBase
785 auto_ptr
<Parser
> m_parser
;
787 vector
<string
> m_filenames
;
795 void SetFilename(const std::string
&name
)
797 m_filenames
.push_back(name
);
805 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
807 m_parser
.reset( new RecordParser
<ContentStore
, ContentStoreOutput
>(*this) );
812 void operator() (const ContentStore
&rec
)
815 cout
<< rec
.Filename
;
816 if( rec
.FolderFlag
) {
817 cout
<< _(" (folder)");
822 // check if this record matches one of the filenames
824 vector
<string
>::iterator i
= find(m_filenames
.begin(),
825 m_filenames
.end(), rec
.Filename
);
826 if( i
!= m_filenames
.end() ) {
832 void SaveFile(const ContentStore
&rec
)
834 size_t slash
= rec
.Filename
.rfind('/');
836 if( slash
== string::npos
)
837 filename
= rec
.Filename
;
839 filename
= rec
.Filename
.substr(slash
+ 1);
841 // modify filename until we find one that doesn't
843 string freshname
= filename
;
845 while( access(freshname
.c_str(), F_OK
) == 0 ) {
847 oss
<< filename
<< count
++;
848 freshname
= oss
.str();
852 cout
<< string_vprintf(_("Saving: %s as %s"),
853 rec
.Filename
.c_str(), freshname
.c_str())
855 ofstream
ofs(freshname
.c_str());
856 ofs
<< rec
.FileContent
;
859 cout
<< _("Error during write!") << endl
;
866 //////////////////////////////////////////////////////////////////////////////
867 // Main application class
872 typedef shared_ptr
<OutputBase
> OutputPtr
;
873 typedef vector
<OutputPtr
> OutputsType
;
876 auto_ptr
<InputBase
> Input
;
881 bool ParseInMode(const string
&mode
);
882 bool ParseOutMode(const string
&mode
);
883 DeviceParser::WriteMode
ParseWriteMode(const std::string
&mode
);
884 // returns true if any of the items in Outputs needs a probe
885 bool OutputsProbeNeeded();
886 int main(int argc
, char *argv
[]);
889 bool App::ParseInMode(const string
&mode
)
891 if( mode
== "device" ) {
892 Input
.reset( new DeviceInput
);
895 else if( mode
== "tar" ) {
896 Input
.reset( new TarInput
);
899 #ifdef __BARRY_BOOST_MODE__
900 else if( mode
== "boost" ) {
901 Input
.reset( new BoostInput
);
905 else if( mode
== "ldif" ) {
906 Input
.reset( new LdifInput
);
909 else if( mode
== "mime" ) {
910 Input
.reset( new MimeInput
);
917 bool App::ParseOutMode(const string
&mode
)
919 if( mode
== "device" ) {
920 Outputs
.push_back( OutputPtr(new DeviceOutput
) );
923 else if( mode
== "tar" ) {
924 Outputs
.push_back( OutputPtr(new TarOutput
) );
927 #ifdef __BARRY_BOOST_MODE__
928 else if( mode
== "boost" ) {
929 Outputs
.push_back( OutputPtr(new BoostOutput
) );
933 else if( mode
== "ldif" ) {
934 Outputs
.push_back( OutputPtr(new LdifOutput
) );
937 else if( mode
== "mime" ) {
938 Outputs
.push_back( OutputPtr(new MimeOutput
) );
941 else if( mode
== "dump" ) {
942 Outputs
.push_back( OutputPtr(new DumpOutput
) );
945 else if( mode
== "sha1" ) {
946 Outputs
.push_back( OutputPtr(new Sha1Output
) );
949 else if( mode
== "cstore" ) {
950 Outputs
.push_back( OutputPtr(new ContentStoreOutput
) );
957 DeviceParser::WriteMode
App::ParseWriteMode(const std::string
&mode
)
959 if( mode
== "erase" )
960 return DeviceParser::ERASE_ALL_WRITE_ALL
;
961 else if( mode
== "overwrite" )
962 return DeviceParser::INDIVIDUAL_OVERWRITE
;
963 else if( mode
== "addonly" )
964 return DeviceParser::ADD_BUT_NO_OVERWRITE
;
965 else if( mode
== "addnew" )
966 return DeviceParser::ADD_WITH_NEW_ID
;
968 throw runtime_error(_("Unknown device output mode. Must be one of: erase, overwrite, addonly, addnew"));
971 bool App::OutputsProbeNeeded()
973 for( OutputsType::iterator i
= Outputs
.begin();
977 if( (*i
)->ProbeNeeded() )
983 int App::main(int argc
, char *argv
[])
985 bool verbose
= false;
986 bool show_parsers
= false, show_fields
= false;
989 // process command line options
990 ModeBase
*current
= 0;
992 int cmd
= getopt(argc
, argv
, "hi:o:nvI:f:p:P:d:D:c:C:ASw:tTl");
996 // first option must be in or out, or a global option
1011 case 'i': // set input mode
1012 // must be first time used
1013 if( Input
.get() || !ParseInMode(optarg
) ) {
1017 current
= Input
.get();
1020 case 'o': // set output mode
1021 // can be used multiple times
1022 if( !ParseOutMode(optarg
) ) {
1026 current
= Outputs
[Outputs
.size() - 1].get();
1030 case 'c': // set ldif dn
1031 current
->SetDN(optarg
);
1034 case 'C': // set ldif attr
1035 current
->SetAttribute(optarg
);
1038 case 'd': // database name
1039 current
->AddDB(optarg
);
1042 case 'D': // database name to skip
1043 current
->AddSkipDB(optarg
);
1046 case 'f': // filename
1047 current
->SetFilename(optarg
);
1050 case 'p': // device PIN
1051 current
->SetPIN(optarg
);
1054 case 'P': // password
1055 current
->SetPassword(optarg
);
1058 case 'w': // device write mode
1059 current
->SetWriteMode(ParseWriteMode(optarg
));
1062 case 'A': // add all DB names to the device builder
1063 current
->AddAllDBs();
1066 case 't': // include type and IDs in sha1 mode
1067 current
->IncludeIDs();
1070 case 'T': // display database names only instead of recs
1071 current
->SetDBNamesOnly();
1074 case 'l': // list only
1078 case 'S': // show parsers and builders
1082 show_parsers
= true;
1085 case 'I': // international charset (iconv)
1086 iconvCharset
= optarg
;
1089 case 'n': // use null hex dump parser only
1090 current
->SetHexDump();
1093 case 'v': // verbose
1104 if( show_parsers
) {
1105 ShowParsers(show_fields
, true);
1110 if( !Input
.get() || !Outputs
.size() ) {
1115 // Initialize the Barry library
1116 Barry::Init(verbose
);
1118 // Create an IConverter object if needed
1119 auto_ptr
<IConverter
> ic
;
1120 if( iconvCharset
.size() ) {
1121 ic
.reset( new IConverter(iconvCharset
.c_str(), true) );
1124 // Probe for devices only if needed
1125 auto_ptr
<Probe
> probe
;
1126 if( Input
->ProbeNeeded() || OutputsProbeNeeded() ) {
1127 // Probe for available devices
1128 probe
.reset( new Probe
);
1131 // Setup the input first (builder)
1132 Builder
&builder
= Input
->GetBuilder(probe
.get(), *ic
);
1134 // Setup a TeeParser with all Outputs
1136 for( OutputsType::iterator i
= Outputs
.begin(); i
!= Outputs
.end(); ++i
) {
1137 Parser
&parser
= (*i
)->GetParser(probe
.get(), *ic
);
1143 pipe
.PumpFile(tee
, ic
.get());
1148 int main(int argc
, char *argv
[])
1154 return app
.main(argc
, argv
);
1156 catch( std::exception
&e
) {
1157 cerr
<< _("Exception: ") << e
.what() << endl
;