1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // commandline_full.cpp
19 //
20
21 #include "gdef.h"
22 #include "gssl.h"
23 #include "gsmtp.h"
24 #include "legal.h"
25 #include "configuration.h"
26 #include "commandline.h"
27 #include "gmessagestore.h"
28 #include "gpopsecrets.h"
29 #include "ggetopt.h"
30 #include "gtest.h"
31 #include "gstr.h"
32 #include "gdebug.h"
33
34 namespace Main
35 {
36 class Show ;
37 }
38
39 /// \class Main::CommandLineImp
40 /// A private implementation class used by Main::CommandLine.
41 ///
42 class Main::CommandLineImp
43 {
44 public:
45 CommandLineImp( Main::Output & , const G::Arg & arg , const std::string & spec ,
46 const std::string & version , const std::string & capabilities ) ;
47 bool contains( const std::string & switch_ ) const ;
48 std::string value( const std::string & switch_ ) const ;
49 G::Arg::size_type argc() const ;
50 bool hasUsageErrors() const ;
51 bool hasSemanticError( const Configuration & ) const ;
52 void showHelp( bool error_stream ) const ;
53 void showUsageErrors( bool error_stream ) const ;
54 void showSemanticError( const Configuration & cfg , bool error_stream ) const ;
55 void logSemanticWarnings( const Configuration & cfg ) const ;
56 void showArgcError( bool error_stream ) const ;
57 void showNoop( bool error_stream = false ) const ;
58 void showError( std::string reason , bool error_stream = true ) const ;
59 void showVersion( bool error_stream = false ) const ;
60 void showBanner( bool error_stream = false , const std::string & = std::string() ) const ;
61 void showCopyright( bool error_stream = false , const std::string & = std::string() ) const ;
62 void showCapabilities( bool error_stream = false , const std::string & = std::string() ) const ;
63 static std::string switchSpec( bool is_windows ) ;
64
65 public:
66 void showWarranty( bool error_stream = false , const std::string & = std::string() ) const ;
67 void showCredit( bool error_stream = false , const std::string & = std::string() ) const ;
68 void showTestFeatures( bool error_stream = false , const std::string & = std::string() ) const ;
69 void showShortHelp( bool error_stream ) const ;
70 std::string semanticError( const Configuration & , bool & ) const ;
71 void showUsage( bool e ) const ;
72 void showExtraHelp( bool error_stream ) const ;
73 static std::string switchSpec_unix() ;
74 static std::string switchSpec_windows() ;
75
76 private:
77 Output & m_output ;
78 std::string m_version ;
79 std::string m_capabilities ;
80 G::Arg m_arg ;
81 G::GetOpt m_getopt ;
82 } ;
83
84 /// \class Main::Show
85 /// A private implementation class used by Main::CommandLineImp.
86 ///
87 class Main::Show
88 {
89 public:
90 Show( Main::Output & , bool e ) ;
91 std::ostream & s() ;
92 ~Show() ;
93
94 private:
95 static Show * m_this ;
96 Show( const Show & ) ; // not implemented
97 void operator=( const Show & ) ; // not implemented
98 std::ostringstream m_ss ;
99 Main::Output & m_output ;
100 bool m_e ;
101 } ;
102
103 // ==
104
switchSpec(bool is_windows)105 std::string Main::CommandLineImp::switchSpec( bool is_windows )
106 {
107 // single-character options unused: 012345678
108 std::string dir = GSmtp::MessageStore::defaultDirectory().str() ;
109 std::string pop_auth = GPop::Secrets::defaultPath() ;
110 std::ostringstream ss ;
111 ss <<
112 (is_windows?switchSpec_windows():switchSpec_unix()) << "|"
113 "q!as-client!runs as a client, forwarding all spooled mail to <host>!: "
114 "equivalent to \"--log --no-syslog --no-daemon --dont-serve --forward --forward-to\"!"
115 "1!host:port!1|"
116 "d!as-server!runs as a server, storing mail in the spool directory!: equivalent to \"--log --close-stderr\"!0!!1|"
117 "y!as-proxy!runs as a proxy server, forwarding each mail immediately to <host>!"
118 ": equivalent to \"--log --close-stderr --poll=0 --forward-to\"!"
119 "1!host:port!1|"
120 "v!verbose!generates more verbose output! (works with --help and --log)!0!!1|"
121 "h!help!displays help text and exits!!0!!1|"
122 ""
123 "p!port!specifies the smtp listening port number (default is 25)!!1!port!2|"
124 "r!remote-clients!allows remote clients to connect!!0!!2|"
125 "s!spool-dir!specifies the spool directory (default is \"" << dir << "\")!!1!dir!2|"
126 "V!version!displays version information and exits!!0!!2|"
127 ""
128 "j!client-tls!enables negotiated tls/ssl for smtp client! (if openssl built in)!0!!3|"
129 "b!client-tls-connection!enables smtp over tls/ssl for smtp client! (if openssl built in)!0!!3|"
130 "K!server-tls!enables negotiated tls/ssl for smtp server using the given openssl certificate file! (which must be in the directory trusted by openssl)!1!pem-file!3|"
131 "9!tls-config!sets tls configuration flags! (eg. 2 for SSLv2/3 support)!1!flags!3|"
132 "g!debug!generates debug-level logging if built in!!0!!3|"
133 "C!client-auth!enables smtp authentication with the remote server, using the given secrets file!!1!file!3|"
134 "L!log-time!adds a timestamp to the logging output!!0!!3|"
135 "N!log-file!log to file instead of stderr! (%d replaced by the date)!1!file!3|"
136 "S!server-auth!enables authentication of remote clients, using the given secrets file!!1!file!3|"
137 "e!close-stderr!closes the standard error stream soon after start-up!!0!!3|"
138 "a!admin!enables the administration interface and specifies its listening port number!!"
139 "1!admin-port!3|"
140 "x!dont-serve!disables acting as a server on any port! (part of --as-client and usually used with --forward)!0!!3|"
141 "X!no-smtp!disables listening for smtp connections! (usually used with --admin or --pop)!0!!3|"
142 "z!filter!specifies an external program to process messages as they are stored!!1!program!3|"
143 "W!filter-timeout!sets the timeout (in seconds) for running the --filter processor (default is 300)!!1!time!3|"
144 "w!prompt-timeout!sets the timeout (in seconds) for getting an initial prompt from the server (default is 20)!!1!time!3|"
145 "D!domain!sets an override for the host's fully qualified domain name!!1!fqdn!3|"
146 "f!forward!forwards stored mail on startup! (requires --forward-to)!0!!3|"
147 "o!forward-to!specifies the remote smtp server! (required by --forward, --poll, --immediate and --admin)!1!host:port!3|"
148 "T!response-timeout!sets the response timeout (in seconds) when talking to a remote server "
149 "(default is 1800)!!1!time!3|"
150 "U!connection-timeout!sets the timeout (in seconds) when connecting to a remote server "
151 "(default is 40)!!1!time!3|"
152 "m!immediate!enables immediate forwarding of messages as soon as they are received! (requires --forward-to)!0!!3|"
153 "I!interface!defines the listening interface(s) for incoming connections! (comma-separated list with optional smtp=,pop=,admin= qualifiers)!1!ip-list!3|"
154 "i!pid-file!defines a file for storing the daemon process-id!!1!pid-file!3|"
155 "O!poll!enables polling of the spool directory for messages to be forwarded with the specified period (zero means on client disconnection)! (requires --forward-to)!1!period!3|"
156 "P!postmaster!!!0!!0|"
157 "Z!verifier!specifies an external program for address verification!!1!program!3|"
158 "Y!client-filter!specifies an external program to process messages when they are forwarded!!1!program!3|"
159 "Q!admin-terminate!enables the terminate command on the admin interface!!0!!3|"
160 "A!anonymous!disables the smtp vrfy command and sends less verbose smtp responses!!0!!3|"
161 "B!pop!enables the pop server!!0!!3|"
162 "E!pop-port!specifies the pop listening port number (default is 110)! (requires --pop)!1!port!3|"
163 "F!pop-auth!defines the pop server secrets file (default is \"" << pop_auth << "\")!!1!file!3|"
164 "G!pop-no-delete!disables message deletion via pop! (requires --pop)!0!!3|"
165 "J!pop-by-name!modifies the pop spool directory according to the pop user name! (requires --pop)!0!!3|"
166 "M!size!limits the size of submitted messages!!1!bytes!3|"
167 ;
168 return ss.str() ;
169 }
170
switchSpec_unix()171 std::string Main::CommandLineImp::switchSpec_unix()
172 {
173 return
174 "l!log!writes log information on standard error and syslog! (but see --close-stderr and --no-syslog)!0!!2|"
175 "t!no-daemon!does not detach from the terminal!!0!!3|"
176 "u!user!names the effective user to switch to if started as root "
177 "(default is \"daemon\")!!1!username!3|"
178 "k!syslog!forces syslog output if logging is enabled (overrides --no-syslog)!!0!!3|"
179 "n!no-syslog!disables syslog output (always overridden by --syslog)!!0!!3" ;
180 }
181
switchSpec_windows()182 std::string Main::CommandLineImp::switchSpec_windows()
183 {
184 return
185 "l!log!writes log information on stderr and to the event log! (but see --close-stderr and --no-syslog)!0!!2|"
186 "t!no-daemon!uses an ordinary window, not the system tray!!0!!3|"
187 "k!syslog!forces system event log output if logging is enabled (overrides --no-syslog)!!0!!3|"
188 "n!no-syslog!disables use of the system event log!!0!!3|"
189 "c!icon!does nothing!!1!0^|1^|2^|3!0|"
190 "H!hidden!hides the application window and suppresses message boxes (requires --no-daemon)!!0!!3|"
191 "R!peer-lookup!lookup the account names of local peers! to put in the envelope files!0!!3" ;
192 }
193
CommandLineImp(Output & output,const G::Arg & arg,const std::string & spec,const std::string & version,const std::string & capabilities)194 Main::CommandLineImp::CommandLineImp( Output & output , const G::Arg & arg , const std::string & spec ,
195 const std::string & version , const std::string & capabilities ) :
196 m_output(output) ,
197 m_version(version) ,
198 m_capabilities(capabilities) ,
199 m_arg(arg) ,
200 m_getopt(m_arg,spec,'|','!','^')
201 {
202 }
203
argc() const204 G::Arg::size_type Main::CommandLineImp::argc() const
205 {
206 return m_getopt.args().c() ;
207 }
208
hasUsageErrors() const209 bool Main::CommandLineImp::hasUsageErrors() const
210 {
211 return m_getopt.hasErrors() ;
212 }
213
showUsage(bool e) const214 void Main::CommandLineImp::showUsage( bool e ) const
215 {
216 Show show( m_output , e ) ;
217
218 G::GetOpt::Level level = G::GetOpt::Level(2U) ;
219 std::string introducer = G::GetOpt::introducerDefault() ;
220 if( m_getopt.contains("verbose") )
221 level = G::GetOpt::levelDefault() ;
222 else
223 introducer = std::string("abbreviated ") + introducer ;
224
225 std::string::size_type tab_stop = 34U ;
226 bool extra = m_getopt.contains("verbose") ;
227 m_getopt.showUsage( show.s() , m_arg.prefix() , "" , introducer , level , tab_stop ,
228 G::GetOpt::wrapDefault() , extra ) ;
229 }
230
contains(const std::string & name) const231 bool Main::CommandLineImp::contains( const std::string & name ) const
232 {
233 return m_getopt.contains( name ) ;
234 }
235
value(const std::string & name) const236 std::string Main::CommandLineImp::value( const std::string & name ) const
237 {
238 return m_getopt.value( name ) ;
239 }
240
semanticError(const Configuration & cfg,bool & fatal) const241 std::string Main::CommandLineImp::semanticError( const Configuration & cfg , bool & fatal ) const
242 {
243 fatal = true ;
244
245 if(
246 ( cfg.doAdmin() && cfg.adminPort() == cfg.port() ) ||
247 ( cfg.doPop() && cfg.popPort() == cfg.port() ) ||
248 ( cfg.doPop() && cfg.doAdmin() && cfg.popPort() == cfg.adminPort() ) )
249 {
250 return "the listening ports must be different" ;
251 }
252
253 if( ! m_getopt.contains("pop") && (
254 m_getopt.contains("pop-port") ||
255 m_getopt.contains("pop-auth") ||
256 m_getopt.contains("pop-by-name") ||
257 m_getopt.contains("pop-no-delete") ) )
258 {
259 return "pop options require --pop" ;
260 }
261
262 if( cfg.withTerminate() && !cfg.doAdmin() )
263 {
264 return "the --admin-terminate option requires --admin" ;
265 }
266
267 if( cfg.daemon() && cfg.spoolDir().isRelative() )
268 {
269 return "in daemon mode the spool-dir must be an absolute path" ;
270 }
271
272 if( cfg.daemon() && (
273 ( !cfg.clientSecretsFile().empty() && G::Path(cfg.clientSecretsFile()).isRelative() ) ||
274 ( !cfg.serverSecretsFile().empty() && G::Path(cfg.serverSecretsFile()).isRelative() ) ||
275 ( !cfg.popSecretsFile().empty() && G::Path(cfg.popSecretsFile()).isRelative() ) ) )
276 {
277 return "in daemon mode the authorisation secrets file(s) must be absolute paths" ;
278 }
279
280 const bool forward_to =
281 m_getopt.contains("as-proxy") || // => forward-to
282 m_getopt.contains("as-client") || // => forward-to
283 m_getopt.contains("forward-to") ;
284
285 if( ! forward_to && (
286 m_getopt.contains("forward") ||
287 m_getopt.contains("poll") ||
288 m_getopt.contains("immediate") ) )
289 {
290 return "the --forward, --immediate and --poll options require --forward-to" ;
291 }
292
293 const bool forwarding =
294 m_getopt.contains("as-proxy") || // => poll
295 m_getopt.contains("as-client") || // => forward
296 m_getopt.contains("forward") ||
297 m_getopt.contains("immediate") ||
298 m_getopt.contains("poll") ;
299
300 if( m_getopt.contains("client-filter") && ! forwarding )
301 {
302 return "the --client-filter option requires --as-proxy, --as-client, --poll, --immediate or --forward" ;
303 }
304
305 const bool not_serving = m_getopt.contains("dont-serve") || m_getopt.contains("as-client") ;
306
307 if( not_serving ) // ie. if not serving admin, smtp or pop
308 {
309 if( m_getopt.contains("filter") )
310 return "the --filter option cannot be used with --as-client or --dont-serve" ;
311
312 if( m_getopt.contains("port") )
313 return "the --port option cannot be used with --as-client or --dont-serve" ;
314
315 if( m_getopt.contains("server-auth") )
316 return "the --server-auth option cannot be used with --as-client or --dont-serve" ;
317
318 if( m_getopt.contains("pop") )
319 return "the --pop option cannot be used with --as-client or --dont-serve" ;
320
321 if( m_getopt.contains("admin") )
322 return "the --admin option cannot be used with --as-client or --dont-serve" ;
323
324 if( m_getopt.contains("poll") )
325 return "the --poll option cannot be used with --as-client or --dont-serve" ;
326 }
327
328 if( m_getopt.contains("no-smtp") ) // ie. if not serving smtp
329 {
330 if( m_getopt.contains("filter") )
331 return "the --filter option cannot be used with --no-smtp" ;
332
333 if( m_getopt.contains("port") )
334 return "the --port option cannot be used with --no-smtp" ;
335
336 if( m_getopt.contains("server-auth") )
337 return "the --server-auth option cannot be used with --no-smtp" ;
338 }
339
340 const bool log =
341 m_getopt.contains("log") ||
342 m_getopt.contains("as-server") || // => log
343 m_getopt.contains("as-client") || // => log
344 m_getopt.contains("as-proxy") ; // => log
345
346 if( m_getopt.contains("verbose") && ! ( m_getopt.contains("help") || log ) )
347 {
348 return "the --verbose option must be used with --log, --help, --as-client, --as-server or --as-proxy" ;
349 }
350
351 if( m_getopt.contains("debug") && !log )
352 {
353 return "the --debug option requires --log, --as-client, --as-server or --as-proxy" ;
354 }
355
356 const bool no_daemon =
357 m_getopt.contains("as-client") || // => no-daemon
358 m_getopt.contains("no-daemon") ;
359
360 if( m_getopt.contains("hidden") && ! no_daemon ) // (win32)
361 {
362 return "the --hidden option requires --no-daemon or --as-client" ;
363 }
364
365 if( m_getopt.contains("client-tls") && m_getopt.contains("client-tls-connection") )
366 {
367 return "the --client-tls and --client-tls-connection options cannot be used together" ;
368 }
369
370 if( m_getopt.contains("server-auth") && m_getopt.value("server-auth") == "/pam" &&
371 !m_getopt.contains("server-tls" ) )
372 {
373 return "--server-auth using pam requires --server-tls" ;
374 }
375
376 if( m_getopt.contains("pop-auth") && m_getopt.value("pop-auth") == "/pam" &&
377 !m_getopt.contains("server-tls" ) )
378 {
379 return "--pop-auth using pam requires --server-tls" ;
380 }
381
382 // warnings...
383
384 const bool no_syslog =
385 m_getopt.contains("no-syslog") ||
386 m_getopt.contains("as-client") ;
387
388 const bool syslog =
389 ! ( no_syslog && ! m_getopt.contains("syslog") ) ;
390
391 const bool close_stderr =
392 m_getopt.contains("close-stderr") ||
393 m_getopt.contains("as-server") ||
394 m_getopt.contains("as-proxy") ;
395
396 if( log && close_stderr && !syslog ) // ie. logging to nowhere
397 {
398 std::string close_stderr_switch =
399 ( m_getopt.contains("close-stderr") ? "--close-stderr" :
400 ( m_getopt.contains("as-server") ? "--as-server" :
401 "--as-proxy" ) ) ;
402
403 std::string warning = "logging is enabled but it has nowhere to go because " +
404 close_stderr_switch + " closes the standard error stream soon after startup and " +
405 "output to the system log is disabled" ;
406
407 if( m_getopt.contains("as-server") && !m_getopt.contains("log") )
408 warning = warning + ": replace --as-server with --log" ;
409 else if( m_getopt.contains("as-server") )
410 warning = warning + ": remove --as-server" ;
411 else if( m_getopt.contains("as-proxy" ) )
412 warning = warning + ": replace --as-proxy with --log --poll 0 --forward-to" ;
413
414 fatal = false ;
415 return warning ;
416 }
417
418 fatal = false ;
419 return std::string() ;
420 }
421
hasSemanticError(const Configuration & cfg) const422 bool Main::CommandLineImp::hasSemanticError( const Configuration & cfg ) const
423 {
424 bool fatal = false ;
425 bool error = ! semanticError(cfg,fatal).empty() ;
426 return error && fatal ;
427 }
428
showSemanticError(const Configuration & cfg,bool e) const429 void Main::CommandLineImp::showSemanticError( const Configuration & cfg , bool e ) const
430 {
431 Show show( m_output , e ) ;
432 bool fatal = false ;
433 show.s() << m_arg.prefix() << ": usage error: " << semanticError(cfg,fatal) << std::endl ;
434 }
435
logSemanticWarnings(const Configuration & cfg) const436 void Main::CommandLineImp::logSemanticWarnings( const Configuration & cfg ) const
437 {
438 bool fatal = false ;
439 std::string warning = semanticError( cfg , fatal ) ;
440 if( !warning.empty() && !fatal )
441 G_WARNING( "CommandLine::logSemanticWarnings: " << warning ) ;
442 }
443
showUsageErrors(bool e) const444 void Main::CommandLineImp::showUsageErrors( bool e ) const
445 {
446 Show show( m_output , e ) ;
447 m_getopt.showErrors( show.s() , m_arg.prefix() ) ;
448 showShortHelp( e ) ;
449 }
450
showArgcError(bool e) const451 void Main::CommandLineImp::showArgcError( bool e ) const
452 {
453 Show show( m_output , e ) ;
454 show.s() << m_arg.prefix() << ": usage error: too many non-switch arguments" << std::endl ;
455 showShortHelp( e ) ;
456 }
457
showShortHelp(bool e) const458 void Main::CommandLineImp::showShortHelp( bool e ) const
459 {
460 Show show( m_output , e ) ;
461 const std::string & exe = m_arg.prefix() ;
462 show.s()
463 << std::string(exe.length()+2U,' ')
464 << "try \"" << exe << " --help --verbose\" for more information" << std::endl ;
465 }
466
showHelp(bool e) const467 void Main::CommandLineImp::showHelp( bool e ) const
468 {
469 Show show( m_output , e ) ;
470 showBanner( e ) ;
471 show.s() << std::endl ;
472 showUsage( e ) ;
473 showExtraHelp( e ) ;
474 showCopyright( e ) ;
475 }
476
showExtraHelp(bool e) const477 void Main::CommandLineImp::showExtraHelp( bool e ) const
478 {
479 Show show( m_output , e ) ;
480 const std::string & exe = m_arg.prefix() ;
481
482 show.s() << std::endl ;
483
484 if( m_getopt.contains("verbose") )
485 {
486 show.s()
487 << "To start a 'storage' daemon in background..." << std::endl
488 << " " << exe << " --as-server" << std::endl
489 << std::endl ;
490
491 show.s()
492 << "To forward stored mail to \"mail.myisp.net\"..." << std::endl
493 << " " << exe << " --as-client mail.myisp.net:smtp" << std::endl
494 << std::endl ;
495
496 show.s()
497 << "To run as a proxy (on port 10025) to a local server (on port 25)..." << std::endl
498 << " " << exe << " --port 10025 --as-proxy localhost:25" << std::endl
499 << std::endl ;
500 }
501 else
502 {
503 show.s()
504 << "For complete usage information run \"" << exe
505 << " --help --verbose\"" << std::endl
506 << std::endl ;
507 }
508 }
509
showNoop(bool e) const510 void Main::CommandLineImp::showNoop( bool e ) const
511 {
512 Show show( m_output , e ) ;
513 show.s() << m_arg.prefix() << ": no messages to send" << std::endl ;
514 }
515
showError(std::string reason,bool e) const516 void Main::CommandLineImp::showError( std::string reason , bool e ) const
517 {
518 Show show( m_output , e ) ;
519 show.s() << m_arg.prefix() << ": " << reason << std::endl ;
520 }
521
showBanner(bool e,const std::string & final) const522 void Main::CommandLineImp::showBanner( bool e , const std::string & final ) const
523 {
524 Show show( m_output , e ) ;
525 show.s()
526 << "E-MailRelay V" << m_version << std::endl << final ;
527 }
528
showCopyright(bool e,const std::string & final) const529 void Main::CommandLineImp::showCopyright( bool e , const std::string & final ) const
530 {
531 Show show( m_output , e ) ;
532 show.s() << Legal::copyright() << std::endl << final ;
533 }
534
showCapabilities(bool e,const std::string & final) const535 void Main::CommandLineImp::showCapabilities( bool e , const std::string & final ) const
536 {
537 if( !m_capabilities.empty() )
538 {
539 Show show( m_output , e ) ;
540 show.s() << "Build configuration [" << m_capabilities << "]" << std::endl << final ;
541 }
542 }
543
showWarranty(bool e,const std::string & final) const544 void Main::CommandLineImp::showWarranty( bool e , const std::string & final ) const
545 {
546 Show show( m_output , e ) ;
547 show.s() << Legal::warranty("","\n") << final ;
548 }
549
showCredit(bool e,const std::string & final) const550 void Main::CommandLineImp::showCredit( bool e , const std::string & final ) const
551 {
552 Show show( m_output , e ) ;
553 show.s() << GSsl::Library::credit("","\n",final) ;
554 }
555
showTestFeatures(bool e,const std::string & final) const556 void Main::CommandLineImp::showTestFeatures( bool e , const std::string & final ) const
557 {
558 Show show( m_output , e ) ;
559 show.s() << "Test features " << (G::Test::enabled()?"enabled":"disabled") << std::endl << final ;
560 }
561
showVersion(bool e) const562 void Main::CommandLineImp::showVersion( bool e ) const
563 {
564 Show show( m_output , e ) ;
565 showBanner( e , "\n" ) ;
566 showCopyright( e , "\n" ) ;
567 if( contains("verbose") )
568 {
569 showCapabilities( e , "\n" ) ;
570 showTestFeatures( e , "\n" ) ;
571 }
572 showCredit( e , "\n" ) ;
573 showWarranty( e ) ;
574 }
575
576 // ===
577
578 Main::Show * Main::Show::m_this = NULL ;
579
Show(Output & output,bool e)580 Main::Show::Show( Output & output , bool e ) :
581 m_output(output) ,
582 m_e(e)
583 {
584 if( m_this == NULL )
585 m_this = this ;
586 }
587
s()588 std::ostream & Main::Show::s()
589 {
590 return m_this->m_ss ;
591 }
592
~Show()593 Main::Show::~Show()
594 {
595 if( m_this == this )
596 {
597 m_this = NULL ;
598 m_output.output( m_ss.str() , m_e ) ;
599 }
600 }
601
602 // ==
603
switchSpec(bool is_windows)604 std::string Main::CommandLine::switchSpec( bool is_windows )
605 {
606 return CommandLineImp::switchSpec( is_windows ) ;
607 }
608
CommandLine(Main::Output & output,const G::Arg & arg,const std::string & spec,const std::string & version,const std::string & capabilities)609 Main::CommandLine::CommandLine( Main::Output & output , const G::Arg & arg , const std::string & spec ,
610 const std::string & version , const std::string & capabilities ) :
611 m_imp(new CommandLineImp(output,arg,spec,version,capabilities))
612 {
613 }
614
~CommandLine()615 Main::CommandLine::~CommandLine()
616 {
617 delete m_imp ;
618 }
619
cfg() const620 Main::Configuration Main::CommandLine::cfg() const
621 {
622 return Configuration( *this ) ;
623 }
624
contains(const std::string & switch_) const625 bool Main::CommandLine::contains( const std::string & switch_ ) const
626 {
627 return m_imp->contains( switch_ ) ;
628 }
629
value(const std::string & switch_) const630 std::string Main::CommandLine::value( const std::string & switch_ ) const
631 {
632 return m_imp->value( switch_ ) ;
633 }
634
argc() const635 G::Arg::size_type Main::CommandLine::argc() const
636 {
637 return m_imp->argc() ;
638 }
639
hasUsageErrors() const640 bool Main::CommandLine::hasUsageErrors() const
641 {
642 return m_imp->hasUsageErrors() ;
643 }
644
hasSemanticError() const645 bool Main::CommandLine::hasSemanticError() const
646 {
647 return m_imp->hasSemanticError( cfg() ) ;
648 }
649
showHelp(bool error_stream) const650 void Main::CommandLine::showHelp( bool error_stream ) const
651 {
652 m_imp->showHelp( error_stream ) ;
653 }
654
showUsageErrors(bool error_stream) const655 void Main::CommandLine::showUsageErrors( bool error_stream ) const
656 {
657 m_imp->showUsageErrors( error_stream ) ;
658 }
659
showSemanticError(bool error_stream) const660 void Main::CommandLine::showSemanticError( bool error_stream ) const
661 {
662 m_imp->showSemanticError( cfg() , error_stream ) ;
663 }
664
logSemanticWarnings() const665 void Main::CommandLine::logSemanticWarnings() const
666 {
667 m_imp->logSemanticWarnings( cfg() ) ;
668 }
669
showArgcError(bool error_stream) const670 void Main::CommandLine::showArgcError( bool error_stream ) const
671 {
672 m_imp->showArgcError( error_stream ) ;
673 }
674
showNoop(bool error_stream) const675 void Main::CommandLine::showNoop( bool error_stream ) const
676 {
677 m_imp->showNoop( error_stream ) ;
678 }
679
showError(const std::string & reason,bool error_stream) const680 void Main::CommandLine::showError( const std::string & reason , bool error_stream ) const
681 {
682 m_imp->showError( reason , error_stream ) ;
683 }
684
showVersion(bool error_stream) const685 void Main::CommandLine::showVersion( bool error_stream ) const
686 {
687 m_imp->showVersion( error_stream ) ;
688 }
689
showBanner(bool error_stream,const std::string & s) const690 void Main::CommandLine::showBanner( bool error_stream , const std::string & s ) const
691 {
692 m_imp->showBanner( error_stream , s ) ;
693 }
694
showCopyright(bool error_stream,const std::string & s) const695 void Main::CommandLine::showCopyright( bool error_stream , const std::string & s ) const
696 {
697 m_imp->showCopyright( error_stream , s ) ;
698 }
699
showCapabilities(bool error_stream,const std::string & s) const700 void Main::CommandLine::showCapabilities( bool error_stream , const std::string & s ) const
701 {
702 m_imp->showCapabilities( error_stream , s ) ;
703 }
704
value(const std::string & switch_,unsigned int default_) const705 unsigned int Main::CommandLine::value( const std::string & switch_ , unsigned int default_ ) const
706 {
707 return m_imp->contains(switch_) ? G::Str::toUInt(value(switch_)) : default_ ;
708 }
709
value(const std::string & switch_,const std::string & separators) const710 G::Strings Main::CommandLine::value( const std::string & switch_ , const std::string & separators ) const
711 {
712 G::Strings result ;
713 if( contains(switch_) )
714 {
715 G::Str::splitIntoFields( value(switch_) , result , separators ) ;
716 }
717 return result ;
718 }
719
contains(const char * switch_) const720 bool Main::CommandLine::contains( const char * switch_ ) const
721 {
722 return contains( std::string(switch_) ) ;
723 }
724
value(const char * switch_) const725 std::string Main::CommandLine::value( const char * switch_ ) const
726 {
727 return value( std::string(switch_) ) ;
728 }
729
value(const char * switch_,unsigned int default_) const730 unsigned int Main::CommandLine::value( const char * switch_ , unsigned int default_ ) const
731 {
732 return value( std::string(switch_) , default_ ) ;
733 }
734
value(const char * switch_,const char * separators) const735 G::Strings Main::CommandLine::value( const char * switch_ , const char * separators ) const
736 {
737 return value( std::string(switch_) , std::string(separators) ) ;
738 }
739
740