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