1 /* $Id: ncbiargs.cpp 634786 2021-07-19 18:53:56Z ivanov $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Denis Vakatov, Anton Butanayev
27 *
28 * File Description:
29 * Command-line arguments' processing:
30 * descriptions -- CArgDescriptions, CArgDesc
31 * parsed values -- CArgs, CArgValue
32 * exceptions -- CArgException, ARG_THROW()
33 * constraints -- CArgAllow; CArgAllow_{Strings,Integers,Int8s,Doubles}
34 *
35 */
36
37 #include <ncbi_pch.hpp>
38 #include <corelib/ncbiapp_api.hpp>
39 #include <corelib/ncbiargs.hpp>
40 #include <corelib/ncbienv.hpp>
41 #include <corelib/ncbifile.hpp>
42 #include <corelib/error_codes.hpp>
43 #include <algorithm>
44
45 #if defined(NCBI_OS_MSWIN)
46 # include <corelib/ncbi_os_mswin.hpp>
47 # include <io.h>
48 # include <fcntl.h>
49 # include <conio.h>
50 #else
51 # include <termios.h>
52 # include <unistd.h>
53 # include <fcntl.h>
54 #endif
55
56
57 #define NCBI_USE_ERRCODE_X Corelib_Config
58
59
60 BEGIN_NCBI_SCOPE
61
62
63 /////////////////////////////////////////////////////////////////////////////
64 // Include the private header
65 //
66
67 #define NCBIARGS__CPP
68 #include "ncbiargs_p.hpp"
69
70
71
72 /////////////////////////////////////////////////////////////////////////////
73 // Constants
74 //
75
76 static const char* s_AutoHelp = "h";
77 static const char* s_AutoHelpFull = "help";
78 static const char* s_AutoHelpXml = "xmlhelp";
79 static const char* s_ExtraName = "....";
80
81
82
83 /////////////////////////////////////////////////////////////////////////////
84
85 inline
s_ArgExptMsg(const string & name,const string & what,const string & attr)86 string s_ArgExptMsg(const string& name, const string& what, const string& attr)
87 {
88 return string("Argument \"") + (name.empty() ? s_ExtraName : name) +
89 "\". " + what + (attr.empty() ? attr : ": `" + attr + "'");
90 }
91
92 inline
s_WriteEscapedStr(CNcbiOstream & out,const char * s)93 void s_WriteEscapedStr(CNcbiOstream& out, const char* s)
94 {
95 out << NStr::XmlEncode(s);
96 }
97
s_WriteXmlLine(CNcbiOstream & out,const string & tag,const string & data)98 void s_WriteXmlLine(CNcbiOstream& out, const string& tag, const string& data)
99 {
100 CStringUTF8 u( CUtf8::AsUTF8(data,eEncoding_Unknown) );
101 out << "<" << tag << ">";
102 s_WriteEscapedStr(out, u.c_str());
103 out << "</" << tag << ">" << endl;
104 }
105
106 // Allow autodetection among decimal and hex, but NOT octal, in case
107 // anyone's been relying on leading zeros being meaningless.
108 inline
s_StringToInt8(const string & value)109 Int8 s_StringToInt8(const string& value)
110 {
111 try {
112 return NStr::StringToInt8(value);
113 } catch (const CStringException&) {
114 if (NStr::StartsWith(value, "0x", NStr::eNocase)) {
115 return NStr::StringToInt8(value, 0, 16);
116 } else {
117 throw;
118 }
119 }
120 }
121
122 /////////////////////////////////////////////////////////////////////////////
123 // CArg_***:: classes representing various types of argument value
124 //
125 // CArgValue
126 // CArg_NoValue : CArgValue
127 // CArg_String : CArgValue
128 // CArg_Int8 : CArg_String
129 // CArg_Integer : CArg_Int8
130 // CArg_IntId : CArg_IntId
131 // CArg_Double : CArg_String
132 // CArg_Boolean : CArg_String
133 // CArg_InputFile : CArg_String
134 // CArg_OutputFile : CArg_String
135 // CArg_IOFile : CArg_String
136 //
137
138
139 ///////////////////////////////////////////////////////
140 // CArgValue::
141
CArgValue(const string & name)142 CArgValue::CArgValue(const string& name)
143 : m_Name(name), m_Ordinal(0), m_Flags(0)
144 {
145 if ( !CArgDescriptions::VerifyName(m_Name, true) ) {
146 NCBI_THROW(CArgException,eInvalidArg,
147 "Invalid argument name: " + m_Name);
148 }
149 }
150
151
~CArgValue(void)152 CArgValue::~CArgValue(void)
153 {
154 return;
155 }
156
157
SetStringList()158 CArgValue::TStringArray& CArgValue::SetStringList()
159 {
160 NCBI_THROW(CArgException, eInvalidArg,
161 "Value lists not implemented for this argument: " + m_Name);
162 }
163
GetDefault(TArgValueFlags * has_default) const164 const string& CArgValue::GetDefault(TArgValueFlags* has_default) const
165 {
166 if (has_default) {
167 *has_default = m_Flags;
168 }
169 return m_Default;
170 }
171
x_SetDefault(const string & def_value,bool from_def)172 void CArgValue::x_SetDefault(const string& def_value, bool from_def)
173 {
174 m_Default = def_value;
175 m_Flags |= fArgValue_HasDefault;
176 if (from_def) {
177 m_Flags |= fArgValue_FromDefault;
178 }
179 }
180
181
182 ///////////////////////////////////////////////////////
183 // CArg_NoValue::
184
CArg_NoValue(const string & name)185 inline CArg_NoValue::CArg_NoValue(const string& name)
186 : CArgValue(name)
187 {
188 return;
189 }
190
191
HasValue(void) const192 bool CArg_NoValue::HasValue(void) const
193 {
194 return false;
195 }
196
197
198 #define THROW_CArg_NoValue \
199 NCBI_THROW(CArgException,eNoValue, s_ArgExptMsg(GetName(), \
200 "The argument has no value", ""));
201
GetStringList() const202 const CArgValue::TStringArray& CArgValue::GetStringList() const
203 {
204 THROW_CArg_NoValue;
205 }
AsString(void) const206 const string& CArg_NoValue::AsString (void) const { THROW_CArg_NoValue; }
AsInt8(void) const207 Int8 CArg_NoValue::AsInt8 (void) const { THROW_CArg_NoValue; }
AsInteger(void) const208 int CArg_NoValue::AsInteger (void) const { THROW_CArg_NoValue; }
AsIntId(void) const209 TIntId CArg_NoValue::AsIntId (void) const { THROW_CArg_NoValue; }
AsDouble(void) const210 double CArg_NoValue::AsDouble (void) const { THROW_CArg_NoValue; }
AsBoolean(void) const211 bool CArg_NoValue::AsBoolean (void) const { THROW_CArg_NoValue; }
AsDirectory(void) const212 const CDir& CArg_NoValue::AsDirectory (void) const { THROW_CArg_NoValue; }
AsDateTime(void) const213 const CTime& CArg_NoValue::AsDateTime (void) const { THROW_CArg_NoValue; }
AsInputFile(CArgValue::TFileFlags) const214 CNcbiIstream& CArg_NoValue::AsInputFile (CArgValue::TFileFlags) const { THROW_CArg_NoValue; }
AsOutputFile(CArgValue::TFileFlags) const215 CNcbiOstream& CArg_NoValue::AsOutputFile(CArgValue::TFileFlags) const { THROW_CArg_NoValue; }
AsIOFile(CArgValue::TFileFlags) const216 CNcbiIostream& CArg_NoValue::AsIOFile(CArgValue::TFileFlags) const { THROW_CArg_NoValue; }
CloseFile(void) const217 void CArg_NoValue::CloseFile (void) const { THROW_CArg_NoValue; }
218
219
220 ///////////////////////////////////////////////////////
221 // CArg_ExcludedValue::
222
CArg_ExcludedValue(const string & name)223 inline CArg_ExcludedValue::CArg_ExcludedValue(const string& name)
224 : CArgValue(name)
225 {
226 return;
227 }
228
229
HasValue(void) const230 bool CArg_ExcludedValue::HasValue(void) const
231 {
232 return false;
233 }
234
235
236 #define THROW_CArg_ExcludedValue \
237 NCBI_THROW(CArgException,eExcludedValue, s_ArgExptMsg(GetName(), \
238 "The value is excluded by other arguments.", ""));
239
AsString(void) const240 const string& CArg_ExcludedValue::AsString (void) const { THROW_CArg_ExcludedValue; }
AsInt8(void) const241 Int8 CArg_ExcludedValue::AsInt8 (void) const { THROW_CArg_ExcludedValue; }
AsInteger(void) const242 int CArg_ExcludedValue::AsInteger (void) const { THROW_CArg_ExcludedValue; }
AsIntId(void) const243 TIntId CArg_ExcludedValue::AsIntId (void) const { THROW_CArg_ExcludedValue; }
AsDouble(void) const244 double CArg_ExcludedValue::AsDouble (void) const { THROW_CArg_ExcludedValue; }
AsBoolean(void) const245 bool CArg_ExcludedValue::AsBoolean (void) const { THROW_CArg_ExcludedValue; }
AsDirectory(void) const246 const CDir& CArg_ExcludedValue::AsDirectory (void) const { THROW_CArg_ExcludedValue; }
AsDateTime(void) const247 const CTime& CArg_ExcludedValue::AsDateTime (void) const { THROW_CArg_ExcludedValue; }
AsInputFile(CArgValue::TFileFlags) const248 CNcbiIstream& CArg_ExcludedValue::AsInputFile (CArgValue::TFileFlags) const { THROW_CArg_ExcludedValue; }
AsOutputFile(CArgValue::TFileFlags) const249 CNcbiOstream& CArg_ExcludedValue::AsOutputFile(CArgValue::TFileFlags) const { THROW_CArg_ExcludedValue; }
AsIOFile(CArgValue::TFileFlags) const250 CNcbiIostream& CArg_ExcludedValue::AsIOFile(CArgValue::TFileFlags) const { THROW_CArg_ExcludedValue; }
CloseFile(void) const251 void CArg_ExcludedValue::CloseFile (void) const { THROW_CArg_ExcludedValue; }
252
253
254 ///////////////////////////////////////////////////////
255 // CArg_String::
256
CArg_String(const string & name,const string & value)257 inline CArg_String::CArg_String(const string& name, const string& value)
258 : CArgValue(name)
259 {
260 m_StringList.push_back(value);
261 }
262
263
HasValue(void) const264 bool CArg_String::HasValue(void) const
265 {
266 return !m_StringList.empty();
267 }
268
269
AsString(void) const270 const string& CArg_String::AsString(void) const
271 {
272 if (m_StringList.empty()) {
273 return kEmptyStr;
274 }
275 return m_StringList[0];
276 }
277
278
GetStringList() const279 const CArgValue::TStringArray& CArg_String::GetStringList() const
280 {
281 return m_StringList;
282 }
283
284
SetStringList()285 CArgValue::TStringArray& CArg_String::SetStringList()
286 {
287 return m_StringList;
288 }
289
290
AsInt8(void) const291 Int8 CArg_String::AsInt8(void) const
292 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
293 "Attempt to cast to a wrong (Int8) type", AsString()));}
294
AsInteger(void) const295 int CArg_String::AsInteger(void) const
296 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
297 "Attempt to cast to a wrong (Integer) type", AsString()));}
298
AsIntId(void) const299 TIntId CArg_String::AsIntId(void) const
300 {
301 NCBI_THROW(CArgException, eWrongCast, s_ArgExptMsg(GetName(),
302 "Attempt to cast to a wrong (TIntId) type", AsString()));
303 }
304
AsDouble(void) const305 double CArg_String::AsDouble(void) const
306 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
307 "Attempt to cast to a wrong (Double) type", AsString()));}
308
AsBoolean(void) const309 bool CArg_String::AsBoolean(void) const
310 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
311 "Attempt to cast to a wrong (Boolean) type", AsString()));}
312
AsDirectory(void) const313 const CDir& CArg_String::AsDirectory (void) const
314 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
315 "Attempt to cast to a wrong (CDir) type", AsString()));}
316
AsDateTime(void) const317 const CTime& CArg_String::AsDateTime (void) const
318 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
319 "Attempt to cast to a wrong (CTime) type", AsString()));}
320
AsInputFile(CArgValue::TFileFlags) const321 CNcbiIstream& CArg_String::AsInputFile(CArgValue::TFileFlags) const
322 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
323 "Attempt to cast to a wrong (InputFile) type", AsString()));}
324
AsOutputFile(CArgValue::TFileFlags) const325 CNcbiOstream& CArg_String::AsOutputFile(CArgValue::TFileFlags) const
326 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
327 "Attempt to cast to a wrong (OutputFile) type", AsString()));}
328
AsIOFile(CArgValue::TFileFlags) const329 CNcbiIostream& CArg_String::AsIOFile(CArgValue::TFileFlags) const
330 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
331 "Attempt to cast to a wrong (IOFile) type", AsString()));}
332
CloseFile(void) const333 void CArg_String::CloseFile(void) const
334 { NCBI_THROW(CArgException,eWrongCast,s_ArgExptMsg(GetName(),
335 "Attempt to close an argument of non-file type", AsString()));}
336
337
338
339 ///////////////////////////////////////////////////////
340 // CArg_Int8::
341
CArg_Int8(const string & name,const string & value)342 inline CArg_Int8::CArg_Int8(const string& name, const string& value)
343 : CArg_String(name, value)
344 {
345 try {
346 m_Integer = s_StringToInt8(value);
347 } catch (const CException& e) {
348 NCBI_RETHROW(e, CArgException, eConvert, s_ArgExptMsg(GetName(),
349 "Argument cannot be converted", value));
350 }
351 }
352
353
AsInt8(void) const354 Int8 CArg_Int8::AsInt8(void) const
355 {
356 return m_Integer;
357 }
358
359
AsIntId(void) const360 TIntId CArg_Int8::AsIntId(void) const
361 {
362 #ifdef NCBI_INT8_GI
363 return m_Integer;
364 #else
365 NCBI_THROW(CArgException, eWrongCast, s_ArgExptMsg(GetName(),
366 "Attempt to cast to a wrong (TIntId) type", AsString()));
367 #endif
368 }
369
370
371 ///////////////////////////////////////////////////////
372 // CArg_Integer::
373
CArg_Integer(const string & name,const string & value)374 inline CArg_Integer::CArg_Integer(const string& name, const string& value)
375 : CArg_Int8(name, value)
376 {
377 if (m_Integer < kMin_Int || m_Integer > kMax_Int) {
378 NCBI_THROW(CArgException, eConvert, s_ArgExptMsg(GetName(),
379 "Integer value is out of range", value));
380 }
381 }
382
383
AsInteger(void) const384 int CArg_Integer::AsInteger(void) const
385 {
386 return static_cast<int> (m_Integer);
387 }
388
389
AsIntId(void) const390 TIntId CArg_Integer::AsIntId(void) const
391 {
392 return AsInteger();
393 }
394
395
396 ///////////////////////////////////////////////////////
397 // CArg_IntId::
398
CArg_IntId(const string & name,const string & value)399 inline CArg_IntId::CArg_IntId(const string& name, const string& value)
400 : CArg_Int8(name, value)
401 {
402 #ifndef NCBI_INT8_GI
403 if (m_Integer < kMin_Int || m_Integer > kMax_Int) {
404 NCBI_THROW(CArgException, eConvert, s_ArgExptMsg(GetName(),
405 "IntId value is out of range", value));
406 }
407 #endif
408 }
409
410
AsInteger(void) const411 int CArg_IntId::AsInteger(void) const
412 {
413 #ifndef NCBI_INT8_GI
414 return static_cast<int> (m_Integer);
415 #else
416 NCBI_THROW(CArgException, eWrongCast, s_ArgExptMsg(GetName(),
417 "Attempt to cast to a wrong (Integer) type", AsString()));
418 #endif
419 }
420
421
AsIntId(void) const422 TIntId CArg_IntId::AsIntId(void) const
423 {
424 return static_cast<TIntId> (m_Integer);
425 }
426
427
428 ///////////////////////////////////////////////////////
429 // CArg_DataSize::
430
CArg_DataSize(const string & name,const string & value)431 inline CArg_DataSize::CArg_DataSize(const string& name, const string& value)
432 : CArg_String(name, value)
433 {
434 try {
435 m_Integer = NStr::StringToUInt8_DataSize(value);
436 } catch (const CException& e) {
437 NCBI_RETHROW(e, CArgException, eConvert, s_ArgExptMsg(GetName(),
438 "Argument cannot be converted", value));
439 }
440 }
441
AsInt8(void) const442 Int8 CArg_DataSize::AsInt8(void) const
443 {
444 return m_Integer;
445 }
446
447
448 ///////////////////////////////////////////////////////
449 // CArg_Double::
450
CArg_Double(const string & name,const string & value)451 inline CArg_Double::CArg_Double(const string& name, const string& value)
452 : CArg_String(name, value)
453 {
454 try {
455 m_Double = NStr::StringToDouble(value, NStr::fDecimalPosixOrLocal);
456 } catch (const CException& e) {
457 NCBI_RETHROW(e,CArgException,eConvert,
458 s_ArgExptMsg(GetName(),"Argument cannot be converted",value));
459 }
460 }
461
462
AsDouble(void) const463 double CArg_Double::AsDouble(void) const
464 {
465 return m_Double;
466 }
467
468
469
470 ///////////////////////////////////////////////////////
471 // CArg_Boolean::
472
CArg_Boolean(const string & name,bool value)473 inline CArg_Boolean::CArg_Boolean(const string& name, bool value)
474 : CArg_String(name, NStr::BoolToString(value))
475 {
476 m_Boolean = value;
477 }
478
479
CArg_Boolean(const string & name,const string & value)480 inline CArg_Boolean::CArg_Boolean(const string& name, const string& value)
481 : CArg_String(name, value)
482 {
483 try {
484 m_Boolean = NStr::StringToBool(value);
485 } catch (const CException& e) {
486 NCBI_RETHROW(e,CArgException,eConvert, s_ArgExptMsg(GetName(),
487 "Argument cannot be converted",value));
488 }
489 }
490
AsBoolean(void) const491 bool CArg_Boolean::AsBoolean(void) const
492 {
493 return m_Boolean;
494 }
495
496
497 ///////////////////////////////////////////////////////
498 // CArg_Flag
499
CArg_Flag(const string & name,bool value)500 CArg_Flag::CArg_Flag(const string& name, bool value)
501 : CArg_Boolean(name, value)
502 {
503 }
HasValue(void) const504 bool CArg_Flag::HasValue(void) const
505 {
506 return AsBoolean();
507 }
508
509
510 ///////////////////////////////////////////////////////
511 // CArg_Dir::
512
CArg_Dir(const string & name,const string & value,CArgDescriptions::TFlags flags)513 CArg_Dir::CArg_Dir(const string& name, const string& value,
514 CArgDescriptions::TFlags flags)
515 : CArg_String(name, value), m_Dir(value), m_DescriptionFlags(flags)
516 {
517 }
518
~CArg_Dir(void)519 CArg_Dir::~CArg_Dir(void)
520 {
521 }
522
AsDirectory() const523 const CDir& CArg_Dir::AsDirectory() const
524 {
525 if (m_DescriptionFlags & CArgDescriptions::fCreatePath) {
526 m_Dir.CreatePath();
527 }
528 return m_Dir;
529 }
530
531 ///////////////////////////////////////////////////////
532 // CArg_DateTime::
533
CArg_DateTime(const string & name,const string & value)534 CArg_DateTime::CArg_DateTime(const string& name, const string& value)
535 : CArg_String(name, value)
536 {
537 bool hasZ = value.size() != 0 && value[value.size()-1] == 'Z';
538 const char* fmt[] = {
539 "M/D/Y h:m:s", // CTime default
540 "Y-M-DTh:m:g", // ISO8601
541 "Y/M/D h:m:g", //
542 "Y-M-D h:m:g", // NCBI SQL server default
543 nullptr
544 };
545 bool res = false;
546 for (int i = 0; !res && fmt[i]; ++i) {
547 try {
548 new (&m_DateTime) CTime(
549 value,
550 CTimeFormat( fmt[i], CTimeFormat::fMatch_Weak | CTimeFormat::fFormat_Simple),
551 hasZ ? CTime::eGmt : CTime::eLocal);
552 res = true;
553 } catch (...) {
554 }
555 }
556 if (!res) {
557 NCBI_THROW(CArgException,eConvert, s_ArgExptMsg(GetName(),
558 "Argument cannot be converted",value));
559 }
560 }
561
AsDateTime() const562 const CTime& CArg_DateTime::AsDateTime() const
563 {
564 return m_DateTime;
565 }
566
567
568 ///////////////////////////////////////////////////////
569 // CArg_Ios::
570
IosMode(CArgValue::TFileFlags flags)571 IOS_BASE::openmode CArg_Ios::IosMode(CArgValue::TFileFlags flags)
572 {
573 IOS_BASE::openmode openmode = (IOS_BASE::openmode) 0;
574 if (flags & CArgValue::fBinary) {
575 openmode |= IOS_BASE::binary;
576 }
577 if (flags & CArgValue::fAppend) {
578 openmode |= IOS_BASE::app;
579 }
580 if (flags & CArgValue::fTruncate) {
581 openmode |= IOS_BASE::trunc;
582 }
583 return openmode;
584 }
585
CArg_Ios(const string & name,const string & value,CArgDescriptions::TFlags flags)586 CArg_Ios::CArg_Ios(
587 const string& name, const string& value,
588 CArgDescriptions::TFlags flags)
589 : CArg_String(name, value),
590 m_DescriptionFlags(0),
591 m_CurrentFlags(0),
592 m_Ios(NULL),
593 m_DeleteFlag(true)
594 {
595 m_DescriptionFlags = (CArgValue::TFileFlags)(
596 (flags & CArgDescriptions::fFileFlags) & ~CArgDescriptions::fPreOpen);
597 }
598
599
~CArg_Ios(void)600 CArg_Ios::~CArg_Ios(void)
601 {
602 if (m_Ios && m_DeleteFlag)
603 delete m_Ios;
604 }
605
x_Open(CArgValue::TFileFlags) const606 void CArg_Ios::x_Open(CArgValue::TFileFlags /*flags*/) const
607 {
608 if ( !m_Ios ) {
609 NCBI_THROW(CArgException,eNoFile, s_ArgExptMsg(GetName(),
610 "File is not accessible",AsString()));
611 }
612 }
613
x_CreatePath(TFileFlags flags) const614 bool CArg_Ios::x_CreatePath(TFileFlags flags) const
615 {
616 CDirEntry entry( AsString() );
617 if (flags & CArgDescriptions::fCreatePath) {
618 CDir( entry.GetDir() ).CreatePath();;
619 }
620 return ((flags & CArgDescriptions::fNoCreate)==0 || entry.Exists());
621 }
622
AsInputFile(TFileFlags flags) const623 CNcbiIstream& CArg_Ios::AsInputFile( TFileFlags flags) const
624 {
625 CFastMutexGuard LOCK(m_AccessMutex);
626 x_Open(flags);
627 CNcbiIstream *str = dynamic_cast<CNcbiIstream*>(m_Ios);
628 if (str) {
629 return *str;
630 }
631 return CArg_String::AsInputFile(flags);
632 }
633
AsOutputFile(TFileFlags flags) const634 CNcbiOstream& CArg_Ios::AsOutputFile( TFileFlags flags) const
635 {
636 CFastMutexGuard LOCK(m_AccessMutex);
637 x_Open(flags);
638 CNcbiOstream *str = dynamic_cast<CNcbiOstream*>(m_Ios);
639 if (str) {
640 return *str;
641 }
642 return CArg_String::AsOutputFile(flags);
643 }
644
AsIOFile(TFileFlags flags) const645 CNcbiIostream& CArg_Ios::AsIOFile( TFileFlags flags) const
646 {
647 CFastMutexGuard LOCK(m_AccessMutex);
648 x_Open(flags);
649 CNcbiIostream *str = dynamic_cast<CNcbiIostream*>(m_Ios);
650 if (str) {
651 return *str;
652 }
653 return CArg_String::AsIOFile(flags);
654 }
655
656
CloseFile(void) const657 void CArg_Ios::CloseFile(void) const
658 {
659 CFastMutexGuard LOCK(m_AccessMutex);
660 if ( !m_Ios ) {
661 ERR_POST_X(21, Warning << s_ArgExptMsg( GetName(),
662 "CArg_Ios::CloseFile: File was not opened", AsString()));
663 return;
664 }
665
666 if ( m_DeleteFlag ) {
667 delete m_Ios;
668 m_Ios = NULL;
669 }
670 }
671
672
673
674 ///////////////////////////////////////////////////////
675 // CArg_InputFile::
676
CArg_InputFile(const string & name,const string & value,CArgDescriptions::TFlags flags)677 CArg_InputFile::CArg_InputFile(const string& name, const string& value,
678 CArgDescriptions::TFlags flags)
679 : CArg_Ios(name, value, flags)
680 {
681 if ( flags & CArgDescriptions::fPreOpen ) {
682 x_Open(m_DescriptionFlags);
683 }
684 }
685
x_Open(CArgValue::TFileFlags flags) const686 void CArg_InputFile::x_Open(CArgValue::TFileFlags flags) const
687 {
688 CNcbiIfstream *fstrm = NULL;
689 if ( m_Ios ) {
690 if (flags != m_CurrentFlags && flags != 0) {
691 if (m_DeleteFlag) {
692 fstrm = dynamic_cast<CNcbiIfstream*>(m_Ios);
693 _ASSERT(fstrm);
694 fstrm->close();
695 } else {
696 m_Ios = NULL;
697 }
698 }
699 }
700 if ( m_Ios != NULL && fstrm == NULL) {
701 return;
702 }
703
704 m_CurrentFlags = flags ? flags : m_DescriptionFlags;
705 IOS_BASE::openmode mode = CArg_Ios::IosMode( m_CurrentFlags);
706 m_DeleteFlag = false;
707
708 if (AsString() == "-") {
709 #if defined(NCBI_OS_MSWIN)
710 NcbiSys_setmode(NcbiSys_fileno(stdin), (mode & IOS_BASE::binary) ? O_BINARY : O_TEXT);
711 #endif
712 m_Ios = &cin;
713 } else if ( !AsString().empty() ) {
714 if (!fstrm) {
715 fstrm = new CNcbiIfstream;
716 }
717 if (fstrm) {
718 fstrm->open(AsString().c_str(),IOS_BASE::in | mode);
719 if ( !fstrm->is_open() ) {
720 delete fstrm;
721 fstrm = NULL;
722 } else {
723 m_DeleteFlag = true;
724 }
725 }
726 m_Ios = fstrm;
727 }
728 CArg_Ios::x_Open(flags);
729 }
730
731
732 ///////////////////////////////////////////////////////
733 // CArg_OutputFile::
734
CArg_OutputFile(const string & name,const string & value,CArgDescriptions::TFlags flags)735 CArg_OutputFile::CArg_OutputFile(
736 const string& name, const string& value,
737 CArgDescriptions::TFlags flags)
738 : CArg_Ios(name, value, flags)
739 {
740 if ( flags & CArgDescriptions::fPreOpen ) {
741 x_Open(m_DescriptionFlags);
742 }
743 }
744
x_Open(CArgValue::TFileFlags flags) const745 void CArg_OutputFile::x_Open(CArgValue::TFileFlags flags) const
746 {
747 CNcbiOfstream *fstrm = NULL;
748 if ( m_Ios ) {
749 if ((flags != m_CurrentFlags && flags != 0) ||
750 (flags & fTruncate)) {
751 if (m_DeleteFlag) {
752 fstrm = dynamic_cast<CNcbiOfstream*>(m_Ios);
753 _ASSERT(fstrm);
754 fstrm->close();
755 } else {
756 m_Ios = NULL;
757 }
758 }
759 }
760 if ( m_Ios != NULL && fstrm == NULL) {
761 return;
762 }
763
764 m_CurrentFlags = flags ? flags : m_DescriptionFlags;
765 IOS_BASE::openmode mode = CArg_Ios::IosMode( m_CurrentFlags);
766 m_DeleteFlag = false;
767
768 if (AsString() == "-") {
769 #if defined(NCBI_OS_MSWIN)
770 NcbiSys_setmode(NcbiSys_fileno(stdout), (mode & IOS_BASE::binary) ? O_BINARY : O_TEXT);
771 #endif
772 m_Ios = &cout;
773 } else if ( !AsString().empty() ) {
774 if (!fstrm) {
775 fstrm = new CNcbiOfstream;
776 }
777 if (fstrm) {
778 if (x_CreatePath(m_CurrentFlags)) {
779 fstrm->open(AsString().c_str(),IOS_BASE::out | mode);
780 }
781 if ( !fstrm->is_open() ) {
782 delete fstrm;
783 fstrm = NULL;
784 } else {
785 m_DeleteFlag = true;
786 }
787 }
788 m_Ios = fstrm;
789 }
790 CArg_Ios::x_Open(flags);
791 }
792
793
794
795 ///////////////////////////////////////////////////////
796 // CArg_IOFile::
797
CArg_IOFile(const string & name,const string & value,CArgDescriptions::TFlags flags)798 CArg_IOFile::CArg_IOFile(
799 const string& name, const string& value,
800 CArgDescriptions::TFlags flags)
801 : CArg_Ios(name, value, flags)
802 {
803 if ( flags & CArgDescriptions::fPreOpen ) {
804 x_Open(m_DescriptionFlags);
805 }
806 }
807
x_Open(CArgValue::TFileFlags flags) const808 void CArg_IOFile::x_Open(CArgValue::TFileFlags flags) const
809 {
810 CNcbiFstream *fstrm = NULL;
811 if ( m_Ios ) {
812 if ((flags != m_CurrentFlags && flags != 0) ||
813 (flags & fTruncate)) {
814 if (m_DeleteFlag) {
815 fstrm = dynamic_cast<CNcbiFstream*>(m_Ios);
816 _ASSERT(fstrm);
817 fstrm->close();
818 } else {
819 m_Ios = NULL;
820 }
821 }
822 }
823 if ( m_Ios != NULL && fstrm == NULL) {
824 return;
825 }
826
827 m_CurrentFlags = flags ? flags : m_DescriptionFlags;
828 IOS_BASE::openmode mode = CArg_Ios::IosMode( m_CurrentFlags);
829 m_DeleteFlag = false;
830
831 if ( !AsString().empty() ) {
832 if (!fstrm) {
833 fstrm = new CNcbiFstream;
834 }
835 if (fstrm) {
836 if (x_CreatePath(m_CurrentFlags)) {
837 fstrm->open(AsString().c_str(),IOS_BASE::in | IOS_BASE::out | mode);
838 }
839 if ( !fstrm->is_open() ) {
840 delete fstrm;
841 fstrm = NULL;
842 } else {
843 m_DeleteFlag = true;
844 }
845 }
846 m_Ios = fstrm;
847 }
848 CArg_Ios::x_Open(flags);
849 }
850
851
852 /////////////////////////////////////////////////////////////////////////////
853 // Aux.functions to figure out various arg. features
854 //
855 // s_IsPositional(arg)
856 // s_IsOptional(arg)
857 // s_IsFlag(arg)
858 //
859
860 class CArgDesc;
861
s_IsKey(const CArgDesc & arg)862 inline bool s_IsKey(const CArgDesc& arg)
863 {
864 return (dynamic_cast<const CArgDescSynopsis*> (&arg) != 0);
865 }
866
867
s_IsPositional(const CArgDesc & arg)868 inline bool s_IsPositional(const CArgDesc& arg)
869 {
870 return dynamic_cast<const CArgDesc_Pos*> (&arg) && !s_IsKey(arg);
871 }
872
873
s_IsOpening(const CArgDesc & arg)874 inline bool s_IsOpening(const CArgDesc& arg)
875 {
876 return dynamic_cast<const CArgDesc_Opening*> (&arg) != NULL;
877 }
878
879
s_IsOptional(const CArgDesc & arg)880 inline bool s_IsOptional(const CArgDesc& arg)
881 {
882 return (dynamic_cast<const CArgDescOptional*> (&arg) != 0);
883 }
884
885
s_IsFlag(const CArgDesc & arg)886 inline bool s_IsFlag(const CArgDesc& arg)
887 {
888 return (dynamic_cast<const CArgDesc_Flag*> (&arg) != 0);
889 }
890
891
s_IsAlias(const CArgDesc & arg)892 inline bool s_IsAlias(const CArgDesc& arg)
893 {
894 return (dynamic_cast<const CArgDesc_Alias*> (&arg) != 0);
895 }
896
897
898 /////////////////////////////////////////////////////////////////////////////
899 // CArgDesc***:: abstract base classes for argument descriptions
900 //
901 // CArgDesc
902 //
903 // CArgDescMandatory : CArgDesc
904 // CArgDescOptional : virtual CArgDescMandatory
905 // CArgDescDefault : virtual CArgDescOptional
906 //
907 // CArgDescSynopsis
908 //
909
910
911 ///////////////////////////////////////////////////////
912 // CArgDesc::
913
CArgDesc(const string & name,const string & comment,CArgDescriptions::TFlags flags)914 CArgDesc::CArgDesc(const string& name, const string& comment, CArgDescriptions::TFlags flags)
915 : m_Name(name), m_Comment(comment), m_Flags(flags)
916 {
917 if ( !CArgDescriptions::VerifyName(m_Name) ) {
918 NCBI_THROW(CArgException,eInvalidArg,
919 "Invalid argument name: " + m_Name);
920 }
921 }
922
923
~CArgDesc(void)924 CArgDesc::~CArgDesc(void)
925 {
926 return;
927 }
928
929
VerifyDefault(void) const930 void CArgDesc::VerifyDefault(void) const
931 {
932 return;
933 }
934
935
SetConstraint(const CArgAllow * constraint,CArgDescriptions::EConstraintNegate)936 void CArgDesc::SetConstraint(const CArgAllow* constraint,
937 CArgDescriptions::EConstraintNegate)
938 {
939 CConstRef<CArgAllow> safe_delete(constraint);
940
941 NCBI_THROW(CArgException, eConstraint,
942 s_ArgExptMsg(GetName(),
943 "No-value arguments may not be constrained",
944 constraint ? constraint->GetUsage() : kEmptyStr));
945 }
946
947
GetConstraint(void) const948 const CArgAllow* CArgDesc::GetConstraint(void) const
949 {
950 return 0;
951 }
952
953
GetUsageConstraint(void) const954 string CArgDesc::GetUsageConstraint(void) const
955 {
956 if (GetFlags() & CArgDescriptions::fConfidential) {
957 return kEmptyStr;
958 }
959 const CArgAllow* constraint = GetConstraint();
960 if (!constraint)
961 return kEmptyStr;
962 string usage;
963 if (IsConstraintInverted()) {
964 usage = " NOT ";
965 }
966 usage += constraint->GetUsage();
967 return usage;
968 }
969
970
971 // Overload the comparison operator -- to handle "AutoPtr<CArgDesc>" elements
972 // in "CArgs::m_Args" stored as "set< AutoPtr<CArgDesc> >"
973 //
operator <(const AutoPtr<CArgDesc> & x,const AutoPtr<CArgDesc> & y)974 inline bool operator< (const AutoPtr<CArgDesc>& x, const AutoPtr<CArgDesc>& y)
975 {
976 return x->GetName() < y->GetName();
977 }
978
PrintXml(CNcbiOstream & out) const979 string CArgDesc::PrintXml(CNcbiOstream& out) const
980 // note: I open 'role' tag, but do not close it here
981 {
982 string role;
983 if (s_IsKey(*this)) {
984 role = "key";
985 } else if (s_IsOpening(*this)) {
986 role = "opening";
987 } else if (s_IsPositional(*this)) {
988 role = GetName().empty() ? "extra" : "positional";
989 } else if (s_IsFlag(*this)) {
990 role = "flag";
991 } else {
992 role = "UNKNOWN";
993 }
994
995 // name
996 out << "<" << role << " name=\"";
997 string name = CUtf8::AsUTF8(GetName(),eEncoding_Unknown);
998 s_WriteEscapedStr(out,name.c_str());
999 out << "\"";
1000 // type
1001 const CArgDescMandatory* am =
1002 dynamic_cast<const CArgDescMandatory*>(this);
1003 if (am) {
1004 out << " type=\"" << CArgDescriptions::GetTypeName(am->GetType()) << "\"";
1005 }
1006 // use (Flags are always optional)
1007 if (s_IsOptional(*this) || s_IsFlag(*this)) {
1008 out << " optional=\"true\"";
1009 }
1010 out << ">" << endl;
1011
1012 s_WriteXmlLine(out, "description", GetComment());
1013 size_t group = GetGroup();
1014 if (group != 0) {
1015 s_WriteXmlLine(out, "group", NStr::SizetToString(group));
1016 }
1017 const CArgDescSynopsis* syn =
1018 dynamic_cast<const CArgDescSynopsis*>(this);
1019 if (syn && !syn->GetSynopsis().empty()) {
1020 s_WriteXmlLine(out, "synopsis", syn->GetSynopsis());
1021 }
1022
1023 // constraint
1024 string constraint = CUtf8::AsUTF8(GetUsageConstraint(),eEncoding_Unknown);
1025 if (!constraint.empty()) {
1026 out << "<" << "constraint";
1027 if (IsConstraintInverted()) {
1028 out << " inverted=\"true\"";
1029 }
1030 out << ">" << endl;
1031 s_WriteXmlLine( out, "description", constraint.c_str());
1032 GetConstraint()->PrintUsageXml(out);
1033 out << "</" << "constraint" << ">" << endl;
1034 }
1035
1036 // flags
1037 CArgDescriptions::TFlags flags = GetFlags();
1038 if (flags != 0) {
1039 out << "<" << "flags" << ">";
1040 if (flags & CArgDescriptions::fPreOpen) {
1041 out << "<" << "preOpen" << "/>";
1042 }
1043 if (flags & CArgDescriptions::fBinary) {
1044 out << "<" << "binary" << "/>";
1045 }
1046 if (flags & CArgDescriptions::fAppend) {
1047 out << "<" << "append" << "/>";
1048 }
1049 if (flags & CArgDescriptions::fTruncate) {
1050 out << "<" << "truncate" << "/>";
1051 }
1052 if (flags & CArgDescriptions::fNoCreate) {
1053 out << "<" << "noCreate" << "/>";
1054 }
1055 if (flags & CArgDescriptions::fAllowMultiple) {
1056 out << "<" << "allowMultiple" << "/>";
1057 }
1058 if (flags & CArgDescriptions::fIgnoreInvalidValue) {
1059 out << "<" << "ignoreInvalidValue" << "/>";
1060 }
1061 if (flags & CArgDescriptions::fWarnOnInvalidValue) {
1062 out << "<" << "warnOnInvalidValue" << "/>";
1063 }
1064 if (flags & CArgDescriptions::fOptionalSeparator) {
1065 out << "<" << "optionalSeparator" << "/>";
1066 }
1067 if (flags & CArgDescriptions::fMandatorySeparator) {
1068 out << "<" << "mandatorySeparator" << "/>";
1069 }
1070 if (flags & CArgDescriptions::fCreatePath) {
1071 out << "<" << "createPath" << "/>";
1072 }
1073 if (flags & CArgDescriptions::fOptionalSeparatorAllowConflict) {
1074 out << "<" << "optionalSeparatorAllowConflict" << "/>";
1075 }
1076 if (flags & CArgDescriptions::fHidden) {
1077 out << "<" << "hidden" << "/>";
1078 }
1079 if (flags & CArgDescriptions::fConfidential) {
1080 out << "<" << "confidential" << "/>";
1081 }
1082 out << "</" << "flags" << ">" << endl;
1083 }
1084 const CArgDescDefault* def = dynamic_cast<const CArgDescDefault*>(this);
1085 if (def) {
1086 s_WriteXmlLine( out, "default", def->GetDisplayValue());
1087 } else if (s_IsFlag(*this)) {
1088 const CArgDesc_Flag* fl = dynamic_cast<const CArgDesc_Flag*>(this);
1089 if (fl && !fl->GetSetValue()) {
1090 s_WriteXmlLine( out, "setvalue", "false");
1091 }
1092 }
1093 return role;
1094 }
1095
1096
1097 ///////////////////////////////////////////////////////
1098 // CArgDescMandatory::
1099
CArgDescMandatory(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags)1100 CArgDescMandatory::CArgDescMandatory(const string& name,
1101 const string& comment,
1102 CArgDescriptions::EType type,
1103 CArgDescriptions::TFlags flags)
1104 : CArgDesc(name, comment, flags),
1105 m_Type(type),
1106 m_NegateConstraint(CArgDescriptions::eConstraint)
1107 {
1108 // verify if "flags" "type" are matching
1109 switch ( type ) {
1110 case CArgDescriptions::eBoolean:
1111 case CArgDescriptions::eOutputFile:
1112 case CArgDescriptions::eIOFile:
1113 return;
1114 case CArgDescriptions::eInputFile:
1115 if((flags &
1116 (CArgDescriptions::fAllowMultiple | CArgDescriptions::fAppend | CArgDescriptions::fTruncate)) == 0)
1117 return;
1118 break;
1119 case CArgDescriptions::k_EType_Size:
1120 _TROUBLE;
1121 NCBI_THROW(CArgException, eArgType, s_ArgExptMsg(GetName(),
1122 "Invalid argument type", "k_EType_Size"));
1123 /*NOTREACHED*/
1124 break;
1125 case CArgDescriptions::eDirectory:
1126 if ( (flags & ~CArgDescriptions::fCreatePath) == 0 )
1127 return;
1128 break;
1129 default:
1130 if ( (flags & CArgDescriptions::fFileFlags) == 0 )
1131 return;
1132 }
1133
1134 NCBI_THROW(CArgException, eArgType,
1135 s_ArgExptMsg(GetName(),
1136 "Argument type/flags mismatch",
1137 string("(type=") +
1138 CArgDescriptions::GetTypeName(type) +
1139 ", flags=" + NStr::UIntToString(flags) + ")"));
1140 }
1141
1142
~CArgDescMandatory(void)1143 CArgDescMandatory::~CArgDescMandatory(void)
1144 {
1145 return;
1146 }
1147
1148
GetUsageCommentAttr(void) const1149 string CArgDescMandatory::GetUsageCommentAttr(void) const
1150 {
1151 CArgDescriptions::EType type = GetType();
1152 // Print type name
1153 string str = CArgDescriptions::GetTypeName(type);
1154
1155 if (type == CArgDescriptions::eDateTime) {
1156 str += ", format: \"Y-M-DTh:m:gZ\" or \"Y/M/D h:m:gZ\"";
1157 }
1158 // Print constraint info, if any
1159 string constr = GetUsageConstraint();
1160 if ( !constr.empty() ) {
1161 str += ", ";
1162 str += constr;
1163 }
1164 return str;
1165 }
1166
1167
ProcessArgument(const string & value) const1168 CArgValue* CArgDescMandatory::ProcessArgument(const string& value) const
1169 {
1170 // Process according to the argument type
1171 CRef<CArgValue> arg_value;
1172 switch ( GetType() ) {
1173 case CArgDescriptions::eString:
1174 arg_value = new CArg_String(GetName(), value);
1175 break;
1176 case CArgDescriptions::eBoolean:
1177 arg_value = new CArg_Boolean(GetName(), value);
1178 break;
1179 case CArgDescriptions::eInt8:
1180 arg_value = new CArg_Int8(GetName(), value);
1181 break;
1182 case CArgDescriptions::eInteger:
1183 arg_value = new CArg_Integer(GetName(), value);
1184 break;
1185 case CArgDescriptions::eIntId:
1186 arg_value = new CArg_IntId(GetName(), value);
1187 break;
1188 case CArgDescriptions::eDouble:
1189 arg_value = new CArg_Double(GetName(), value);
1190 break;
1191 case CArgDescriptions::eInputFile: {
1192 arg_value = new CArg_InputFile(GetName(), value, GetFlags());
1193 break;
1194 }
1195 case CArgDescriptions::eOutputFile: {
1196 arg_value = new CArg_OutputFile(GetName(), value, GetFlags());
1197 break;
1198 }
1199 case CArgDescriptions::eIOFile: {
1200 arg_value = new CArg_IOFile(GetName(), value, GetFlags());
1201 break;
1202 }
1203 case CArgDescriptions::eDirectory: {
1204 arg_value = new CArg_Dir(GetName(), value, GetFlags());
1205 break;
1206 }
1207 case CArgDescriptions::eDataSize:
1208 arg_value = new CArg_DataSize(GetName(), value);
1209 break;
1210 case CArgDescriptions::eDateTime:
1211 arg_value = new CArg_DateTime(GetName(), value);
1212 break;
1213 case CArgDescriptions::k_EType_Size: {
1214 _TROUBLE;
1215 NCBI_THROW(CArgException, eArgType, s_ArgExptMsg(GetName(),
1216 "Unknown argument type", NStr::IntToString((int)GetType())));
1217 }
1218 } /* switch GetType() */
1219
1220
1221 // Check against additional (user-defined) constraints, if any imposed
1222 if ( m_Constraint ) {
1223 bool err = false;
1224 try {
1225 bool check = m_Constraint->Verify(value);
1226 if (m_NegateConstraint == CArgDescriptions::eConstraintInvert) {
1227 err = check;
1228 } else {
1229 err = !check;
1230 }
1231
1232 } catch (...) {
1233 err = true;
1234 }
1235
1236 if (err) {
1237 if (GetFlags() & CArgDescriptions::fConfidential) {
1238 NCBI_THROW(CArgException, eConstraint, s_ArgExptMsg(GetName(),
1239 "Disallowed value",value));
1240 } else {
1241 string err_msg;
1242 if (m_NegateConstraint == CArgDescriptions::eConstraintInvert) {
1243 err_msg = "Illegal value, unexpected ";
1244 } else {
1245 err_msg = "Illegal value, expected ";
1246 }
1247 NCBI_THROW(CArgException, eConstraint, s_ArgExptMsg(GetName(),
1248 err_msg + m_Constraint->GetUsage(),value));
1249 }
1250 }
1251 }
1252
1253 const CArgDescDefault* dflt = dynamic_cast<const CArgDescDefault*> (this);
1254 if (dflt) {
1255 arg_value->x_SetDefault(dflt->GetDefaultValue(), false);
1256 }
1257 return arg_value.Release();
1258 }
1259
1260
ProcessDefault(void) const1261 CArgValue* CArgDescMandatory::ProcessDefault(void) const
1262 {
1263 NCBI_THROW(CArgException, eNoArg,
1264 s_ArgExptMsg(GetName(), "Mandatory value is missing",
1265 GetUsageCommentAttr()));
1266 }
1267
1268
SetConstraint(const CArgAllow * constraint,CArgDescriptions::EConstraintNegate negate)1269 void CArgDescMandatory::SetConstraint
1270 (const CArgAllow* constraint,
1271 CArgDescriptions::EConstraintNegate negate)
1272 {
1273 m_Constraint = constraint;
1274 m_NegateConstraint = negate;
1275 }
1276
1277
GetConstraint(void) const1278 const CArgAllow* CArgDescMandatory::GetConstraint(void) const
1279 {
1280 return m_Constraint;
1281 }
1282
1283
IsConstraintInverted() const1284 bool CArgDescMandatory::IsConstraintInverted() const
1285 {
1286 return m_NegateConstraint == CArgDescriptions::eConstraintInvert;
1287 }
1288
1289
1290 ///////////////////////////////////////////////////////
1291 // CArgDescOptional::
1292
1293
CArgDescOptional(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags)1294 CArgDescOptional::CArgDescOptional(const string& name,
1295 const string& comment,
1296 CArgDescriptions::EType type,
1297 CArgDescriptions::TFlags flags)
1298 : CArgDescMandatory(name, comment, type, flags),
1299 m_Group(0)
1300 {
1301 return;
1302 }
1303
1304
~CArgDescOptional(void)1305 CArgDescOptional::~CArgDescOptional(void)
1306 {
1307 return;
1308 }
1309
1310
ProcessDefault(void) const1311 CArgValue* CArgDescOptional::ProcessDefault(void) const
1312 {
1313 return new CArg_NoValue(GetName());
1314 }
1315
1316
1317
1318
1319 ///////////////////////////////////////////////////////
1320 // CArgDescDefault::
1321
1322
CArgDescDefault(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags,const string & default_value,const string & env_var,const char * display_value)1323 CArgDescDefault::CArgDescDefault(const string& name,
1324 const string& comment,
1325 CArgDescriptions::EType type,
1326 CArgDescriptions::TFlags flags,
1327 const string& default_value,
1328 const string& env_var,
1329 const char* display_value)
1330 : CArgDescMandatory(name, comment, type, flags),
1331 CArgDescOptional(name, comment, type, flags),
1332 m_DefaultValue(default_value), m_EnvVar(env_var),
1333 m_use_display(display_value != nullptr)
1334 {
1335 if (m_use_display) {
1336 m_DisplayValue = display_value;
1337 }
1338 return;
1339 }
1340
1341
~CArgDescDefault(void)1342 CArgDescDefault::~CArgDescDefault(void)
1343 {
1344 return;
1345 }
1346
GetDefaultValue(void) const1347 const string& CArgDescDefault::GetDefaultValue(void) const
1348 {
1349 if (!m_EnvVar.empty() && CNcbiApplication::Instance()) {
1350 const string& value =
1351 CNcbiApplication::Instance()->GetEnvironment().Get(m_EnvVar);
1352 if (!value.empty()) {
1353 return value;
1354 }
1355 }
1356 return m_DefaultValue;
1357 }
1358
GetDisplayValue(void) const1359 const string& CArgDescDefault::GetDisplayValue(void) const
1360 {
1361 return m_use_display ? m_DisplayValue : GetDefaultValue();
1362 }
1363
ProcessDefault(void) const1364 CArgValue* CArgDescDefault::ProcessDefault(void) const
1365 {
1366 CArgValue* v = ProcessArgument(GetDefaultValue());
1367 if (v) {
1368 v->x_SetDefault(GetDefaultValue(), true);
1369 }
1370 return v;
1371 }
1372
1373
VerifyDefault(void) const1374 void CArgDescDefault::VerifyDefault(void) const
1375 {
1376 if (GetType() == CArgDescriptions::eInputFile ||
1377 GetType() == CArgDescriptions::eOutputFile ||
1378 GetType() == CArgDescriptions::eIOFile ||
1379 GetType() == CArgDescriptions::eDirectory) {
1380 return;
1381 }
1382
1383 // Process, then immediately delete
1384 CRef<CArgValue> arg_value(ProcessArgument(GetDefaultValue()));
1385 }
1386
1387
1388 ///////////////////////////////////////////////////////
1389 // CArgDescSynopsis::
1390
1391
CArgDescSynopsis(const string & synopsis)1392 CArgDescSynopsis::CArgDescSynopsis(const string& synopsis)
1393 : m_Synopsis(synopsis)
1394 {
1395 for (string::const_iterator it = m_Synopsis.begin();
1396 it != m_Synopsis.end(); ++it) {
1397 if (*it != '_' && !isalnum((unsigned char)(*it))) {
1398 NCBI_THROW(CArgException,eSynopsis,
1399 "Argument synopsis must be alphanumeric: "+ m_Synopsis);
1400 }
1401 }
1402 }
1403
1404
1405
1406 /////////////////////////////////////////////////////////////////////////////
1407 // CArgDesc_***:: classes for argument descriptions
1408 // CArgDesc_Flag : CArgDesc
1409 //
1410 // CArgDesc_Key : virtual CArgDescMandatory
1411 // CArgDesc_KeyOpt : CArgDesc_Key, virtual CArgDescOptional
1412 // CArgDesc_KeyDef : CArgDesc_Key, CArgDescDefault
1413 //
1414 // CArgDesc_Pos : virtual CArgDescMandatory
1415 // CArgDesc_PosOpt : CArgDesc_Pos, virtual CArgDescOptional
1416 // CArgDesc_PosDef : CArgDesc_Pos, CArgDescDefault
1417 //
1418
1419
1420 ///////////////////////////////////////////////////////
1421 // CArgDesc_Flag::
1422
1423
CArgDesc_Flag(const string & name,const string & comment,bool set_value,CArgDescriptions::TFlags flags)1424 CArgDesc_Flag::CArgDesc_Flag(const string& name,
1425 const string& comment,
1426 bool set_value,
1427 CArgDescriptions::TFlags flags)
1428
1429 : CArgDesc(name, comment, flags),
1430 m_Group(0),
1431 m_SetValue(set_value)
1432 {
1433 return;
1434 }
1435
1436
~CArgDesc_Flag(void)1437 CArgDesc_Flag::~CArgDesc_Flag(void)
1438 {
1439 return;
1440 }
1441
1442
GetUsageSynopsis(bool) const1443 string CArgDesc_Flag::GetUsageSynopsis(bool /*name_only*/) const
1444 {
1445 return "-" + GetName();
1446 }
1447
1448
GetUsageCommentAttr(void) const1449 string CArgDesc_Flag::GetUsageCommentAttr(void) const
1450 {
1451 return kEmptyStr;
1452 }
1453
1454
ProcessArgument(const string &) const1455 CArgValue* CArgDesc_Flag::ProcessArgument(const string& /*value*/) const
1456 {
1457 CArgValue* v = new CArg_Flag(GetName(), m_SetValue);
1458 if (v) {
1459 v->x_SetDefault(NStr::BoolToString(!m_SetValue), false);
1460 }
1461 return v;
1462 }
1463
1464
ProcessDefault(void) const1465 CArgValue* CArgDesc_Flag::ProcessDefault(void) const
1466 {
1467 CArgValue* v = new CArg_Flag(GetName(), !m_SetValue);
1468 if (v) {
1469 v->x_SetDefault(NStr::BoolToString(!m_SetValue), true);
1470 }
1471 return v;
1472 }
1473
1474
1475
1476 ///////////////////////////////////////////////////////
1477 // CArgDesc_Pos::
1478
1479
CArgDesc_Pos(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags)1480 CArgDesc_Pos::CArgDesc_Pos(const string& name,
1481 const string& comment,
1482 CArgDescriptions::EType type,
1483 CArgDescriptions::TFlags flags)
1484 : CArgDescMandatory(name, comment, type, flags)
1485 {
1486 return;
1487 }
1488
1489
~CArgDesc_Pos(void)1490 CArgDesc_Pos::~CArgDesc_Pos(void)
1491 {
1492 return;
1493 }
1494
1495
GetUsageSynopsis(bool) const1496 string CArgDesc_Pos::GetUsageSynopsis(bool /*name_only*/) const
1497 {
1498 return GetName().empty() ? s_ExtraName : GetName();
1499 }
1500
1501
1502 ///////////////////////////////////////////////////////
1503 // CArgDesc_Opening::
1504
1505
CArgDesc_Opening(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags)1506 CArgDesc_Opening::CArgDesc_Opening(const string& name,
1507 const string& comment,
1508 CArgDescriptions::EType type,
1509 CArgDescriptions::TFlags flags)
1510 : CArgDescMandatory(name, comment, type, flags)
1511 {
1512 return;
1513 }
1514
1515
~CArgDesc_Opening(void)1516 CArgDesc_Opening::~CArgDesc_Opening(void)
1517 {
1518 return;
1519 }
1520
1521
GetUsageSynopsis(bool) const1522 string CArgDesc_Opening::GetUsageSynopsis(bool /*name_only*/) const
1523 {
1524 return GetName().empty() ? s_ExtraName : GetName();
1525 }
1526
1527
1528
1529 ///////////////////////////////////////////////////////
1530 // CArgDesc_PosOpt::
1531
1532
CArgDesc_PosOpt(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags)1533 CArgDesc_PosOpt::CArgDesc_PosOpt(const string& name,
1534 const string& comment,
1535 CArgDescriptions::EType type,
1536 CArgDescriptions::TFlags flags)
1537 : CArgDescMandatory (name, comment, type, flags),
1538 CArgDescOptional (name, comment, type, flags),
1539 CArgDesc_Pos (name, comment, type, flags)
1540 {
1541 return;
1542 }
1543
1544
~CArgDesc_PosOpt(void)1545 CArgDesc_PosOpt::~CArgDesc_PosOpt(void)
1546 {
1547 return;
1548 }
1549
1550
1551
1552 ///////////////////////////////////////////////////////
1553 // CArgDesc_PosDef::
1554
1555
CArgDesc_PosDef(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags,const string & default_value,const string & env_var,const char * display_value)1556 CArgDesc_PosDef::CArgDesc_PosDef(const string& name,
1557 const string& comment,
1558 CArgDescriptions::EType type,
1559 CArgDescriptions::TFlags flags,
1560 const string& default_value,
1561 const string& env_var,
1562 const char* display_value)
1563 : CArgDescMandatory (name, comment, type, flags),
1564 CArgDescOptional (name, comment, type, flags),
1565 CArgDescDefault (name, comment, type, flags, default_value, env_var, display_value),
1566 CArgDesc_PosOpt (name, comment, type, flags)
1567 {
1568 return;
1569 }
1570
1571
~CArgDesc_PosDef(void)1572 CArgDesc_PosDef::~CArgDesc_PosDef(void)
1573 {
1574 return;
1575 }
1576
1577
1578
1579 ///////////////////////////////////////////////////////
1580 // CArgDesc_Key::
1581
1582
CArgDesc_Key(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags,const string & synopsis)1583 CArgDesc_Key::CArgDesc_Key(const string& name,
1584 const string& comment,
1585 CArgDescriptions::EType type,
1586 CArgDescriptions::TFlags flags,
1587 const string& synopsis)
1588 : CArgDescMandatory(name, comment, type, flags),
1589 CArgDesc_Pos (name, comment, type, flags),
1590 CArgDescSynopsis(synopsis)
1591 {
1592 return;
1593 }
1594
1595
~CArgDesc_Key(void)1596 CArgDesc_Key::~CArgDesc_Key(void)
1597 {
1598 return;
1599 }
1600
1601
s_KeyUsageSynopsis(const string & name,const string & synopsis,bool name_only,CArgDescriptions::TFlags flags)1602 inline string s_KeyUsageSynopsis(const string& name, const string& synopsis,
1603 bool name_only,
1604 CArgDescriptions::TFlags flags)
1605 {
1606 if ( name_only ) {
1607 return '-' + name;
1608 } else {
1609 char separator =
1610 (flags & CArgDescriptions::fMandatorySeparator) ? '=' : ' ';
1611 return '-' + name + separator + synopsis;
1612 }
1613 }
1614
1615
GetUsageSynopsis(bool name_only) const1616 string CArgDesc_Key::GetUsageSynopsis(bool name_only) const
1617 {
1618 return s_KeyUsageSynopsis(GetName(), GetSynopsis(), name_only, GetFlags());
1619 }
1620
1621
1622
1623 ///////////////////////////////////////////////////////
1624 // CArgDesc_KeyOpt::
1625
1626
CArgDesc_KeyOpt(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags,const string & synopsis)1627 CArgDesc_KeyOpt::CArgDesc_KeyOpt(const string& name,
1628 const string& comment,
1629 CArgDescriptions::EType type,
1630 CArgDescriptions::TFlags flags,
1631 const string& synopsis)
1632 : CArgDescMandatory(name, comment, type, flags),
1633 CArgDescOptional (name, comment, type, flags),
1634 CArgDesc_PosOpt (name, comment, type, flags),
1635 CArgDescSynopsis(synopsis)
1636 {
1637 return;
1638 }
1639
1640
~CArgDesc_KeyOpt(void)1641 CArgDesc_KeyOpt::~CArgDesc_KeyOpt(void)
1642 {
1643 return;
1644 }
1645
1646
GetUsageSynopsis(bool name_only) const1647 string CArgDesc_KeyOpt::GetUsageSynopsis(bool name_only) const
1648 {
1649 return s_KeyUsageSynopsis(GetName(), GetSynopsis(), name_only, GetFlags());
1650 }
1651
1652
1653
1654 ///////////////////////////////////////////////////////
1655 // CArgDesc_KeyDef::
1656
1657
CArgDesc_KeyDef(const string & name,const string & comment,CArgDescriptions::EType type,CArgDescriptions::TFlags flags,const string & synopsis,const string & default_value,const string & env_var,const char * display_value)1658 CArgDesc_KeyDef::CArgDesc_KeyDef(const string& name,
1659 const string& comment,
1660 CArgDescriptions::EType type,
1661 CArgDescriptions::TFlags flags,
1662 const string& synopsis,
1663 const string& default_value,
1664 const string& env_var,
1665 const char* display_value)
1666 : CArgDescMandatory(name, comment, type, flags),
1667 CArgDescOptional (name, comment, type, flags),
1668 CArgDesc_PosDef (name, comment, type, flags, default_value, env_var, display_value),
1669 CArgDescSynopsis(synopsis)
1670 {
1671 return;
1672 }
1673
1674
~CArgDesc_KeyDef(void)1675 CArgDesc_KeyDef::~CArgDesc_KeyDef(void)
1676 {
1677 return;
1678 }
1679
1680
GetUsageSynopsis(bool name_only) const1681 string CArgDesc_KeyDef::GetUsageSynopsis(bool name_only) const
1682 {
1683 return s_KeyUsageSynopsis(GetName(), GetSynopsis(), name_only, GetFlags());
1684 }
1685
1686
1687 ///////////////////////////////////////////////////////
1688 // CArgDesc_Alias::
1689
CArgDesc_Alias(const string & alias,const string & arg_name,const string & comment)1690 CArgDesc_Alias::CArgDesc_Alias(const string& alias,
1691 const string& arg_name,
1692 const string& comment)
1693 : CArgDesc(alias, comment),
1694 m_ArgName(arg_name),
1695 m_NegativeFlag(false)
1696 {
1697 }
1698
1699
~CArgDesc_Alias(void)1700 CArgDesc_Alias::~CArgDesc_Alias(void)
1701 {
1702 }
1703
1704
GetAliasedName(void) const1705 const string& CArgDesc_Alias::GetAliasedName(void) const
1706 {
1707 return m_ArgName;
1708 }
1709
1710
GetUsageSynopsis(bool) const1711 string CArgDesc_Alias::GetUsageSynopsis(bool /*name_only*/) const
1712 {
1713 return kEmptyStr;
1714 }
1715
1716
GetUsageCommentAttr(void) const1717 string CArgDesc_Alias::GetUsageCommentAttr(void) const
1718 {
1719 return kEmptyStr;
1720 }
1721
1722
ProcessArgument(const string &) const1723 CArgValue* CArgDesc_Alias::ProcessArgument(const string& /*value*/) const
1724 {
1725 return new CArg_NoValue(GetName());
1726 }
1727
1728
ProcessDefault(void) const1729 CArgValue* CArgDesc_Alias::ProcessDefault(void) const
1730 {
1731 return new CArg_NoValue(GetName());
1732 }
1733
1734
1735 /////////////////////////////////////////////////////////////////////////////
1736 // CArgs::
1737 //
1738
1739
CArgs(void)1740 CArgs::CArgs(void)
1741 {
1742 m_nExtra = 0;
1743 }
1744
1745
~CArgs(void)1746 CArgs::~CArgs(void)
1747 {
1748 return;
1749 }
1750
CArgs(const CArgs & other)1751 CArgs::CArgs(const CArgs& other)
1752 {
1753 Assign(other);
1754 }
1755
operator =(const CArgs & other)1756 CArgs& CArgs::operator=(const CArgs& other)
1757 {
1758 return Assign(other);
1759 }
1760
Assign(const CArgs & other)1761 CArgs& CArgs::Assign(const CArgs& other)
1762 {
1763 if (this != &other) {
1764 m_Args = other.m_Args;
1765 m_nExtra = other.m_nExtra;
1766 m_Command = other.m_Command;
1767 }
1768 return *this;
1769 }
1770
s_ComposeNameExtra(size_t idx)1771 static string s_ComposeNameExtra(size_t idx)
1772 {
1773 return '#' + NStr::UInt8ToString(idx);
1774 }
1775
1776
s_IsArgNameChar(char c)1777 inline bool s_IsArgNameChar(char c)
1778 {
1779 return isalnum(c) || c == '_' || c == '-';
1780 }
1781
1782
x_Find(const string & name) const1783 CArgs::TArgsCI CArgs::x_Find(const string& name) const
1784 {
1785 CArgs::TArgsCI arg = m_Args.find(CRef<CArgValue> (new CArg_NoValue(name)));
1786 if (arg != m_Args.end() || name.empty() || name[0] == '-' ||
1787 !s_IsArgNameChar(name[0])) {
1788 return arg;
1789 }
1790 return m_Args.find(CRef<CArgValue> (new CArg_NoValue("-" + name)));
1791 }
1792
x_Find(const string & name)1793 CArgs::TArgsI CArgs::x_Find(const string& name)
1794 {
1795 CArgs::TArgsI arg = m_Args.find(CRef<CArgValue> (new CArg_NoValue(name)));
1796 if (arg != m_Args.end() || name.empty() || name[0] == '-' ||
1797 !s_IsArgNameChar(name[0])) {
1798 return arg;
1799 }
1800 return m_Args.find(CRef<CArgValue> (new CArg_NoValue("-" + name)));
1801 }
1802
1803
Exist(const string & name) const1804 bool CArgs::Exist(const string& name) const
1805 {
1806 return (x_Find(name) != m_Args.end());
1807 }
1808
1809
operator [](const string & name) const1810 const CArgValue& CArgs::operator[] (const string& name) const
1811 {
1812 TArgsCI arg = x_Find(name);
1813 if (arg == m_Args.end()) {
1814 // Special diagnostics for "extra" args
1815 if (!name.empty() && name[0] == '#') {
1816 size_t idx;
1817 try {
1818 idx = NStr::StringToUInt(name.c_str() + 1);
1819 } catch (...) {
1820 idx = kMax_UInt;
1821 }
1822 if (idx == kMax_UInt) {
1823 NCBI_THROW(CArgException, eInvalidArg,
1824 "Asked for an argument with invalid name: \"" +
1825 name + "\"");
1826 }
1827 if (m_nExtra == 0) {
1828 NCBI_THROW(CArgException, eInvalidArg,
1829 "No \"extra\" (unnamed positional) arguments "
1830 "provided, cannot Get: " + s_ComposeNameExtra(idx));
1831 }
1832 if (idx == 0 || idx >= m_nExtra) {
1833 NCBI_THROW(CArgException, eInvalidArg,
1834 "\"Extra\" (unnamed positional) arg is "
1835 "out-of-range (#1.." + s_ComposeNameExtra(m_nExtra)
1836 + "): " + s_ComposeNameExtra(idx));
1837 }
1838 }
1839
1840 // Diagnostics for all other argument classes
1841 NCBI_THROW(CArgException, eInvalidArg,
1842 "Unknown argument requested: \"" + name + "\"");
1843 }
1844
1845 // Found arg with name "name"
1846 return **arg;
1847 }
1848
1849
operator [](size_t idx) const1850 const CArgValue& CArgs::operator[] (size_t idx) const
1851 {
1852 return (*this)[s_ComposeNameExtra(idx)];
1853 }
1854
GetAll(void) const1855 vector< CRef<CArgValue> > CArgs::GetAll(void) const
1856 {
1857 vector< CRef<CArgValue> > res;
1858 ITERATE( TArgs, a, m_Args) {
1859 if ((**a).HasValue()) {
1860 res.push_back( *a );
1861 }
1862 }
1863 return res;
1864 }
1865
1866
Print(string & str) const1867 string& CArgs::Print(string& str) const
1868 {
1869 for (TArgsCI arg = m_Args.begin(); arg != m_Args.end(); ++arg) {
1870 // Arg. name
1871 const string& arg_name = (*arg)->GetName();
1872 str += arg_name;
1873
1874 // Arg. value, if any
1875 const CArgValue& arg_value = (*this)[arg_name];
1876 if ( arg_value ) {
1877 str += " = `";
1878 string tmp;
1879 try {
1880 tmp = NStr::Join( arg_value.GetStringList(), " ");
1881 } catch (...) {
1882 tmp = arg_value.AsString();
1883 }
1884 str += tmp;
1885 str += "'\n";
1886 } else {
1887 str += ": <not assigned>\n";
1888 }
1889 }
1890 return str;
1891 }
1892
1893
Remove(const string & name)1894 void CArgs::Remove(const string& name)
1895 {
1896 CArgs::TArgsI it = m_Args.find(CRef<CArgValue> (new CArg_NoValue(name)));
1897 m_Args.erase(it);
1898 }
1899
1900
Reset(void)1901 void CArgs::Reset(void)
1902 {
1903 m_nExtra = 0;
1904 m_Args.clear();
1905 }
1906
1907
Add(CArgValue * arg,bool update,bool add_value)1908 void CArgs::Add(CArgValue* arg, bool update, bool add_value)
1909 {
1910 // special case: add an "extra" arg (generate virtual name for it)
1911 bool is_extra = false;
1912 if ( arg->GetName().empty() ) {
1913 arg->m_Name = s_ComposeNameExtra(m_nExtra + 1);
1914 is_extra = true;
1915 }
1916
1917 // check-up
1918 _ASSERT(CArgDescriptions::VerifyName(arg->GetName(), true));
1919 CArgs::TArgsI arg_it = x_Find(arg->GetName());
1920 if ( arg_it != m_Args.end()) {
1921 if (update) {
1922 Remove(arg->GetName());
1923 } else {
1924 if (add_value) {
1925 const string& v = arg->AsString();
1926 CRef<CArgValue> av = *arg_it;
1927 av->SetStringList().push_back(v);
1928 } else {
1929 NCBI_THROW(CArgException,eSynopsis,
1930 "Argument with this name is defined already: "
1931 + arg->GetName());
1932 }
1933 }
1934 }
1935
1936 // add
1937 arg->SetOrdinalPosition(m_Args.size()+1);
1938 m_Args.insert(CRef<CArgValue>(arg));
1939
1940 if ( is_extra ) {
1941 m_nExtra++;
1942 }
1943 }
1944
1945
IsEmpty(void) const1946 bool CArgs::IsEmpty(void) const
1947 {
1948 return m_Args.empty();
1949 }
1950
1951 enum EEchoInput {
1952 eNoEcho,
1953 eEchoInput
1954 };
1955
s_CArgs_ReadFromFile(const string & name,const string & filename)1956 static string s_CArgs_ReadFromFile(const string& name, const string& filename)
1957 {
1958 CArg_InputFile f(name, filename, CArgDescriptions::fBinary);
1959 istreambuf_iterator<char> it( f.AsInputFile() );
1960 vector<char> value;
1961 std::copy( it, istreambuf_iterator<char>(), back_inserter(value));
1962 while (value[ value.size()-1] == '\r' || value[ value.size()-1] == '\n') {
1963 value.pop_back();
1964 }
1965 return string( value.data(), value.size());
1966 }
1967
1968
s_CArgs_ReadFromStdin(const string & name,EEchoInput echo_input,const char * cue)1969 static string s_CArgs_ReadFromStdin(const string& name, EEchoInput echo_input, const char* cue)
1970 {
1971 string thx("\n");
1972 string prompt;
1973 if (cue) {
1974 prompt = cue;
1975 } else {
1976 prompt = "Please enter value of parameter '";
1977 prompt += name;
1978 prompt += "': ";
1979 }
1980 if (!prompt.empty()) {
1981 cout << prompt;
1982 cout.flush();
1983 }
1984
1985 string value;
1986
1987 #if defined(NCBI_OS_MSWIN)
1988 DWORD dw = 0;
1989 HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
1990 if (hIn != INVALID_HANDLE_VALUE) {
1991
1992 DWORD mode = 0, silent_mode = 0;
1993 if (echo_input == eNoEcho) {
1994 GetConsoleMode(hIn, &mode);
1995 silent_mode = mode & ~ENABLE_ECHO_INPUT;
1996 SetConsoleMode(hIn, silent_mode);
1997 }
1998
1999 char buffer[256];
2000 while( ReadFile(hIn, buffer, 256, &dw, NULL) && dw != 0) {
2001 bool eol = false;
2002 while ( buffer[dw-1] == '\n' || buffer[dw-1] == '\r') {
2003 --dw; eol = true;
2004 }
2005 value.append(buffer,dw);
2006 if (eol) {
2007 break;
2008 }
2009 }
2010
2011 if (echo_input == eNoEcho) {
2012 SetConsoleMode(hIn, mode);
2013 }
2014 }
2015 #else
2016 struct termios mode, silent_mode;
2017 if (echo_input == eNoEcho) {
2018 tcgetattr( STDIN_FILENO, &mode );
2019 silent_mode = mode;
2020 silent_mode.c_lflag &= ~ECHO;
2021 tcsetattr( STDIN_FILENO, TCSANOW, &silent_mode );
2022 }
2023
2024 for (;;) {
2025 int ich = getchar();
2026 if (ich == EOF)
2027 break;
2028
2029 char ch = (char) ich;
2030 if (ch == '\n' || ch == '\r')
2031 break;
2032
2033 if (ch == '\b') {
2034 if (value.size() > 0) {
2035 value.resize(value.size() - 1);
2036 }
2037 } else {
2038 value += ch;
2039 }
2040 }
2041
2042 if (echo_input == eNoEcho) {
2043 tcsetattr( STDIN_FILENO, TCSANOW, &mode );
2044 }
2045 #endif
2046 if (!prompt.empty()) {
2047 cout << thx;
2048 }
2049 return value;
2050 }
2051
2052
s_CArgs_ReadFromConsole(const string & name,EEchoInput echo_input,const char * cue)2053 static string s_CArgs_ReadFromConsole(const string& name, EEchoInput echo_input, const char* cue)
2054 {
2055 string thx("\n");
2056 string prompt;
2057 if (cue) {
2058 prompt = cue;
2059 } else {
2060 prompt = "Please enter value of parameter '";
2061 prompt += name;
2062 prompt += "': ";
2063 }
2064
2065 string value;
2066 #if defined(NCBI_OS_MSWIN)
2067 DWORD dw = 0;
2068 HANDLE hOut = INVALID_HANDLE_VALUE;
2069 if (!prompt.empty()) {
2070 hOut = CreateFile(_TX("CONOUT$"),
2071 GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
2072 FILE_ATTRIBUTE_NORMAL, NULL);
2073 if (hOut != INVALID_HANDLE_VALUE) {
2074 WriteFile(hOut, prompt.data(), (DWORD)prompt.length(), &dw, NULL);
2075 }
2076 }
2077
2078 HANDLE hIn = CreateFile(_TX("CONIN$"),
2079 GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING,
2080 FILE_ATTRIBUTE_NORMAL, NULL);
2081 if (hIn != INVALID_HANDLE_VALUE) {
2082 DWORD mode = 0, silent_mode = 0;
2083 if (echo_input == eNoEcho) {
2084 GetConsoleMode(hIn, &mode);
2085 silent_mode = mode & ~ENABLE_ECHO_INPUT;
2086 SetConsoleMode(hIn, silent_mode);
2087 }
2088
2089 char buffer[256];
2090 while( ReadFile(hIn, buffer, 256, &dw, NULL) && dw != 0) {
2091 bool eol = false;
2092 while ( buffer[dw-1] == '\n' || buffer[dw-1] == '\r') {
2093 --dw;
2094 eol = true;
2095 }
2096 value.append(buffer,dw);
2097 if (eol) {
2098 break;
2099 }
2100 }
2101
2102 if (echo_input == eNoEcho) {
2103 SetConsoleMode(hIn, mode);
2104 }
2105 CloseHandle(hIn);
2106 }
2107
2108 if (hOut != INVALID_HANDLE_VALUE) {
2109 WriteFile(hOut, thx.data(), (DWORD)thx.length(), &dw, NULL);
2110 CloseHandle(hOut);
2111 }
2112 #else
2113 int tty = open("/dev/tty", O_RDWR);
2114 if (tty >= 0) {
2115 if (!prompt.empty()) {
2116 write( tty, prompt.data(), prompt.length());
2117 }
2118
2119 struct termios mode, silent_mode;
2120 if (echo_input == eNoEcho) {
2121 tcgetattr( tty, &mode );
2122 silent_mode = mode;
2123 silent_mode.c_lflag &= ~ECHO;
2124 tcsetattr( tty, TCSANOW, &silent_mode );
2125 }
2126
2127 char buffer[256];
2128 ssize_t i = 0;
2129 while ( (i = read(tty, buffer, 256)) > 0) {
2130 bool eol = false;
2131 while ( i > 0 && (buffer[i-1] == '\n' || buffer[i-1] == '\r')) {
2132 --i;
2133 eol = true;
2134 }
2135 value.append(buffer,i);
2136 if (eol) {
2137 break;
2138 }
2139 }
2140
2141 if (echo_input == eNoEcho) {
2142 tcsetattr( tty, TCSANOW, &mode );
2143 }
2144 if (!prompt.empty()) {
2145 write( tty, thx.data(), thx.length());
2146 }
2147 close(tty);
2148 }
2149 #endif
2150 return value;
2151 }
2152
2153
2154 ///////////////////////////////////////////////////////
2155 // CArgErrorHandler::
2156
HandleError(const CArgDesc & arg_desc,const string & value) const2157 CArgValue* CArgErrorHandler::HandleError(const CArgDesc& arg_desc,
2158 const string& value) const
2159 {
2160 if ((arg_desc.GetFlags() & CArgDescriptions::fIgnoreInvalidValue) == 0) {
2161 // Re-process invalid value to throw the same exception
2162 return arg_desc.ProcessArgument(value);
2163 // Should never get past ProcessArgument()
2164 }
2165 if ((arg_desc.GetFlags() & CArgDescriptions::fWarnOnInvalidValue) != 0) {
2166 ERR_POST_X(22, Warning << "Invalid value " << value <<
2167 " for argument " << arg_desc.GetName() <<
2168 " - argument will be ignored.");
2169 }
2170 // return 0 to ignore the argument
2171 return 0;
2172 }
2173
2174
2175 ///////////////////////////////////////////////////////
2176 // CArgDescriptions::
2177 //
2178
2179
CArgDescriptions(bool auto_help,CArgErrorHandler * err_handler)2180 CArgDescriptions::CArgDescriptions(bool auto_help,
2181 CArgErrorHandler* err_handler)
2182 : m_ArgsType(eRegularArgs),
2183 m_nExtra(0),
2184 m_nExtraOpt(0),
2185 m_CurrentGroup(0),
2186 m_PositionalMode(ePositionalMode_Strict),
2187 m_MiscFlags(fMisc_Default),
2188 m_AutoHelp(auto_help),
2189 m_ErrorHandler(err_handler)
2190 {
2191 if ( !m_ErrorHandler ) {
2192 // Use default error handler
2193 m_ErrorHandler.Reset(new CArgErrorHandler);
2194 }
2195
2196 SetUsageContext("NCBI_PROGRAM", kEmptyStr);
2197 m_ArgGroups.push_back(kEmptyStr); // create default group #0
2198 if ( m_AutoHelp ) {
2199 AddFlag(s_AutoHelp,
2200 "Print USAGE and DESCRIPTION; ignore all other parameters");
2201 }
2202 AddFlag(s_AutoHelpFull,
2203 "Print USAGE, DESCRIPTION and ARGUMENTS;"
2204 " ignore all other parameters");
2205 AddFlag(s_AutoHelpXml,
2206 "Print USAGE, DESCRIPTION and ARGUMENTS in XML format;"
2207 " ignore all other parameters");
2208 }
2209
2210
~CArgDescriptions(void)2211 CArgDescriptions::~CArgDescriptions(void)
2212 {
2213 return;
2214 }
2215
2216
SetArgsType(EArgSetType args_type)2217 void CArgDescriptions::SetArgsType(EArgSetType args_type)
2218 {
2219 m_ArgsType = args_type;
2220
2221 // Run args check for a CGI application
2222 if (m_ArgsType == eCgiArgs) {
2223 // Must have no named positional arguments
2224 if ( !m_PosArgs.empty() ) {
2225 NCBI_THROW(CArgException, eInvalidArg,
2226 "CGI application cannot have positional arguments, "
2227 "name of the offending argument: '"
2228 + *m_PosArgs.begin() + "'.");
2229 }
2230
2231 // Must have no unnamed positional arguments
2232 if (m_nExtra || m_nExtraOpt) {
2233 NCBI_THROW(CArgException, eInvalidArg,
2234 "CGI application cannot have unnamed positional "
2235 "arguments.");
2236 }
2237 }
2238 }
2239
2240
GetTypeName(EType type)2241 const char* CArgDescriptions::GetTypeName(EType type)
2242 {
2243 static const char* s_TypeName[k_EType_Size] = {
2244 "String",
2245 "Boolean",
2246 "Int8",
2247 "Integer",
2248 "IntId",
2249 "Real",
2250 "File_In",
2251 "File_Out",
2252 "File_IO",
2253 "Directory",
2254 "DataSize",
2255 "DateTime"
2256 };
2257
2258 if (type == k_EType_Size) {
2259 _TROUBLE;
2260 NCBI_THROW(CArgException, eArgType,
2261 "Invalid argument type: k_EType_Size");
2262 }
2263 return s_TypeName[(int) type];
2264 }
2265
2266
AddKey(const string & name,const string & synopsis,const string & comment,EType type,TFlags flags)2267 void CArgDescriptions::AddKey
2268 (const string& name,
2269 const string& synopsis,
2270 const string& comment,
2271 EType type,
2272 TFlags flags)
2273 {
2274 unique_ptr<CArgDesc_Key> arg(new CArgDesc_Key(name,
2275 comment, type, flags, synopsis));
2276
2277 x_AddDesc(*arg);
2278 arg.release();
2279 }
2280
2281
AddOptionalKey(const string & name,const string & synopsis,const string & comment,EType type,TFlags flags)2282 void CArgDescriptions::AddOptionalKey
2283 (const string& name,
2284 const string& synopsis,
2285 const string& comment,
2286 EType type,
2287 TFlags flags)
2288 {
2289 unique_ptr<CArgDesc_KeyOpt> arg(new CArgDesc_KeyOpt(name,
2290 comment, type, flags, synopsis));
2291
2292 x_AddDesc(*arg);
2293 arg.release();
2294 }
2295
2296
AddDefaultKey(const string & name,const string & synopsis,const string & comment,EType type,const string & default_value,TFlags flags,const string & env_var,const char * display_value)2297 void CArgDescriptions::AddDefaultKey
2298 (const string& name,
2299 const string& synopsis,
2300 const string& comment,
2301 EType type,
2302 const string& default_value,
2303 TFlags flags,
2304 const string& env_var,
2305 const char* display_value)
2306 {
2307 unique_ptr<CArgDesc_KeyDef> arg(new CArgDesc_KeyDef(name,
2308 comment, type, flags, synopsis, default_value, env_var, display_value));
2309
2310 x_AddDesc(*arg);
2311 arg.release();
2312 }
2313
2314
AddFlag(const string & name,const string & comment,CBoolEnum<EFlagValue> set_value,TFlags flags)2315 void CArgDescriptions::AddFlag(
2316 const string& name,
2317 const string& comment,
2318 CBoolEnum<EFlagValue> set_value,
2319 TFlags flags)
2320 {
2321 unique_ptr<CArgDesc_Flag> arg(new CArgDesc_Flag(name, comment, set_value == eFlagHasValueIfSet, flags));
2322 x_AddDesc(*arg);
2323 arg.release();
2324 }
2325
2326
AddPositional(const string & name,const string & comment,EType type,TFlags flags)2327 void CArgDescriptions::AddPositional(
2328 const string& name,
2329 const string& comment,
2330 EType type,
2331 TFlags flags)
2332 {
2333 unique_ptr<CArgDesc_Pos> arg(new CArgDesc_Pos(name, comment, type, flags));
2334
2335 x_AddDesc(*arg);
2336 arg.release();
2337 }
2338
2339
AddOpening(const string & name,const string & comment,EType type,TFlags flags)2340 void CArgDescriptions::AddOpening(
2341 const string& name,
2342 const string& comment,
2343 EType type,
2344 TFlags flags)
2345 {
2346 unique_ptr<CArgDesc_Opening> arg(new CArgDesc_Opening(name, comment, type, flags));
2347
2348 x_AddDesc(*arg);
2349 arg.release();
2350 }
2351
2352
AddOptionalPositional(const string & name,const string & comment,EType type,TFlags flags)2353 void CArgDescriptions::AddOptionalPositional(
2354 const string& name,
2355 const string& comment,
2356 EType type,
2357 TFlags flags)
2358 {
2359 unique_ptr<CArgDesc_PosOpt> arg
2360 (new CArgDesc_PosOpt(name, comment, type, flags));
2361
2362 x_AddDesc(*arg);
2363 arg.release();
2364 }
2365
2366
AddDefaultPositional(const string & name,const string & comment,EType type,const string & default_value,TFlags flags,const string & env_var,const char * display_value)2367 void CArgDescriptions::AddDefaultPositional(
2368 const string& name,
2369 const string& comment,
2370 EType type,
2371 const string& default_value,
2372 TFlags flags,
2373 const string& env_var,
2374 const char* display_value)
2375 {
2376 unique_ptr<CArgDesc_PosDef> arg(new CArgDesc_PosDef(name,
2377 comment, type, flags, default_value, env_var, display_value));
2378
2379 x_AddDesc(*arg);
2380 arg.release();
2381 }
2382
2383
AddExtra(unsigned n_mandatory,unsigned n_optional,const string & comment,EType type,TFlags flags)2384 void CArgDescriptions::AddExtra(
2385 unsigned n_mandatory,
2386 unsigned n_optional,
2387 const string& comment,
2388 EType type,
2389 TFlags flags)
2390 {
2391 if (!n_mandatory && !n_optional) {
2392 NCBI_THROW(CArgException,eSynopsis,
2393 "Number of extra arguments cannot be zero");
2394 }
2395 if (n_mandatory > 4096) {
2396 NCBI_THROW(CArgException,eSynopsis,
2397 "Number of mandatory extra arguments is too big");
2398 }
2399
2400 m_nExtra = n_mandatory;
2401 m_nExtraOpt = n_optional;
2402
2403 unique_ptr<CArgDesc_Pos> arg
2404 (m_nExtra ?
2405 new CArgDesc_Pos (kEmptyStr, comment, type, flags) :
2406 new CArgDesc_PosOpt(kEmptyStr, comment, type, flags));
2407
2408 x_AddDesc(*arg);
2409 arg.release();
2410 }
2411
2412
AddAlias(const string & alias,const string & arg_name)2413 void CArgDescriptions::AddAlias(const string& alias,
2414 const string& arg_name)
2415 {
2416 unique_ptr<CArgDesc_Alias>arg
2417 (new CArgDesc_Alias(alias, arg_name, kEmptyStr));
2418
2419 x_AddDesc(*arg);
2420 arg.release();
2421 }
2422
2423
AddNegatedFlagAlias(const string & alias,const string & arg_name,const string & comment)2424 void CArgDescriptions::AddNegatedFlagAlias(const string& alias,
2425 const string& arg_name,
2426 const string& comment)
2427 {
2428 // Make sure arg_name describes a flag
2429 TArgsCI orig = x_Find(arg_name);
2430 if (orig == m_Args.end() || !s_IsFlag(**orig)) {
2431 NCBI_THROW(CArgException, eArgType,
2432 "Attempt to negate a non-flag argument: " + arg_name);
2433 }
2434
2435 unique_ptr<CArgDesc_Alias> arg(new CArgDesc_Alias(alias, arg_name, comment));
2436 arg->SetNegativeFlag(true);
2437
2438 x_AddDesc(*arg);
2439 arg.release();
2440 }
2441
AddDependencyGroup(CArgDependencyGroup * dep_group)2442 void CArgDescriptions::AddDependencyGroup(CArgDependencyGroup* dep_group)
2443 {
2444 m_DependencyGroups.insert( CConstRef<CArgDependencyGroup>(dep_group));
2445 }
2446
SetConstraint(const string & name,const CArgAllow * constraint,EConstraintNegate negate)2447 void CArgDescriptions::SetConstraint(const string& name,
2448 const CArgAllow* constraint,
2449 EConstraintNegate negate)
2450 {
2451 TArgsI it = x_Find(name);
2452 if (it == m_Args.end()) {
2453 CConstRef<CArgAllow> safe_delete(constraint); // delete, if last ref
2454 NCBI_THROW(CArgException, eConstraint,
2455 "Attempt to set constraint for undescribed argument: " + name);
2456 }
2457 (*it)->SetConstraint(constraint, negate);
2458 }
2459
2460
SetConstraint(const string & name,const CArgAllow & constraint,EConstraintNegate negate)2461 void CArgDescriptions::SetConstraint(const string& name,
2462 const CArgAllow& constraint,
2463 EConstraintNegate negate)
2464 {
2465 CArgAllow* onheap = constraint.Clone();
2466 if ( !onheap ) {
2467 NCBI_THROW(CArgException, eConstraint,
2468 "Clone method not implemented for: " + name);
2469 }
2470 SetConstraint(name, onheap, negate);
2471 }
2472
2473
SetDependency(const string & arg1,EDependency dep,const string & arg2)2474 void CArgDescriptions::SetDependency(const string& arg1,
2475 EDependency dep,
2476 const string& arg2)
2477 {
2478 m_Dependencies.insert(TDependencies::value_type(arg1,
2479 SArgDependency(arg2, dep)));
2480 if (dep == eExcludes) {
2481 // Exclusions must work in both directions
2482 m_Dependencies.insert(TDependencies::value_type(arg2,
2483 SArgDependency(arg1, dep)));
2484 }
2485 }
2486
2487
SetCurrentGroup(const string & group)2488 void CArgDescriptions::SetCurrentGroup(const string& group)
2489 {
2490 m_CurrentGroup = x_GetGroupIndex(group);
2491 if (m_CurrentGroup >= m_ArgGroups.size()) {
2492 m_ArgGroups.push_back(group);
2493 m_CurrentGroup = m_ArgGroups.size() - 1;
2494 }
2495 }
2496
2497
SetErrorHandler(const string & name,CArgErrorHandler * err_handler)2498 void CArgDescriptions::SetErrorHandler(const string& name,
2499 CArgErrorHandler* err_handler)
2500 {
2501 TArgsI it = x_Find(name);
2502 if (it == m_Args.end()) {
2503 NCBI_THROW(CArgException, eInvalidArg,
2504 "Attempt to set error handler for undescribed argument: "+ name);
2505 }
2506 (*it)->SetErrorHandler(err_handler);
2507 }
2508
2509
Exist(const string & name) const2510 bool CArgDescriptions::Exist(const string& name) const
2511 {
2512 return (x_Find(name) != m_Args.end());
2513 }
2514
2515
Delete(const string & name)2516 void CArgDescriptions::Delete(const string& name)
2517 {
2518 {{ // ...from the list of all args
2519 TArgsI it = x_Find(name);
2520 if (it == m_Args.end()) {
2521 NCBI_THROW(CArgException,eSynopsis,
2522 "Argument description is not found");
2523 }
2524 m_Args.erase(it);
2525 if (name == s_AutoHelp) {
2526 m_AutoHelp = false;
2527 }
2528
2529 // take special care of the extra args
2530 if ( name.empty() ) {
2531 m_nExtra = 0;
2532 m_nExtraOpt = 0;
2533 return;
2534 }
2535 }}
2536
2537 {{ // ...from the list of key/flag args
2538 TKeyFlagArgs::iterator it =
2539 find(m_KeyFlagArgs.begin(), m_KeyFlagArgs.end(), name);
2540 if (it != m_KeyFlagArgs.end()) {
2541 m_KeyFlagArgs.erase(it);
2542 _ASSERT(find(m_KeyFlagArgs.begin(), m_KeyFlagArgs.end(), name) ==
2543 m_KeyFlagArgs.end());
2544 _ASSERT(find(m_PosArgs.begin(), m_PosArgs.end(), name) ==
2545 m_PosArgs.end());
2546 return;
2547 }
2548 }}
2549
2550 {{ // ...from the list of positional args' positions
2551 TPosArgs::iterator it =
2552 find(m_PosArgs.begin(), m_PosArgs.end(), name);
2553 _ASSERT (it != m_PosArgs.end());
2554 m_PosArgs.erase(it);
2555 _ASSERT(find(m_PosArgs.begin(), m_PosArgs.end(), name) ==
2556 m_PosArgs.end());
2557 }}
2558 }
2559
2560
2561 // Fake class to hold only a name -- to find in "m_Args"
2562 class CArgDesc_NameOnly : public CArgDesc
2563 {
2564 public:
CArgDesc_NameOnly(const string & name)2565 CArgDesc_NameOnly(const string& name) :
2566 CArgDesc(name, kEmptyStr) {}
2567 private:
GetUsageSynopsis(bool) const2568 virtual string GetUsageSynopsis(bool/*name_only*/) const{return kEmptyStr;}
GetUsageCommentAttr(void) const2569 virtual string GetUsageCommentAttr(void) const {return kEmptyStr;}
ProcessArgument(const string &) const2570 virtual CArgValue* ProcessArgument(const string&) const {return 0;}
ProcessDefault(void) const2571 virtual CArgValue* ProcessDefault(void) const {return 0;}
2572 };
2573
x_Find(const string & name,bool * negative) const2574 CArgDescriptions::TArgsCI CArgDescriptions::x_Find(const string& name,
2575 bool* negative) const
2576 {
2577 CArgDescriptions::TArgsCI arg =
2578 m_Args.find(AutoPtr<CArgDesc> (new CArgDesc_NameOnly(name)));
2579 if ( arg != m_Args.end() ) {
2580 const CArgDesc_Alias* al =
2581 dynamic_cast<const CArgDesc_Alias*>(arg->get());
2582 if ( al ) {
2583 if ( negative ) {
2584 *negative = al->GetNegativeFlag();
2585 }
2586 return x_Find(al->GetAliasedName(), negative);
2587 }
2588 }
2589 return arg;
2590 }
2591
x_Find(const string & name,bool * negative)2592 CArgDescriptions::TArgsI CArgDescriptions::x_Find(const string& name,
2593 bool* negative)
2594 {
2595 CArgDescriptions::TArgsI arg =
2596 m_Args.find(AutoPtr<CArgDesc> (new CArgDesc_NameOnly(name)));
2597 if ( arg != m_Args.end() ) {
2598 const CArgDesc_Alias* al =
2599 dynamic_cast<const CArgDesc_Alias*>(arg->get());
2600 if ( al ) {
2601 if ( negative ) {
2602 *negative = al->GetNegativeFlag();
2603 }
2604 return x_Find(al->GetAliasedName(), negative);
2605 }
2606 }
2607 return arg;
2608 }
2609
2610
x_GetGroupIndex(const string & group) const2611 size_t CArgDescriptions::x_GetGroupIndex(const string& group) const
2612 {
2613 if ( group.empty() ) {
2614 return 0;
2615 }
2616 for (size_t i = 1; i < m_ArgGroups.size(); ++i) {
2617 if ( NStr::EqualNocase(m_ArgGroups[i], group) ) {
2618 return i;
2619 }
2620 }
2621 return m_ArgGroups.size();
2622 }
2623
2624
x_PreCheck(void) const2625 void CArgDescriptions::x_PreCheck(void) const
2626 {
2627 // Check for the consistency of positional args
2628 if ( m_nExtra ) {
2629 for (TPosArgs::const_iterator name = m_PosArgs.begin();
2630 name != m_PosArgs.end(); ++name) {
2631 TArgsCI arg_it = x_Find(*name);
2632 _ASSERT(arg_it != m_Args.end());
2633 CArgDesc& arg = **arg_it;
2634
2635 if (dynamic_cast<const CArgDesc_PosOpt*> (&arg)) {
2636 NCBI_THROW(CArgException, eSynopsis,
2637 "Having both optional named and required unnamed "
2638 "positional arguments is prohibited");
2639 }
2640 }
2641 }
2642
2643 // Check for the validity of default values.
2644 // Also check for conflict between no-separator and regular names
2645 for (TArgsCI it = m_Args.begin(); it != m_Args.end(); ++it) {
2646 CArgDesc& arg = **it;
2647
2648 const string& name = arg.GetName();
2649 if (name.length() > 1 && m_NoSeparator.find(name[0]) != NPOS) {
2650 // find the argument with optional separator and check its flags
2651 for (TArgsCI i = m_Args.begin(); i != m_Args.end(); ++i) {
2652 CArgDesc& a = **i;
2653 const string& n = a.GetName();
2654 if (n.length() == 1 && n[0] == name[0] &&
2655 (a.GetFlags() & CArgDescriptions::fOptionalSeparator)) {
2656 if ((a.GetFlags() & CArgDescriptions::fOptionalSeparatorAllowConflict) == 0) {
2657 NCBI_THROW(CArgException, eInvalidArg,
2658 string("'") + name[0] +
2659 "' argument allowed to contain no separator conflicts with '" +
2660 name + "' argument. To allow such conflicts, add" +
2661 " CArgDescriptions::fOptionalSeparatorAllowConflict flag into" +
2662 " description of '" + name[0] + "'.");
2663 }
2664 break;
2665 }
2666 }
2667 }
2668
2669 /*
2670 if (dynamic_cast<CArgDescDefault*> (&arg) == 0) {
2671 continue;
2672 }
2673 */
2674
2675 try {
2676 arg.VerifyDefault();
2677 continue;
2678 } catch (const CException& e) {
2679 NCBI_RETHROW(e,CArgException,eConstraint,
2680 "Invalid default argument value");
2681 } catch (const exception& e) {
2682 NCBI_THROW(CArgException, eConstraint,
2683 string("Invalid default value: ") + e.what());
2684 }
2685 }
2686 }
2687
2688
CreateArgs(const CNcbiArguments & args) const2689 CArgs* CArgDescriptions::CreateArgs(const CNcbiArguments& args) const
2690 {
2691 const_cast<CArgDescriptions&>(*this).SetCurrentGroup(kEmptyStr);
2692 return CreateArgs(args.Size(), args);
2693 }
2694
2695
x_CheckAutoHelp(const string & arg) const2696 void CArgDescriptions::x_CheckAutoHelp(const string& arg) const
2697 {
2698 if (arg.compare(string("-") + s_AutoHelp) == 0) {
2699 if (m_AutoHelp) {
2700 NCBI_THROW(CArgHelpException,eHelp,kEmptyStr);
2701 }
2702 } else if (arg.compare(string("-") + s_AutoHelpFull) == 0) {
2703 NCBI_THROW(CArgHelpException,eHelpFull,kEmptyStr);
2704 } else if (arg.compare(string("-") + s_AutoHelpXml) == 0) {
2705 NCBI_THROW(CArgHelpException,eHelpXml,kEmptyStr);
2706 }
2707 }
2708
2709
2710 // (return TRUE if "arg2" was used)
x_CreateArg(const string & arg1,bool have_arg2,const string & arg2,unsigned * n_plain,CArgs & args) const2711 bool CArgDescriptions::x_CreateArg(const string& arg1,
2712 bool have_arg2, const string& arg2,
2713 unsigned* n_plain, CArgs& args) const
2714 {
2715 // Argument name
2716 string name;
2717 bool is_keyflag = false;
2718
2719 // Check if to start processing the args as positional
2720 if (*n_plain == kMax_UInt || m_PositionalMode == ePositionalMode_Loose) {
2721 // Check for the "--" delimiter
2722 if (arg1.compare("--") == 0) {
2723 if (*n_plain == kMax_UInt) {
2724 *n_plain = 0; // pos.args started
2725 }
2726 return false;
2727 }
2728 size_t argssofar = args.GetAll().size();
2729 // Check if argument has key/flag syntax
2730 if ((arg1.length() > 1) && arg1[0] == '-') {
2731 name = arg1.substr(1);
2732 TArgsCI it = m_Args.end();
2733 try {
2734 it = x_Find(name);
2735 } catch (const CArgException&) {
2736 }
2737 if (it == m_Args.end()) {
2738 if (m_OpeningArgs.size() > argssofar) {
2739 return x_CreateArg(arg1, m_OpeningArgs[argssofar], have_arg2, arg2, *n_plain, args);
2740 }
2741 }
2742 // Check for '=' in the arg1
2743 size_t eq = name.find('=');
2744 if (eq != NPOS) {
2745 name = name.substr(0, eq);
2746 }
2747 if (m_PositionalMode == ePositionalMode_Loose) {
2748 is_keyflag = x_Find(name) != m_Args.end();
2749 // If not a valid key/flag, treat it as a positional value
2750 if (!VerifyName(name) || !is_keyflag) {
2751 if (*n_plain == kMax_UInt) {
2752 *n_plain = 0; // pos.args started
2753 }
2754 }
2755 }
2756 } else {
2757 if (m_OpeningArgs.size() > argssofar) {
2758 return x_CreateArg(arg1, m_OpeningArgs[argssofar], have_arg2, arg2, *n_plain, args);
2759 }
2760 if (*n_plain == kMax_UInt) {
2761 *n_plain = 0; // pos.args started
2762 }
2763 }
2764 }
2765
2766 // Whether the value of "arg2" is used
2767 bool arg2_used = false;
2768
2769 // Extract name of positional argument
2770 if (*n_plain != kMax_UInt && !is_keyflag) {
2771 if (*n_plain < m_PosArgs.size()) {
2772 name = m_PosArgs[*n_plain]; // named positional argument
2773 } else {
2774 name = kEmptyStr; // unnamed (extra) positional argument
2775 }
2776 (*n_plain)++;
2777
2778 // Check for too many positional arguments
2779 if (kMax_UInt - m_nExtraOpt > m_nExtra + m_PosArgs.size() &&
2780 *n_plain > m_PosArgs.size() + m_nExtra + m_nExtraOpt) {
2781 NCBI_THROW(CArgException,eSynopsis,
2782 "Too many positional arguments (" +
2783 NStr::UIntToString(*n_plain) +
2784 "), the offending value: "+ arg1);
2785 }
2786 }
2787
2788 arg2_used = x_CreateArg(arg1, name, have_arg2, arg2, *n_plain, args);
2789
2790 // Success (also indicate whether one or two "raw" args have been used)
2791 return arg2_used;
2792 }
2793
2794
x_CreateArg(const string & arg1,const string & name_in,bool have_arg2,const string & arg2,unsigned n_plain,CArgs & args,bool update,CArgValue ** new_value) const2795 bool CArgDescriptions::x_CreateArg(const string& arg1,
2796 const string& name_in,
2797 bool have_arg2,
2798 const string& arg2,
2799 unsigned n_plain,
2800 CArgs& args,
2801 bool update,
2802 CArgValue** new_value) const
2803 {
2804 if (new_value)
2805 *new_value = 0;
2806
2807 string name(name_in);
2808 bool arg2_used = false;
2809 bool no_separator = false;
2810 bool eq_separator = false;
2811 bool negative = false;
2812
2813 // Get arg. description
2814 TArgsCI it;
2815 try {
2816 it = x_Find(name, &negative);
2817 } catch (const CArgException&) {
2818 // Suppress overzealous "invalid argument name" exceptions
2819 // in the no-separator case.
2820 if (m_NoSeparator.find(name[0]) != NPOS) {
2821 it = m_Args.end(); // avoid duplicating the logic below
2822 } else {
2823 throw;
2824 }
2825 }
2826
2827 // Check for '/' in the arg1
2828 bool confidential = it != m_Args.end() &&
2829 ((*it)->GetFlags() & CArgDescriptions::fConfidential) != 0;
2830 char conf_method = confidential ? 't' : '\0';
2831 size_t dash = name.rfind('-');
2832 if (it == m_Args.end() && dash != NPOS && dash != 0) {
2833 string test(name.substr(0, dash));
2834 string suffix(name.substr(dash+1));
2835 if (NStr::strcasecmp(suffix.c_str(), "file") == 0 ||
2836 NStr::strcasecmp(suffix.c_str(), "verbatim") == 0)
2837 {
2838 try {
2839 it = x_Find(test);
2840 } catch (const CArgException&) {
2841 it = m_Args.end();
2842 }
2843 if (it != m_Args.end()) {
2844 // verify that it has Confidential flag
2845 // and there is something after dash
2846 if (((*it)->GetFlags() & CArgDescriptions::fConfidential) &&
2847 name.size() > (dash+1)) {
2848 confidential = true;
2849 conf_method = name[dash+1];
2850 name = test;
2851 }
2852 }
2853 }
2854 }
2855
2856
2857 if (it == m_Args.end() && m_NoSeparator.find(name[0]) != NPOS) {
2858 it = x_Find(name.substr(0, 1), &negative);
2859 _ASSERT(it != m_Args.end());
2860 no_separator = true;
2861 }
2862 if (it == m_Args.end()) {
2863 if ( name.empty() ) {
2864 NCBI_THROW(CArgException,eInvalidArg,
2865 "Unexpected extra argument, at position # " +
2866 NStr::UIntToString(n_plain));
2867 } else {
2868 NCBI_THROW(CArgException,eInvalidArg,
2869 "Unknown argument: \"" + name + "\"");
2870 }
2871 }
2872 _ASSERT(*it);
2873
2874 const CArgDesc& arg = **it;
2875
2876 if ( s_IsFlag(arg) ) {
2877 x_CheckAutoHelp(arg1);
2878 }
2879
2880 // Check value separated by '='
2881 string arg_val;
2882 if ( s_IsKey(arg) && !confidential) {
2883 eq_separator = arg1.length() > name.length() &&
2884 (arg1[name.length() + 1] == '=');
2885 if ( !eq_separator ) {
2886 if ((arg.GetFlags() & fMandatorySeparator) != 0) {
2887 NCBI_THROW(CArgException,eInvalidArg,
2888 "Invalid argument: " + arg1);
2889 }
2890 no_separator |= (arg.GetFlags() & fOptionalSeparator) != 0 &&
2891 name.length() == 1 && arg1.length() > 2;
2892 }
2893 }
2894
2895 // Get argument value
2896 string value;
2897 if ( !eq_separator && !no_separator ) {
2898 if ( !s_IsKey(arg) || (confidential && conf_method == 't')) {
2899 value = arg1;
2900 }
2901 else {
2902 // <key> <value> arg -- advance from the arg.name to the arg.value
2903 if ( !have_arg2 ) {
2904
2905 // if update specified we try to add default value
2906 // (mandatory throws an exception out of the ProcessDefault())
2907 if (update) {
2908 CRef<CArgValue> arg_value(arg.ProcessDefault());
2909 // Add the value to "args"
2910 args.Add(arg_value, update);
2911 return arg2_used;
2912 }
2913
2914 NCBI_THROW(CArgException,eNoArg,s_ArgExptMsg(arg1,
2915 "Value is missing", kEmptyStr));
2916 }
2917 value = arg2;
2918 arg2_used = true;
2919 }
2920 }
2921 else {
2922 _ASSERT(s_IsKey(arg));
2923 if ( no_separator ) {
2924 arg_val = arg1.substr(2);
2925 }
2926 else {
2927 arg_val = arg1.substr(name.length() + 2);
2928 }
2929 value = arg_val;
2930 }
2931
2932 if (confidential) {
2933 switch (conf_method) {
2934 default:
2935 break;
2936 case 'f':
2937 case 'F':
2938 value = (value != "-") ? s_CArgs_ReadFromFile(name, value)
2939 : s_CArgs_ReadFromStdin(name, eNoEcho, "");
2940 break;
2941 case 't':
2942 case 'T':
2943 value = s_CArgs_ReadFromConsole(name, eNoEcho, nullptr);
2944 break;
2945 }
2946 }
2947
2948 CArgValue* av = 0;
2949 try {
2950 // Process the "raw" argument value into "CArgValue"
2951 if ( negative && s_IsFlag(arg) ) {
2952 // Negative flag - use default value rather than
2953 // normal one.
2954 av = arg.ProcessDefault();
2955 }
2956 else {
2957 av = arg.ProcessArgument(value);
2958 }
2959 }
2960 catch (const CArgException&) {
2961 const CArgErrorHandler* err_handler = arg.GetErrorHandler();
2962 if ( !err_handler ) {
2963 err_handler = m_ErrorHandler.GetPointerOrNull();
2964 }
2965 _ASSERT(err_handler);
2966 av = err_handler->HandleError(arg, value);
2967 }
2968
2969 if ( !av ) {
2970 return arg2_used;
2971 }
2972 CRef<CArgValue> arg_value(av);
2973
2974 if (new_value) {
2975 *new_value = av;
2976 }
2977
2978 bool allow_multiple = false;
2979 const CArgDescMandatory* adm =
2980 dynamic_cast<const CArgDescMandatory*>(&arg);
2981
2982 if (adm) {
2983 allow_multiple =
2984 (adm->GetFlags() & CArgDescriptions::fAllowMultiple) != 0;
2985 }
2986
2987 // Add the argument value to "args"
2988 args.Add(arg_value, update, allow_multiple);
2989
2990 return arg2_used;
2991 }
2992
2993
x_IsMultiArg(const string & name) const2994 bool CArgDescriptions::x_IsMultiArg(const string& name) const
2995 {
2996 TArgsCI it = x_Find(name);
2997 if (it == m_Args.end()) {
2998 return false;
2999 }
3000 const CArgDesc& arg = **it;
3001 const CArgDescMandatory* adm =
3002 dynamic_cast<const CArgDescMandatory*>(&arg);
3003
3004 if (!adm) {
3005 return false;
3006 }
3007 return (adm->GetFlags() & CArgDescriptions::fAllowMultiple) != 0;
3008 }
3009
3010
x_PostCheck(CArgs & args,unsigned int n_plain,EPostCheckCaller caller) const3011 void CArgDescriptions::x_PostCheck(CArgs& args,
3012 unsigned int n_plain,
3013 EPostCheckCaller caller)
3014 const
3015 {
3016 // If explicitly specified, printout usage and exit in case there
3017 // was no args passed to the application
3018 if (IsSetMiscFlag(fUsageIfNoArgs) && args.IsEmpty()) {
3019 NCBI_THROW(CArgHelpException, eHelpErr, kEmptyStr);
3020 }
3021
3022 // Check dependencies, create set of exclusions
3023 unsigned int nExtra = m_nExtra;
3024 string nameReq, nameExc;
3025 unsigned int nExtraReq = 0;
3026 unsigned int nExtraExc = 0;
3027 unsigned int nEx = 0;
3028 set<string> exclude;
3029 map<string,string> require;
3030 ITERATE(TDependencies, dep, m_Dependencies) {
3031 // Skip missing and empty arguments
3032 if (!args.Exist(dep->first) || !args[dep->first]) {
3033 continue;
3034 }
3035 switch ( dep->second.m_Dep ) {
3036 case eRequires:
3037 require.insert(make_pair(dep->second.m_Arg,dep->first));
3038 if (dep->second.m_Arg.at(0) == '#') {
3039 nEx = NStr::StringToUInt(
3040 CTempString(dep->second.m_Arg.data()+1, dep->second.m_Arg.size()-1));
3041 if (nEx > nExtraReq) {
3042 nExtraReq = nEx;
3043 nameReq = dep->first;
3044 }
3045 }
3046 break;
3047 case eExcludes:
3048 // Excluded exists and is not empty?
3049 if (args.Exist(dep->second.m_Arg) && args[dep->second.m_Arg]) {
3050 NCBI_THROW(CArgException, eConstraint,
3051 s_ArgExptMsg(dep->second.m_Arg,
3052 "Incompatible with argument", dep->first));
3053 }
3054 exclude.insert(dep->second.m_Arg);
3055 if (dep->second.m_Arg.at(0) == '#') {
3056 nEx = NStr::StringToUInt(
3057 CTempString(dep->second.m_Arg.data()+1, dep->second.m_Arg.size()-1));
3058 if (nEx > nExtraExc) {
3059 nExtraExc = nEx;
3060 nameExc = dep->first;
3061 }
3062 }
3063 break;
3064 }
3065 }
3066 if (nExtraReq != 0 && nExtraExc != 0 && nExtraReq >= nExtraExc) {
3067 NCBI_THROW(CArgException,eSynopsis,
3068 "Conflicting dependencies on unnamed positional arguments: " +
3069 nameReq + " requires #" + NStr::UIntToString(nExtraReq) + ", " +
3070 nameExc + " excludes #" + NStr::UIntToString(nExtraExc));
3071 }
3072 nExtra = max(nExtra, nExtraReq);
3073 if (nExtraExc > 0) {
3074 nExtra = max(nExtra, nExtraExc-1);
3075 }
3076
3077 // Check that all opening args are provided
3078 ITERATE (TPosArgs, it, m_OpeningArgs) {
3079 if (!args.Exist(*it)) {
3080 NCBI_THROW(CArgException,eNoArg, "Opening argument not provided: " + *it);
3081 }
3082 }
3083
3084 // Check if all mandatory unnamed positional arguments are provided
3085 // note that positional ones are filled first, no matter are they optional or not
3086 if (m_PosArgs.size() <= n_plain &&
3087 n_plain < m_PosArgs.size() + nExtra){
3088 NCBI_THROW(CArgException,eNoArg,
3089 "Too few (" + NStr::NumericToString(n_plain - m_PosArgs.size()) +
3090 ") unnamed positional arguments. Must define at least " +
3091 NStr::NumericToString(nExtra));
3092 }
3093
3094 // Compose an ordered list of args
3095 list<const CArgDesc*> def_args;
3096 ITERATE (TKeyFlagArgs, it, m_KeyFlagArgs) {
3097 const CArgDesc& arg = **x_Find(*it);
3098 def_args.push_back(&arg);
3099 }
3100 ITERATE (TPosArgs, it, m_PosArgs) {
3101 const CArgDesc& arg = **x_Find(*it);
3102 def_args.push_back(&arg);
3103 }
3104
3105 for (set< CConstRef<CArgDependencyGroup> >::const_iterator i = m_DependencyGroups.begin();
3106 i != m_DependencyGroups.end(); ++i) {
3107 i->GetPointer()->Evaluate(args);
3108 }
3109
3110 // Set default values (if available) for the arguments not defined
3111 // in the command line.
3112 ITERATE (list<const CArgDesc*>, it, def_args) {
3113 const CArgDesc& arg = **it;
3114
3115 // Nothing to do if defined in the command-line
3116 if ( args.Exist(arg.GetName()) ) {
3117 continue;
3118 }
3119
3120 if (require.find(arg.GetName()) != require.end() ) {
3121 string requester(require.find(arg.GetName())->second);
3122 // Required argument must be present
3123 NCBI_THROW(CArgException, eConstraint,
3124 s_ArgExptMsg(arg.GetName(),
3125 "Must be specified, as it is required by argument", requester));
3126 }
3127
3128 if (exclude.find(arg.GetName()) != exclude.end()) {
3129 CRef<CArg_ExcludedValue> arg_exvalue(
3130 new CArg_ExcludedValue(arg.GetName()));
3131 // Add the excluded-value argument to "args"
3132 args.Add(arg_exvalue);
3133 continue;
3134 }
3135 // Use default argument value
3136 try {
3137 CRef<CArgValue> arg_value(arg.ProcessDefault());
3138 // Add the value to "args"
3139 args.Add(arg_value);
3140 }
3141 catch (const CArgException&) {
3142 // mandatory argument, for CGI can be taken not only from the
3143 // command line but also from the HTTP request
3144 if (GetArgsType() != eCgiArgs || caller == eConvertKeys) {
3145 throw;
3146 }
3147 }
3148 }
3149 }
3150
3151
SetUsageContext(const string & usage_name,const string & usage_description,bool usage_sort_args,SIZE_TYPE usage_width)3152 void CArgDescriptions::SetUsageContext
3153 (const string& usage_name,
3154 const string& usage_description,
3155 bool usage_sort_args,
3156 SIZE_TYPE usage_width)
3157 {
3158 if (usage_name.empty()) {
3159 CNcbiApplicationAPI* app = CNcbiApplicationAPI::Instance();
3160 if (app) {
3161 m_UsageName = app->GetProgramDisplayName();
3162 }
3163 } else {
3164 m_UsageName = usage_name;
3165 }
3166 #if defined(NCBI_OS_MSWIN)
3167 NStr::TrimSuffixInPlace(m_UsageName, ".exe", NStr::eNocase);
3168 #endif
3169 m_UsageDescription = usage_description;
3170 usage_sort_args ? SetMiscFlags(fUsageSortArgs) : ResetMiscFlags(fUsageSortArgs);
3171
3172 const SIZE_TYPE kMinUsageWidth = 30;
3173 if (usage_width < kMinUsageWidth) {
3174 usage_width = kMinUsageWidth;
3175 ERR_POST_X(23, Warning <<
3176 "CArgDescriptions::SetUsageContext() -- usage_width=" <<
3177 usage_width << " adjusted to " << kMinUsageWidth);
3178 }
3179 m_UsageWidth = usage_width;
3180 }
3181
SetDetailedDescription(const string & usage_description)3182 void CArgDescriptions::SetDetailedDescription( const string& usage_description)
3183 {
3184 m_DetailedDescription = usage_description;
3185 }
3186
VerifyName(const string & name,bool extended)3187 bool CArgDescriptions::VerifyName(const string& name, bool extended)
3188 {
3189 if ( name.empty() )
3190 return true;
3191
3192 string::const_iterator it = name.begin();
3193 if (extended && *it == '#') {
3194 for (++it; it != name.end(); ++it) {
3195 if ( !isdigit((unsigned char)(*it)) ) {
3196 return false;
3197 }
3198 }
3199 } else {
3200 if (name[0] == '-') {
3201 // Prohibit names like '-' or '--foo'.
3202 // The second char must be present and may not be '-'.
3203 if (name.length() == 1 || name[1] == '-') {
3204 return false;
3205 }
3206 }
3207 for ( ; it != name.end(); ++it) {
3208 if ( !s_IsArgNameChar((unsigned char)(*it)) )
3209 return false;
3210 }
3211 }
3212
3213 return true;
3214 }
3215
3216
x_AddDesc(CArgDesc & arg)3217 void CArgDescriptions::x_AddDesc(CArgDesc& arg)
3218 {
3219 const string& name = arg.GetName();
3220
3221 if ( Exist(name) ) {
3222 NCBI_THROW(CArgException,eSynopsis,
3223 "Argument with this name is already defined: " + name);
3224 }
3225
3226 arg.SetGroup(m_CurrentGroup);
3227
3228 if (s_IsKey(arg) || s_IsFlag(arg)) {
3229 _ASSERT(find(m_KeyFlagArgs.begin(), m_KeyFlagArgs.end(), name)
3230 == m_KeyFlagArgs.end());
3231 m_KeyFlagArgs.push_back(name);
3232 } else if ( !s_IsAlias(arg) && !name.empty() ) {
3233 TPosArgs& container = s_IsOpening(arg) ? m_OpeningArgs : m_PosArgs;
3234 _ASSERT(find(container.begin(), container.end(), name)
3235 == container.end());
3236 if ( s_IsOptional(arg) ) {
3237 container.push_back(name);
3238 } else {
3239 TPosArgs::iterator it;
3240 for (it = container.begin(); it != container.end(); ++it) {
3241 if ( s_IsOptional(**x_Find(*it)) )
3242 break;
3243 }
3244 container.insert(it, name);
3245 }
3246 }
3247
3248 if ((arg.GetFlags() & fOptionalSeparator) != 0 &&
3249 name.length() == 1 &&
3250 s_IsKey(arg)) {
3251 m_NoSeparator += arg.GetName();
3252 }
3253
3254 arg.SetErrorHandler(m_ErrorHandler.GetPointerOrNull());
3255 m_Args.insert(&arg);
3256 }
3257
3258
PrintUsageIfNoArgs(bool do_print)3259 void CArgDescriptions::PrintUsageIfNoArgs(bool do_print)
3260 {
3261 do_print ? SetMiscFlags(fUsageIfNoArgs) : ResetMiscFlags(fUsageIfNoArgs);
3262 }
3263
3264
3265
3266 ///////////////////////////////////////////////////////
3267 // CArgDescriptions::PrintUsage()
3268
3269
s_PrintCommentBody(list<string> & arr,const string & s,SIZE_TYPE width)3270 static void s_PrintCommentBody(list<string>& arr, const string& s,
3271 SIZE_TYPE width)
3272 {
3273 NStr::Wrap(s, width, arr, NStr::fWrap_Hyphenate, " ");
3274 }
3275
3276
x_PrintComment(list<string> & arr,const CArgDesc & arg,SIZE_TYPE width) const3277 void CArgDescriptions::x_PrintComment(list<string>& arr,
3278 const CArgDesc& arg,
3279 SIZE_TYPE width) const
3280 {
3281 string intro = ' ' + arg.GetUsageSynopsis(true/*name_only*/);
3282
3283 // Print type (and value constraint, if any)
3284 string attr = arg.GetUsageCommentAttr();
3285 if ( !attr.empty() ) {
3286 char separator =
3287 (arg.GetFlags() & CArgDescriptions::fMandatorySeparator) ? '=' : ' ';
3288 string t;
3289 t += separator;
3290 t += '<' + attr + '>';
3291 if (arg.GetFlags() & CArgDescriptions::fConfidential) {
3292 arr.push_back( intro + " - read value interactively from console");
3293 arr.push_back( intro + "-file <" +
3294 CArgDescriptions::GetTypeName(CArgDescriptions::eInputFile) + "> - read value from file");
3295 t = "-verbatim";
3296 t += separator;
3297 t += '<' + attr + '>';
3298 }
3299 attr = t;
3300 }
3301
3302 // Add aliases for non-positional arguments
3303 list<string> negatives;
3304 if ( !s_IsPositional(arg) ) {
3305 ITERATE(CArgDescriptions::TArgs, it, m_Args) {
3306 const CArgDesc_Alias* alias =
3307 dynamic_cast<const CArgDesc_Alias*>(it->get());
3308 if (!alias || alias->GetAliasedName() != arg.GetName()) {
3309 continue;
3310 }
3311 if ( alias->GetNegativeFlag() ) {
3312 negatives.push_back(alias->GetName());
3313 }
3314 else {
3315 intro += ", -" + alias->GetName();
3316 }
3317 }
3318 }
3319
3320 intro += attr;
3321
3322 // Wrap intro if necessary...
3323 {{
3324 SIZE_TYPE indent = intro.find(", ");
3325 if (indent == NPOS || indent > width / 2) {
3326 indent = intro.find(" <");
3327 if (indent == NPOS || indent > width / 2) {
3328 indent = 0;
3329 }
3330 }
3331 NStr::Wrap(intro, width, arr, NStr::fWrap_Hyphenate,
3332 string(indent + 2, ' '), kEmptyStr);
3333 }}
3334
3335 // Print description
3336 s_PrintCommentBody(arr, arg.GetComment(), width);
3337
3338 // Print default value, if any
3339 const CArgDescDefault* dflt = dynamic_cast<const CArgDescDefault*> (&arg);
3340 if ( dflt ) {
3341 s_PrintCommentBody
3342 (arr, "Default = `" + dflt->GetDisplayValue() + '\'', width);
3343 }
3344
3345 // Print required/excluded args
3346 string require;
3347 string exclude;
3348 pair<TDependency_CI, TDependency_CI> dep_rg =
3349 m_Dependencies.equal_range(arg.GetName());
3350 for (TDependency_CI dep = dep_rg.first; dep != dep_rg.second; ++dep) {
3351 switch ( dep->second.m_Dep ) {
3352 case eRequires:
3353 if ( !require.empty() ) {
3354 require += ", ";
3355 }
3356 require += dep->second.m_Arg;
3357 break;
3358 case eExcludes:
3359 if ( !exclude.empty() ) {
3360 exclude += ", ";
3361 }
3362 exclude += dep->second.m_Arg;
3363 break;
3364 }
3365 }
3366 if ( !require.empty() ) {
3367 s_PrintCommentBody(arr, " * Requires: " + require, width);
3368 }
3369 if ( !exclude.empty() ) {
3370 s_PrintCommentBody(arr, " * Incompatible with: " + exclude, width);
3371 }
3372 if ( !negatives.empty() ) {
3373 string neg_info;
3374 ITERATE(list<string>, neg, negatives) {
3375 if ( !neg_info.empty() ) {
3376 neg_info += ", ";
3377 }
3378 neg_info += *neg;
3379 }
3380 SIZE_TYPE indent = neg_info.find(", ");
3381 if (indent == NPOS || indent > width / 2) {
3382 indent = 0;
3383 }
3384 neg_info = " -" + neg_info;
3385 NStr::Wrap(neg_info, width, arr, NStr::fWrap_Hyphenate,
3386 string(indent + 2, ' '), kEmptyStr);
3387
3388 // Print description
3389 string neg_comment = arg.GetComment();
3390 if ( neg_comment.empty() ) {
3391 neg_comment = "Negative for " + arg.GetName();
3392 }
3393 s_PrintCommentBody(arr, neg_comment, width);
3394 }
3395 if (s_IsFlag(arg)) {
3396 const CArgDesc_Flag* fl = dynamic_cast<const CArgDesc_Flag*>(&arg);
3397 if (fl && !fl->GetSetValue()) {
3398 s_PrintCommentBody(arr, "When the flag is present, its value is FALSE", width);
3399 }
3400 }
3401 }
3402
CPrintUsage(const CArgDescriptions & desc)3403 CArgDescriptions::CPrintUsage::CPrintUsage(const CArgDescriptions& desc)
3404 : m_desc(desc)
3405 {
3406 typedef list<const CArgDesc*> TList;
3407 typedef TList::iterator TListI;
3408
3409 m_args.push_front(0);
3410 TListI it_pos = m_args.begin();
3411
3412 // Opening
3413 for (TPosArgs::const_iterator name = desc.m_OpeningArgs.begin();
3414 name != desc.m_OpeningArgs.end(); ++name) {
3415 TArgsCI it = desc.x_Find(*name);
3416 _ASSERT(it != desc.m_Args.end());
3417 if (it->get()->GetFlags() & CArgDescriptions::fHidden)
3418 {
3419 continue;
3420 }
3421 m_args.insert(it_pos, it->get());
3422 }
3423
3424 // Keys and Flags
3425 if ( desc.IsSetMiscFlag(fUsageSortArgs) ) {
3426 // Alphabetically ordered,
3427 // mandatory keys to go first, then flags, then optional keys
3428 TListI& it_opt_keys = it_pos;
3429 TListI it_keys = m_args.insert(it_pos,nullptr);
3430 TListI it_flags = m_args.insert(it_pos,nullptr);
3431
3432 for (TArgsCI it = desc.m_Args.begin(); it != desc.m_Args.end(); ++it) {
3433 const CArgDesc* arg = it->get();
3434 if (it->get()->GetFlags() & CArgDescriptions::fHidden)
3435 {
3436 continue;
3437 }
3438
3439 if (dynamic_cast<const CArgDesc_KeyOpt*> (arg) ||
3440 dynamic_cast<const CArgDesc_KeyDef*> (arg)) {
3441 m_args.insert(it_opt_keys, arg);
3442 } else if (dynamic_cast<const CArgDesc_Key*> (arg)) {
3443 m_args.insert(it_keys, arg);
3444 } else if (dynamic_cast<const CArgDesc_Flag*> (arg)) {
3445 if ((desc.m_AutoHelp &&
3446 strcmp(s_AutoHelp, (arg->GetName()).c_str()) == 0) ||
3447 strcmp(s_AutoHelpFull, (arg->GetName()).c_str()) == 0)
3448 m_args.push_front(arg);
3449 else
3450 m_args.insert(it_flags, arg);
3451 }
3452 }
3453 m_args.erase(it_keys);
3454 m_args.erase(it_flags);
3455 } else {
3456 // Unsorted, just the order they were described by user
3457 for (TKeyFlagArgs::const_iterator name = desc.m_KeyFlagArgs.begin();
3458 name != desc.m_KeyFlagArgs.end(); ++name) {
3459 TArgsCI it = desc.x_Find(*name);
3460 _ASSERT(it != desc.m_Args.end());
3461 if (it->get()->GetFlags() & CArgDescriptions::fHidden)
3462 {
3463 continue;
3464 }
3465
3466 m_args.insert(it_pos, it->get());
3467 }
3468 }
3469
3470 // Positional
3471 for (TPosArgs::const_iterator name = desc.m_PosArgs.begin();
3472 name != desc.m_PosArgs.end(); ++name) {
3473 TArgsCI it = desc.x_Find(*name);
3474 _ASSERT(it != desc.m_Args.end());
3475 if (it->get()->GetFlags() & CArgDescriptions::fHidden)
3476 {
3477 continue;
3478 }
3479 const CArgDesc* arg = it->get();
3480
3481 // Mandatory args to go first, then go optional ones
3482 if (dynamic_cast<const CArgDesc_PosOpt*> (arg)) {
3483 m_args.push_back(arg);
3484 } else if (dynamic_cast<const CArgDesc_Pos*> (arg)) {
3485 m_args.insert(it_pos, arg);
3486 }
3487 }
3488 m_args.erase(it_pos);
3489
3490 // Extra
3491 {{
3492 TArgsCI it = desc.x_Find(kEmptyStr);
3493 if (it != desc.m_Args.end()) {
3494 if ((it->get()->GetFlags() & CArgDescriptions::fHidden) == 0)
3495 {
3496 m_args.push_back(it->get());
3497 }
3498 }
3499 }}
3500 }
3501
~CPrintUsage()3502 CArgDescriptions::CPrintUsage::~CPrintUsage()
3503 {
3504 }
3505
AddSynopsis(list<string> & arr,const string & intro,const string & prefix) const3506 void CArgDescriptions::CPrintUsage::AddSynopsis(list<string>& arr,
3507 const string& intro, const string& prefix) const
3508 {
3509 list<const CArgDesc*>::const_iterator it;
3510 list<string> syn;
3511 if (m_desc.GetArgsType() == eCgiArgs) {
3512 for (it = m_args.begin(); it != m_args.end(); ++it) {
3513 const CArgDescSynopsis* as =
3514 dynamic_cast<const CArgDescSynopsis*>(&**it);
3515
3516 if (as) {
3517 const string& name = (*it)->GetName();
3518 const string& synopsis = as->GetSynopsis();
3519 syn.push_back(name+"="+synopsis);
3520 }
3521 } // for
3522 NStr::WrapList(
3523 syn, m_desc.m_UsageWidth, "&", arr, 0, "?", " "+m_desc.m_UsageName+"?");
3524
3525 } else { // regular application
3526 if (!intro.empty()) {
3527 syn.push_back(intro);
3528 }
3529 for (it = m_args.begin(); it != m_args.end(); ++it) {
3530 if ( s_IsOptional(**it) || s_IsFlag(**it) ) {
3531 syn.push_back('[' + (*it)->GetUsageSynopsis() + ']');
3532 } else if ( s_IsPositional(**it) || s_IsOpening(**it) ) {
3533 syn.push_back('<' + (*it)->GetUsageSynopsis() + '>');
3534 } else {
3535 syn.push_back((*it)->GetUsageSynopsis());
3536 }
3537 } // for
3538 NStr::WrapList(syn, m_desc.m_UsageWidth, " ", arr, 0, prefix, " ");
3539 }
3540 }
3541
AddDescription(list<string> & arr,bool detailed) const3542 void CArgDescriptions::CPrintUsage::AddDescription(list<string>& arr, bool detailed) const
3543 {
3544 if ( m_desc.m_UsageDescription.empty() ) {
3545 arr.push_back("DESCRIPTION -- none");
3546 } else {
3547 arr.push_back("DESCRIPTION");
3548 s_PrintCommentBody(arr,
3549 (detailed && !m_desc.m_DetailedDescription.empty()) ?
3550 m_desc.m_DetailedDescription : m_desc.m_UsageDescription,
3551 m_desc.m_UsageWidth);
3552 }
3553 }
3554
AddCommandDescription(list<string> & arr,const string & cmd,const map<string,string> * aliases,size_t max_cmd_len,bool detailed) const3555 void CArgDescriptions::CPrintUsage::AddCommandDescription(list<string>& arr,
3556 const string& cmd, const map<string,string>* aliases,
3557 size_t max_cmd_len, bool detailed) const
3558 {
3559 if (detailed) {
3560 arr.push_back(kEmptyStr);
3561 }
3562 string cmd_full(cmd);
3563 if (aliases) {
3564 map<string,string>::const_iterator a = aliases->find(cmd);
3565 if (a != aliases->end()) {
3566 cmd_full += " (" + a->second + ")";
3567 }
3568 }
3569 cmd_full += string( max_cmd_len - cmd_full.size(), ' ');
3570 cmd_full += "- ";
3571 cmd_full += m_desc.m_UsageDescription;
3572 arr.push_back(string(" ")+ cmd_full);
3573 if (detailed) {
3574 AddSynopsis(arr,string(max_cmd_len+3,' '),string(max_cmd_len+6,' '));
3575 }
3576 }
3577
AddDetails(list<string> & arr) const3578 void CArgDescriptions::CPrintUsage::AddDetails(list<string>& arr) const
3579 {
3580 list<const CArgDesc*>::const_iterator it;
3581 list<string> req;
3582 list<string> opt;
3583 // Collect mandatory args
3584 for (it = m_args.begin(); it != m_args.end(); ++it) {
3585 if (s_IsOptional(**it) || s_IsFlag(**it)) {
3586 continue;
3587 }
3588 m_desc.x_PrintComment(req, **it, m_desc.m_UsageWidth);
3589 }
3590 // Collect optional args
3591 for (size_t grp = 0; grp < m_desc.m_ArgGroups.size(); ++grp) {
3592 list<string> grp_opt;
3593 bool group_not_empty = false;
3594 if ( !m_desc.m_ArgGroups[grp].empty() ) {
3595 NStr::Wrap(m_desc.m_ArgGroups[grp], m_desc.m_UsageWidth, grp_opt,
3596 NStr::fWrap_Hyphenate, " *** ");
3597 }
3598 for (it = m_args.begin(); it != m_args.end(); ++it) {
3599 if (!s_IsOptional(**it) && !s_IsFlag(**it)) {
3600 continue;
3601 }
3602 if ((*it)->GetGroup() == grp) {
3603 m_desc.x_PrintComment(grp_opt, **it, m_desc.m_UsageWidth);
3604 group_not_empty = true;
3605 }
3606 }
3607 if ( group_not_empty ) {
3608 opt.insert(opt.end(), grp_opt.begin(), grp_opt.end());
3609 opt.push_back(kEmptyStr);
3610 }
3611 }
3612 if ( !req.empty() ) {
3613 arr.push_back(kEmptyStr);
3614 arr.push_back("REQUIRED ARGUMENTS");
3615 arr.splice(arr.end(), req);
3616 }
3617 if ( !m_desc.m_nExtra && !opt.empty() ) {
3618 arr.push_back(kEmptyStr);
3619 arr.push_back("OPTIONAL ARGUMENTS");
3620 arr.splice(arr.end(), opt);
3621 }
3622
3623 // # of extra arguments
3624 if (m_desc.m_nExtra || (m_desc.m_nExtraOpt && m_desc.m_nExtraOpt != kMax_UInt)) {
3625 string str_extra = "NOTE: Specify ";
3626 if ( m_desc.m_nExtra ) {
3627 if (m_desc.m_nExtraOpt)
3628 str_extra += "at least ";
3629 str_extra += NStr::UIntToString(m_desc.m_nExtra);
3630 if (m_desc.m_nExtraOpt && m_desc.m_nExtraOpt != kMax_UInt) {
3631 str_extra += ", and ";
3632 }
3633 }
3634 if (m_desc.m_nExtraOpt && m_desc.m_nExtraOpt != kMax_UInt) {
3635 str_extra += "no more than ";
3636 str_extra += NStr::UIntToString(m_desc.m_nExtra + m_desc.m_nExtraOpt);
3637 }
3638 str_extra +=
3639 " argument" + string(&"s"[m_desc.m_nExtra
3640 + (m_desc.m_nExtraOpt != kMax_UInt
3641 ? m_desc.m_nExtraOpt : 0) == 1]) +
3642 " in \"....\"";
3643 s_PrintCommentBody(arr, str_extra, m_desc.m_UsageWidth);
3644 }
3645 if ( m_desc.m_nExtra && !opt.empty() ) {
3646 arr.push_back(kEmptyStr);
3647 arr.push_back("OPTIONAL ARGUMENTS");
3648 arr.splice(arr.end(), opt);
3649 }
3650
3651 if (!m_desc.m_DependencyGroups.empty()) {
3652 arr.push_back(kEmptyStr);
3653 arr.push_back("DEPENDENCY GROUPS");
3654 for (set< CConstRef<CArgDependencyGroup> >::const_iterator i = m_desc.m_DependencyGroups.begin();
3655 i != m_desc.m_DependencyGroups.end(); ++i) {
3656 i->GetPointer()->PrintUsage(arr, 0);
3657 }
3658 }
3659 }
3660
PrintUsage(string & str,bool detailed) const3661 string& CArgDescriptions::PrintUsage(string& str, bool detailed) const
3662 {
3663 CPrintUsage x(*this);
3664 list<string> arr;
3665
3666 // SYNOPSIS
3667 arr.push_back("USAGE");
3668 x.AddSynopsis(arr, m_UsageName," ");
3669
3670 // DESCRIPTION
3671 arr.push_back(kEmptyStr);
3672 x.AddDescription(arr, detailed);
3673
3674 // details
3675 if (detailed) {
3676 x.AddDetails(arr);
3677 } else {
3678 arr.push_back(kEmptyStr);
3679 arr.push_back("Use '-help' to print detailed descriptions of command line arguments");
3680 }
3681
3682 str += NStr::Join(arr, "\n");
3683 str += "\n";
3684 return str;
3685 }
3686
CPrintUsageXml(const CArgDescriptions & desc,CNcbiOstream & out)3687 CArgDescriptions::CPrintUsageXml::CPrintUsageXml(const CArgDescriptions& desc, CNcbiOstream& out)
3688 : m_desc(desc), m_out(out)
3689 {
3690 m_out << "<?xml version=\"1.0\"?>" << endl;
3691 m_out << "<" << "ncbi_application xmlns=\"ncbi:application\"" << endl
3692 << " xmlns:xs=\"http://www.w3.org/2001/XMLSchema-instance\"" << endl
3693 << " xs:schemaLocation=\"ncbi:application ncbi_application.xsd\"" << endl
3694 << ">" << endl;
3695 m_out << "<" << "program" << " type=\"";
3696 if (desc.GetArgsType() == eRegularArgs) {
3697 m_out << "regular";
3698 } else if (desc.GetArgsType() == eCgiArgs) {
3699 m_out << "cgi";
3700 } else {
3701 m_out << "UNKNOWN";
3702 }
3703 m_out << "\"" << ">" << endl;
3704 s_WriteXmlLine(m_out, "name", desc.m_UsageName);
3705 s_WriteXmlLine(m_out, "version",
3706 CNcbiApplication::Instance()->GetVersion().Print());
3707 s_WriteXmlLine(m_out, "description", desc.m_UsageDescription);
3708 s_WriteXmlLine(m_out, "detailed_description", desc.m_DetailedDescription);
3709 m_out << "</" << "program" << ">" << endl;
3710 }
~CPrintUsageXml()3711 CArgDescriptions::CPrintUsageXml::~CPrintUsageXml()
3712 {
3713 m_out << "</" << "ncbi_application" << ">" << endl;
3714 }
3715
PrintArguments(const CArgDescriptions & desc) const3716 void CArgDescriptions::CPrintUsageXml::PrintArguments(const CArgDescriptions& desc) const
3717 {
3718 m_out << "<" << "arguments";
3719 if (desc.GetPositionalMode() == ePositionalMode_Loose) {
3720 m_out << " positional_mode=\"loose\"";
3721 }
3722 m_out << ">" << endl;
3723
3724 string tag;
3725
3726 // opening
3727 ITERATE(TPosArgs, p, desc.m_OpeningArgs) {
3728 ITERATE (TArgs, a, desc.m_Args) {
3729 if ((**a).GetName() == *p) {
3730 tag = (*a)->PrintXml(m_out);
3731 m_out << "</" << tag << ">" << endl;
3732 }
3733 }
3734 }
3735 // positional
3736 ITERATE(TPosArgs, p, desc.m_PosArgs) {
3737 ITERATE (TArgs, a, desc.m_Args) {
3738 if ((**a).GetName() == *p) {
3739 tag = (*a)->PrintXml(m_out);
3740 desc.x_PrintAliasesAsXml(m_out, (*a)->GetName());
3741 m_out << "</" << tag << ">" << endl;
3742 }
3743 }
3744 }
3745 // keys
3746 ITERATE (TArgs, a, desc.m_Args) {
3747 if (s_IsKey(**a)) {
3748 tag = (*a)->PrintXml(m_out);
3749 desc.x_PrintAliasesAsXml(m_out, (*a)->GetName());
3750 m_out << "</" << tag << ">" << endl;
3751 }
3752 }
3753 // flags
3754 ITERATE (TArgs, a, desc.m_Args) {
3755 if (s_IsFlag(**a)) {
3756 tag = (*a)->PrintXml(m_out);
3757 desc.x_PrintAliasesAsXml(m_out, (*a)->GetName());
3758 desc.x_PrintAliasesAsXml(m_out, (*a)->GetName(), true);
3759 m_out << "</" << tag << ">" << endl;
3760 }
3761 }
3762 // extra positional
3763 ITERATE (TArgs, a, desc.m_Args) {
3764 if (s_IsPositional(**a) && (**a).GetName().empty()) {
3765 tag = (*a)->PrintXml(m_out);
3766 s_WriteXmlLine(m_out, "min_occurs", NStr::UIntToString(desc.m_nExtra));
3767 s_WriteXmlLine(m_out, "max_occurs", NStr::UIntToString(desc.m_nExtraOpt));
3768 m_out << "</" << tag << ">" << endl;
3769 }
3770 }
3771 if (!desc.m_Dependencies.empty()) {
3772 m_out << "<" << "dependencies" << ">" << endl;
3773 ITERATE(TDependencies, dep, desc.m_Dependencies) {
3774 if (dep->second.m_Dep == eRequires) {
3775 m_out << "<" << "first_requires_second" << ">" << endl;
3776 s_WriteXmlLine(m_out, "arg1", dep->first);
3777 s_WriteXmlLine(m_out, "arg2", dep->second.m_Arg);
3778 m_out << "</" << "first_requires_second" << ">" << endl;
3779 }
3780 }
3781 ITERATE(TDependencies, dep, desc.m_Dependencies) {
3782 if (dep->second.m_Dep == eExcludes) {
3783 m_out << "<" << "first_excludes_second" << ">" << endl;
3784 s_WriteXmlLine(m_out, "arg1", dep->first);
3785 s_WriteXmlLine(m_out, "arg2", dep->second.m_Arg);
3786 m_out << "</" << "first_excludes_second" << ">" << endl;
3787 }
3788 }
3789 m_out << "</" << "dependencies" << ">" << endl;
3790 }
3791
3792 for (set< CConstRef<CArgDependencyGroup> >::const_iterator i = m_desc.m_DependencyGroups.begin();
3793 i != m_desc.m_DependencyGroups.end(); ++i) {
3794 i->GetPointer()->PrintUsageXml(m_out);
3795 }
3796 m_out << "</" << "arguments" << ">" << endl;
3797 }
3798
PrintUsageXml(CNcbiOstream & out) const3799 void CArgDescriptions::PrintUsageXml(CNcbiOstream& out) const
3800 {
3801 CPrintUsageXml x(*this,out);
3802 x.PrintArguments(*this);
3803 }
3804
x_PrintAliasesAsXml(CNcbiOstream & out,const string & name,bool negated) const3805 void CArgDescriptions::x_PrintAliasesAsXml( CNcbiOstream& out,
3806 const string& name, bool negated /* =false*/) const
3807 {
3808 ITERATE (TArgs, a, m_Args) {
3809 if (s_IsAlias(**a)) {
3810 const CArgDesc_Alias& alias =
3811 dynamic_cast<const CArgDesc_Alias&>(**a);
3812 if (negated == alias.GetNegativeFlag()) {
3813 string tag = negated ? "negated_alias" : "alias";
3814 if (alias.GetAliasedName() == name) {
3815 s_WriteXmlLine(out, tag, alias.GetName());
3816 }
3817 }
3818 }
3819 }
3820 }
3821
3822 /////////////////////////////////////////////////////////////////////////////
3823 // CCommandArgDescriptions
3824
CCommandArgDescriptions(bool auto_help,CArgErrorHandler * err_handler,TCommandArgFlags cmd_flags)3825 CCommandArgDescriptions::CCommandArgDescriptions(
3826 bool auto_help, CArgErrorHandler* err_handler, TCommandArgFlags cmd_flags)
3827 : CArgDescriptions(auto_help,err_handler), m_Cmd_req(cmd_flags), m_CurrentCmdGroup(0)
3828 {
3829 }
3830
~CCommandArgDescriptions(void)3831 CCommandArgDescriptions::~CCommandArgDescriptions(void)
3832 {
3833 }
3834
SetCurrentCommandGroup(const string & group)3835 void CCommandArgDescriptions::SetCurrentCommandGroup(const string& group)
3836 {
3837 m_CurrentCmdGroup = x_GetCommandGroupIndex(group);
3838 if (m_CurrentCmdGroup == 0) {
3839 m_CmdGroups.push_back(group);
3840 m_CurrentCmdGroup = m_CmdGroups.size();
3841 }
3842 }
3843
x_IsCommandMandatory(void) const3844 bool CCommandArgDescriptions::x_IsCommandMandatory(void) const
3845 {
3846 return (m_Cmd_req & eCommandOptional) == 0;
3847 }
3848
x_GetCommandGroupIndex(const string & group) const3849 size_t CCommandArgDescriptions::x_GetCommandGroupIndex(const string& group) const
3850 {
3851 size_t i = 1;
3852 ITERATE( list<string>, g, m_CmdGroups) {
3853 if ( NStr::EqualNocase(*g, group) ) {
3854 return i;
3855 }
3856 ++i;
3857 }
3858 return 0;
3859 }
3860
AddCommand(const string & cmd,CArgDescriptions * description,const string & alias,ECommandFlags flags)3861 void CCommandArgDescriptions::AddCommand(
3862 const string& cmd, CArgDescriptions* description,
3863 const string& alias, ECommandFlags flags)
3864 {
3865
3866 string command( NStr::TruncateSpaces(cmd));
3867 if (command.empty()) {
3868 NCBI_THROW(CArgException,eSynopsis,
3869 "Command cannot be empty: "+ cmd);
3870 }
3871 if (description) {
3872 if ( m_AutoHelp ) {
3873 if (description->Exist(s_AutoHelp)) {
3874 description->Delete(s_AutoHelp);
3875 }
3876 }
3877 if (description->Exist(s_AutoHelpFull)) {
3878 description->Delete(s_AutoHelpFull);
3879 }
3880 if (description->Exist(s_AutoHelpXml)) {
3881 description->Delete(s_AutoHelpXml);
3882 }
3883
3884 if (m_CurrentCmdGroup == 0) {
3885 SetCurrentCommandGroup(kEmptyStr);
3886 }
3887 m_Commands.remove(command);
3888 if (flags != eHidden) {
3889 m_Commands.push_back(command);
3890 }
3891 m_Description[command] = description;
3892 m_Groups[command] = m_CurrentCmdGroup;
3893 if (!alias.empty()) {
3894 m_Aliases[command] = alias;
3895 } else {
3896 m_Aliases.erase(command);
3897 }
3898 } else {
3899 m_Commands.remove(command);
3900 m_Description.erase(command);
3901 m_Groups.erase(command);
3902 m_Aliases.erase(command);
3903 }
3904 }
3905
x_IdentifyCommand(const string & command) const3906 string CCommandArgDescriptions::x_IdentifyCommand(const string& command) const
3907 {
3908 if (m_Description.find(command) != m_Description.end()) {
3909 return command;
3910 }
3911 map<string,string>::const_iterator a = m_Aliases.begin();
3912 for ( ; a != m_Aliases.end(); ++a) {
3913 if (a->second == command) {
3914 return a->first;
3915 }
3916 }
3917 string cmd(command);
3918 if (cmd != "-") {
3919 vector<string> candidates;
3920 TDescriptions::const_iterator d;
3921 for (d = m_Description.begin(); d != m_Description.end(); ++d) {
3922 if (NStr::StartsWith(d->first,cmd)) {
3923 candidates.push_back(d->first);
3924 }
3925 }
3926 if (candidates.size() == 1) {
3927 return candidates.front();
3928 }
3929 }
3930 return kEmptyStr;
3931 }
3932
CreateArgs(const CNcbiArguments & argv) const3933 CArgs* CCommandArgDescriptions::CreateArgs(const CNcbiArguments& argv) const
3934 {
3935 if (argv.Size() > 1) {
3936 if (x_IsCommandMandatory()) {
3937 if (argv[1].empty()) {
3938 NCBI_THROW(CArgException,eInvalidArg, "Nonempty command is required");
3939 }
3940 x_CheckAutoHelp(argv[1]);
3941 }
3942 string command( x_IdentifyCommand(argv[1]));
3943 TDescriptions::const_iterator d;
3944 d = m_Description.find(command);
3945 if (d != m_Description.end()) {
3946 CNcbiArguments argv2(argv);
3947 argv2.Shift();
3948 m_Command = command;
3949 return d->second->CreateArgs(argv2)->SetCommand(command);
3950 }
3951 m_Command.clear();
3952 if (x_IsCommandMandatory() && !m_Description.empty()) {
3953 NCBI_THROW(CArgException,eInvalidArg, "Command not recognized: " + argv[1]);
3954 }
3955 }
3956 if (x_IsCommandMandatory() && !m_Description.empty()) {
3957 NCBI_THROW(CArgException,eInvalidArg, "Command is required");
3958 }
3959 return CArgDescriptions::CreateArgs(argv)->SetCommand(kEmptyStr);
3960 }
3961
PrintUsage(string & str,bool detailed) const3962 string& CCommandArgDescriptions::PrintUsage(string& str, bool detailed) const
3963 {
3964 const CArgDescriptions* argdesc = NULL;
3965 string cmd(m_Command);
3966 if (cmd.empty())
3967 {
3968 const CNcbiArguments& cmdargs = CNcbiApplication::Instance()->GetArguments();
3969 size_t cmdsize = cmdargs.Size();
3970 if (cmdsize > 2) {
3971 cmd = cmdargs[ 2];
3972 if (cmd.empty()) {
3973 if (!x_IsCommandMandatory()) {
3974 argdesc = this;
3975 }
3976 } else {
3977 cmd = x_IdentifyCommand(cmd);
3978 }
3979 }
3980 }
3981 TDescriptions::const_iterator d;
3982 if (!m_Description.empty()) {
3983 d = m_Description.find(cmd);
3984 if (d != m_Description.end()) {
3985 argdesc = d->second.get();
3986 }
3987 } else {
3988 argdesc = this;
3989 }
3990
3991 if (argdesc) {
3992 CPrintUsage x(*argdesc);
3993 list<string> arr;
3994
3995 /*
3996 if (!cmd.empty()) {
3997 arr.push_back("COMMAND");
3998 arr.push_back(string(" ")+cmd);
3999 arr.push_back(kEmptyStr);
4000 }
4001 */
4002
4003 // SYNOPSIS
4004 arr.push_back("USAGE");
4005 x.AddSynopsis(arr, m_UsageName + " " + cmd," ");
4006
4007 // DESCRIPTION
4008 arr.push_back(kEmptyStr);
4009 x.AddDescription(arr, detailed);
4010
4011 // details
4012 if (detailed) {
4013 x.AddDetails(arr);
4014 } else {
4015 arr.push_back(kEmptyStr);
4016 arr.push_back("Use '-help " + cmd + "' to print detailed descriptions of command line arguments");
4017 }
4018
4019 str += NStr::Join(arr, "\n");
4020 str += "\n";
4021 return str;
4022 }
4023
4024 CPrintUsage x(*this);
4025 list<string> arr;
4026
4027 arr.push_back("USAGE");
4028 arr.push_back(string(" ")+ m_UsageName +" <command> [options]");
4029 if (!x_IsCommandMandatory()) {
4030 arr.push_back("or");
4031 x.AddSynopsis(arr, m_UsageName," ");
4032 }
4033
4034 arr.push_back(kEmptyStr);
4035 x.AddDescription(arr, detailed);
4036
4037 // max command name length
4038 size_t max_cmd_len = 0;
4039 for (d = m_Description.begin(); d != m_Description.end(); ++d) {
4040 size_t alias_size=0;
4041 map<string,string>::const_iterator a = m_Aliases.find(d->first);
4042 if (a != m_Aliases.end()) {
4043 alias_size = a->second.size() + 3;
4044 }
4045 max_cmd_len = max(max_cmd_len, d->first.size() + alias_size);
4046 }
4047 max_cmd_len += 2;
4048
4049 list<string> cmds = m_Commands;
4050 if ((m_Cmd_req & eNoSortCommands)==0) {
4051 cmds.sort();
4052 }
4053 if (m_CmdGroups.size() > 1) {
4054 list<string> cmdgroups = m_CmdGroups;
4055 if ((m_Cmd_req & eNoSortGroups)==0) {
4056 cmdgroups.sort();
4057 }
4058 ITERATE( list<string>, gi, cmdgroups) {
4059 string grouptitle;
4060 bool titleprinted = false;
4061 if (gi->empty()) {
4062 grouptitle = "Commands";
4063 } else {
4064 grouptitle = *gi;
4065 }
4066 size_t group = x_GetCommandGroupIndex(*gi);
4067 ITERATE( list<string>, di, cmds) {
4068 map<string, size_t >::const_iterator j = m_Groups.find(*di);
4069 if (j != m_Groups.end() && j->second == group) {
4070 if (!titleprinted) {
4071 arr.push_back(kEmptyStr);
4072 arr.push_back(grouptitle + ":");
4073 titleprinted = true;
4074 }
4075 CPrintUsage y(*(m_Description.lower_bound(*di)->second));
4076 y.AddCommandDescription(arr, *di, &m_Aliases, max_cmd_len, detailed);
4077 }
4078 }
4079 ++group;
4080 }
4081 } else {
4082 arr.push_back(kEmptyStr);
4083 arr.push_back("AVAILABLE COMMANDS:");
4084 ITERATE( list<string>, di, cmds) {
4085 CPrintUsage y(*(m_Description.find(*di)->second));
4086 y.AddCommandDescription(arr, *di, &m_Aliases, max_cmd_len, detailed);
4087 }
4088 }
4089
4090 if (!x_IsCommandMandatory() && detailed) {
4091 arr.push_back(kEmptyStr);
4092 arr.push_back("Missing command:");
4093 x.AddDetails(arr);
4094 }
4095
4096 arr.push_back(kEmptyStr);
4097 if (m_AutoHelp) {
4098 arr.push_back("Use '-h command' to print help on a specific command");
4099 }
4100 arr.push_back("Use '-help command' to print detailed descriptions of command line arguments");
4101
4102 str += NStr::Join(arr, "\n");
4103 str += "\n";
4104 return str;
4105 }
4106
4107
PrintUsageXml(CNcbiOstream & out) const4108 void CCommandArgDescriptions::PrintUsageXml(CNcbiOstream& out) const
4109 {
4110 CPrintUsageXml x(*this,out);
4111 if (!x_IsCommandMandatory()) {
4112 x.PrintArguments(*this);
4113 }
4114 TDescriptions::const_iterator d;
4115 for (d = m_Description.begin(); d != m_Description.end(); ++d) {
4116 out << "<command>" << endl;
4117 out << "<name>" << d->first << "</name>" << endl;
4118 if (m_Aliases.find(d->first) != m_Aliases.end()) {
4119 out << "<alias>" << (m_Aliases.find(d->first)->second) << "</alias>" << endl;
4120 }
4121 s_WriteXmlLine(out, "description", d->second->m_UsageDescription);
4122 s_WriteXmlLine(out, "detailed_description", d->second->m_DetailedDescription);
4123 x.PrintArguments(*(d->second));
4124 out << "</command>" << endl;
4125 }
4126 if (m_CmdGroups.size() > 1) {
4127 out << "<command_groups>" << endl;
4128 for (const string& g : m_CmdGroups) {
4129 out << "<name>" << g << "</name>" << endl;
4130 size_t group = x_GetCommandGroupIndex(g);
4131 for (const string& c : m_Commands) {
4132 if (m_Groups.find(c) != m_Groups.end() && m_Groups.find(c)->second == group) {
4133 out << "<command>" << c << "</command>" << endl;
4134 }
4135 }
4136 }
4137 out << "</command_groups>" << endl;
4138 }
4139 }
4140
GetAllDescriptions(void)4141 list<CArgDescriptions*> CCommandArgDescriptions::GetAllDescriptions(void) {
4142 list<CArgDescriptions*> all( CArgDescriptions::GetAllDescriptions() );
4143 for (TDescriptions::const_iterator d = m_Description.begin(); d != m_Description.end(); ++d) {
4144 all.push_back( d->second.get());
4145 }
4146 return all;
4147 }
4148
4149 ///////////////////////////////////////////////////////
4150 ///////////////////////////////////////////////////////
4151 // CArgAllow::
4152 // CArgAllow_Symbols::
4153 // CArgAllow_String::
4154 // CArgAllow_Strings::
4155 // CArgAllow_Int8s::
4156 // CArgAllow_Integers::
4157 // CArgAllow_Doubles::
4158 //
4159
4160
4161 ///////////////////////////////////////////////////////
4162 // CArgAllow::
4163 //
4164
~CArgAllow(void)4165 CArgAllow::~CArgAllow(void)
4166 {
4167 }
4168
PrintUsageXml(CNcbiOstream &) const4169 void CArgAllow::PrintUsageXml(CNcbiOstream& ) const
4170 {
4171 }
4172
Clone(void) const4173 CArgAllow* CArgAllow::Clone(void) const
4174 {
4175 return NULL;
4176 }
4177
4178 ///////////////////////////////////////////////////////
4179 // s_IsSymbol() -- check if the symbol belongs to one of standard character
4180 // classes from <ctype.h>, or to user-defined symbol set
4181 //
4182
s_IsAllowedSymbol(unsigned char ch,CArgAllow_Symbols::ESymbolClass symbol_class,const string & symbol_set)4183 inline bool s_IsAllowedSymbol(unsigned char ch,
4184 CArgAllow_Symbols::ESymbolClass symbol_class,
4185 const string& symbol_set)
4186 {
4187 switch ( symbol_class ) {
4188 case CArgAllow_Symbols::eAlnum: return isalnum(ch) != 0;
4189 case CArgAllow_Symbols::eAlpha: return isalpha(ch) != 0;
4190 case CArgAllow_Symbols::eCntrl: return iscntrl(ch) != 0;
4191 case CArgAllow_Symbols::eDigit: return isdigit(ch) != 0;
4192 case CArgAllow_Symbols::eGraph: return isgraph(ch) != 0;
4193 case CArgAllow_Symbols::eLower: return islower(ch) != 0;
4194 case CArgAllow_Symbols::ePrint: return isprint(ch) != 0;
4195 case CArgAllow_Symbols::ePunct: return ispunct(ch) != 0;
4196 case CArgAllow_Symbols::eSpace: return isspace(ch) != 0;
4197 case CArgAllow_Symbols::eUpper: return isupper(ch) != 0;
4198 case CArgAllow_Symbols::eXdigit: return isxdigit(ch) != 0;
4199 case CArgAllow_Symbols::eUser:
4200 return symbol_set.find_first_of(ch) != NPOS;
4201 }
4202 _TROUBLE; return false;
4203 }
4204
4205
s_GetUsageSymbol(CArgAllow_Symbols::ESymbolClass symbol_class,const string & symbol_set)4206 static string s_GetUsageSymbol(CArgAllow_Symbols::ESymbolClass symbol_class,
4207 const string& symbol_set)
4208 {
4209 switch ( symbol_class ) {
4210 case CArgAllow_Symbols::eAlnum: return "alphanumeric";
4211 case CArgAllow_Symbols::eAlpha: return "alphabetic";
4212 case CArgAllow_Symbols::eCntrl: return "control symbol";
4213 case CArgAllow_Symbols::eDigit: return "decimal";
4214 case CArgAllow_Symbols::eGraph: return "graphical symbol";
4215 case CArgAllow_Symbols::eLower: return "lower case";
4216 case CArgAllow_Symbols::ePrint: return "printable";
4217 case CArgAllow_Symbols::ePunct: return "punctuation";
4218 case CArgAllow_Symbols::eSpace: return "space";
4219 case CArgAllow_Symbols::eUpper: return "upper case";
4220 case CArgAllow_Symbols::eXdigit: return "hexadecimal";
4221 case CArgAllow_Symbols::eUser:
4222 return "'" + NStr::PrintableString(symbol_set) + "'";
4223 }
4224 _TROUBLE; return kEmptyStr;
4225 }
4226
s_GetSymbolClass(CArgAllow_Symbols::ESymbolClass symbol_class)4227 static string s_GetSymbolClass(CArgAllow_Symbols::ESymbolClass symbol_class)
4228 {
4229 switch ( symbol_class ) {
4230 case CArgAllow_Symbols::eAlnum: return "Alnum";
4231 case CArgAllow_Symbols::eAlpha: return "Alpha";
4232 case CArgAllow_Symbols::eCntrl: return "Cntrl";
4233 case CArgAllow_Symbols::eDigit: return "Digit";
4234 case CArgAllow_Symbols::eGraph: return "Graph";
4235 case CArgAllow_Symbols::eLower: return "Lower";
4236 case CArgAllow_Symbols::ePrint: return "Print";
4237 case CArgAllow_Symbols::ePunct: return "Punct";
4238 case CArgAllow_Symbols::eSpace: return "Space";
4239 case CArgAllow_Symbols::eUpper: return "Upper";
4240 case CArgAllow_Symbols::eXdigit: return "Xdigit";
4241 case CArgAllow_Symbols::eUser: return "User";
4242 }
4243 _TROUBLE; return kEmptyStr;
4244 }
4245
4246
4247
4248 ///////////////////////////////////////////////////////
4249 // CArgAllow_Symbols::
4250 //
4251
CArgAllow_Symbols(ESymbolClass symbol_class)4252 CArgAllow_Symbols::CArgAllow_Symbols(ESymbolClass symbol_class)
4253 : CArgAllow()
4254 {
4255 Allow( symbol_class);
4256 return;
4257 }
4258
4259
CArgAllow_Symbols(const string & symbol_set)4260 CArgAllow_Symbols::CArgAllow_Symbols(const string& symbol_set)
4261 : CArgAllow()
4262 {
4263 Allow( symbol_set);
4264 return;
4265 }
4266
4267
Verify(const string & value) const4268 bool CArgAllow_Symbols::Verify(const string& value) const
4269 {
4270 if (value.length() != 1)
4271 return false;
4272
4273 ITERATE( set< TSymClass >, pi, m_SymClass) {
4274 if (s_IsAllowedSymbol(value[0], pi->first, pi->second)) {
4275 return true;
4276 }
4277 }
4278 return false;
4279 }
4280
4281
GetUsage(void) const4282 string CArgAllow_Symbols::GetUsage(void) const
4283 {
4284 string usage;
4285 ITERATE( set< TSymClass >, pi, m_SymClass) {
4286 if (!usage.empty()) {
4287 usage += ", or ";
4288 }
4289 usage += s_GetUsageSymbol(pi->first, pi->second);
4290 }
4291
4292 return "one symbol: " + usage;
4293 }
4294
PrintUsageXml(CNcbiOstream & out) const4295 void CArgAllow_Symbols::PrintUsageXml(CNcbiOstream& out) const
4296 {
4297 out << "<" << "Symbols" << ">" << endl;
4298 ITERATE( set< TSymClass >, pi, m_SymClass) {
4299 if (pi->first != eUser) {
4300 s_WriteXmlLine( out, "type", s_GetSymbolClass(pi->first).c_str());
4301 } else {
4302 ITERATE( string, p, pi->second) {
4303 string c;
4304 s_WriteXmlLine( out, "value", c.append(1,*p).c_str());
4305 }
4306 }
4307 }
4308 out << "</" << "Symbols" << ">" << endl;
4309 }
4310
4311 CArgAllow_Symbols&
Allow(CArgAllow_Symbols::ESymbolClass symbol_class)4312 CArgAllow_Symbols::Allow(CArgAllow_Symbols::ESymbolClass symbol_class)
4313 {
4314 m_SymClass.insert( make_pair(symbol_class, kEmptyStr) );
4315 return *this;
4316 }
4317
Allow(const string & symbol_set)4318 CArgAllow_Symbols& CArgAllow_Symbols::Allow(const string& symbol_set)
4319 {
4320 m_SymClass.insert( make_pair(eUser, symbol_set ));
4321 return *this;
4322 }
4323
Clone(void) const4324 CArgAllow* CArgAllow_Symbols::Clone(void) const
4325 {
4326 CArgAllow_Symbols* clone = new CArgAllow_Symbols;
4327 clone->m_SymClass = m_SymClass;
4328 return clone;
4329 }
4330
4331
4332
4333 ///////////////////////////////////////////////////////
4334 // CArgAllow_String::
4335 //
4336
CArgAllow_String(ESymbolClass symbol_class)4337 CArgAllow_String::CArgAllow_String(ESymbolClass symbol_class)
4338 : CArgAllow_Symbols(symbol_class)
4339 {
4340 return;
4341 }
4342
4343
CArgAllow_String(const string & symbol_set)4344 CArgAllow_String::CArgAllow_String(const string& symbol_set)
4345 : CArgAllow_Symbols(symbol_set)
4346 {
4347 return;
4348 }
4349
4350
Verify(const string & value) const4351 bool CArgAllow_String::Verify(const string& value) const
4352 {
4353 ITERATE( set< TSymClass >, pi, m_SymClass) {
4354 string::const_iterator it;
4355 for (it = value.begin(); it != value.end(); ++it) {
4356 if ( !s_IsAllowedSymbol(*it, pi->first, pi->second) )
4357 break;;
4358 }
4359 if (it == value.end()) {
4360 return true;
4361 }
4362 }
4363 return false;
4364 }
4365
4366
GetUsage(void) const4367 string CArgAllow_String::GetUsage(void) const
4368 {
4369 string usage;
4370 ITERATE( set< TSymClass >, pi, m_SymClass) {
4371 if (!usage.empty()) {
4372 usage += ", or ";
4373 }
4374 usage += s_GetUsageSymbol(pi->first, pi->second);
4375 }
4376
4377 return "to contain only symbols: " + usage;
4378 }
4379
4380
PrintUsageXml(CNcbiOstream & out) const4381 void CArgAllow_String::PrintUsageXml(CNcbiOstream& out) const
4382 {
4383 out << "<" << "String" << ">" << endl;
4384 ITERATE( set< TSymClass >, pi, m_SymClass) {
4385 if (pi->first != eUser) {
4386 s_WriteXmlLine( out, "type", s_GetSymbolClass(pi->first).c_str());
4387 } else {
4388 s_WriteXmlLine( out, "charset", pi->second.c_str());
4389 }
4390 }
4391 out << "</" << "String" << ">" << endl;
4392 }
4393
Clone(void) const4394 CArgAllow* CArgAllow_String::Clone(void) const
4395 {
4396 CArgAllow_String* clone = new CArgAllow_String;
4397 clone->m_SymClass = m_SymClass;
4398 return clone;
4399 }
4400
4401
4402
4403 ///////////////////////////////////////////////////////
4404 // CArgAllow_Strings::
4405 //
4406
CArgAllow_Strings(NStr::ECase use_case)4407 CArgAllow_Strings::CArgAllow_Strings(NStr::ECase use_case)
4408 : CArgAllow(),
4409 m_Strings(PNocase_Conditional(use_case))
4410 {
4411 return;
4412 }
4413
4414
CArgAllow_Strings(initializer_list<string> values,NStr::ECase use_case)4415 CArgAllow_Strings::CArgAllow_Strings(initializer_list<string> values, NStr::ECase use_case)
4416 : m_Strings(values, PNocase_Conditional(use_case))
4417 {
4418 }
4419
4420
Allow(const string & value)4421 CArgAllow_Strings* CArgAllow_Strings::Allow(const string& value)
4422 {
4423 m_Strings.insert(value);
4424 return this;
4425 }
4426
4427
Verify(const string & value) const4428 bool CArgAllow_Strings::Verify(const string& value) const
4429 {
4430 TStrings::const_iterator it = m_Strings.find(value);
4431 return it != m_Strings.end();
4432 }
4433
4434
4435 string
GetUsage(void) const4436 CArgAllow_Strings::GetUsage(void) const
4437 {
4438 if ( m_Strings.empty() ) {
4439 return "ERROR: Constraint with no values allowed(?!)";
4440 }
4441
4442 string str;
4443 TStrings::const_iterator it = m_Strings.begin();
4444 for (;;) {
4445 str += "`";
4446 str += *it;
4447
4448 ++it;
4449 if (it == m_Strings.end()) {
4450 str += "'";
4451 if ( m_Strings.key_comp()("a", "A") ) {
4452 str += " {case insensitive}";
4453 }
4454 break;
4455 }
4456 str += "', ";
4457 }
4458 return str;
4459 }
4460
4461
PrintUsageXml(CNcbiOstream & out) const4462 void CArgAllow_Strings::PrintUsageXml(CNcbiOstream& out) const
4463 {
4464 out << "<" << "Strings";
4465 out << " case_sensitive=\"";
4466 if ( m_Strings.key_comp()("a", "A") ) {
4467 out << "false";
4468 } else {
4469 out << "true";
4470 }
4471 out << "\">" << endl;
4472 ITERATE( TStrings, p, m_Strings) {
4473 s_WriteXmlLine( out, "value", (*p).c_str());
4474 }
4475 out << "</" << "Strings" << ">" << endl;
4476 }
4477
4478
AllowValue(const string & value)4479 CArgAllow_Strings& CArgAllow_Strings::AllowValue(const string& value)
4480 {
4481 return *Allow(value);
4482 }
4483
Clone(void) const4484 CArgAllow* CArgAllow_Strings::Clone(void) const
4485 {
4486 CArgAllow_Strings* clone = new CArgAllow_Strings(m_Strings.key_comp().GetCase());
4487 clone->m_Strings = m_Strings;
4488 return clone;
4489 }
4490
4491
4492 ///////////////////////////////////////////////////////
4493 // CArgAllow_Int8s::
4494 //
4495
CArgAllow_Int8s(Int8 x_)4496 CArgAllow_Int8s::CArgAllow_Int8s(Int8 x_)
4497 : CArgAllow()
4498 {
4499 Allow( x_);
4500 }
4501
CArgAllow_Int8s(Int8 x_min,Int8 x_max)4502 CArgAllow_Int8s::CArgAllow_Int8s(Int8 x_min, Int8 x_max)
4503 : CArgAllow()
4504 {
4505 AllowRange(x_min, x_max);
4506 }
4507
4508
Verify(const string & value) const4509 bool CArgAllow_Int8s::Verify(const string& value) const
4510 {
4511 Int8 val = s_StringToInt8(value);
4512 ITERATE( set< TInterval >, pi, m_MinMax) {
4513 if (pi->first <= val && val<= pi->second) {
4514 return true;
4515 }
4516 }
4517 return false;
4518 }
4519
4520
GetUsage(void) const4521 string CArgAllow_Int8s::GetUsage(void) const
4522 {
4523 if (m_MinMax.size() == 1) {
4524 Int8 x_min = m_MinMax.begin()->first;
4525 Int8 x_max = m_MinMax.begin()->second;
4526 if (x_min == x_max) {
4527 return NStr::Int8ToString(x_min);
4528 } else if (x_min == kMin_I8 && x_max != kMax_I8) {
4529 return string("less or equal to ") + NStr::Int8ToString(x_max);
4530 } else if (x_min != kMin_I8 && x_max == kMax_I8) {
4531 return string("greater or equal to ") + NStr::Int8ToString(x_min);
4532 } else if (x_min == kMin_I8 && x_max == kMax_I8) {
4533 return kEmptyStr;
4534 }
4535 }
4536 string usage;
4537 ITERATE( set< TInterval >, pi, m_MinMax) {
4538 if (!usage.empty()) {
4539 usage += ", ";
4540 }
4541 if (pi->first == pi->second) {
4542 usage += NStr::Int8ToString(pi->first);
4543 } else {
4544 usage += NStr::Int8ToString(pi->first) + ".." + NStr::Int8ToString(pi->second);
4545 }
4546
4547 }
4548 return usage;
4549 }
4550
4551
PrintUsageXml(CNcbiOstream & out) const4552 void CArgAllow_Int8s::PrintUsageXml(CNcbiOstream& out) const
4553 {
4554 string tag("Int8s");
4555 if (dynamic_cast<const CArgAllow_Integers*>(this) != 0) {
4556 tag = "Integers";
4557 }
4558 out << "<" << tag << ">" << endl;
4559 ITERATE( set< TInterval >, pi, m_MinMax) {
4560 s_WriteXmlLine( out, "min", NStr::Int8ToString(pi->first).c_str());
4561 s_WriteXmlLine( out, "max", NStr::Int8ToString(pi->second).c_str());
4562 }
4563 out << "</" << tag << ">" << endl;
4564 }
4565
AllowRange(Int8 from,Int8 to)4566 CArgAllow_Int8s& CArgAllow_Int8s::AllowRange(Int8 from, Int8 to)
4567 {
4568 m_MinMax.insert( make_pair(from,to) );
4569 return *this;
4570 }
4571
Allow(Int8 value)4572 CArgAllow_Int8s& CArgAllow_Int8s::Allow(Int8 value)
4573 {
4574 m_MinMax.insert( make_pair(value,value) );
4575 return *this;
4576 }
4577
Clone(void) const4578 CArgAllow* CArgAllow_Int8s::Clone(void) const
4579 {
4580 CArgAllow_Int8s* clone = new CArgAllow_Int8s;
4581 clone->m_MinMax = m_MinMax;
4582 return clone;
4583 }
4584
4585
4586
4587 ///////////////////////////////////////////////////////
4588 // CArgAllow_Integers::
4589 //
4590
CArgAllow_Integers(int x_)4591 CArgAllow_Integers::CArgAllow_Integers(int x_)
4592 : CArgAllow_Int8s(x_)
4593 {
4594 }
4595
CArgAllow_Integers(int x_min,int x_max)4596 CArgAllow_Integers::CArgAllow_Integers(int x_min, int x_max)
4597 : CArgAllow_Int8s(x_min, x_max)
4598 {
4599 }
4600
GetUsage(void) const4601 string CArgAllow_Integers::GetUsage(void) const
4602 {
4603 if (m_MinMax.size() == 1) {
4604 Int8 x_min = m_MinMax.begin()->first;
4605 Int8 x_max = m_MinMax.begin()->second;
4606 if (x_min == x_max) {
4607 return NStr::Int8ToString(x_min);
4608 } else if (x_min == kMin_Int && x_max != kMax_Int) {
4609 return string("less or equal to ") + NStr::Int8ToString(x_max);
4610 } else if (x_min != kMin_Int && x_max == kMax_Int) {
4611 return string("greater or equal to ") + NStr::Int8ToString(x_min);
4612 } else if (x_min == kMin_Int && x_max == kMax_Int) {
4613 return kEmptyStr;
4614 }
4615 }
4616 return CArgAllow_Int8s::GetUsage();;
4617 }
4618
Clone(void) const4619 CArgAllow* CArgAllow_Integers::Clone(void) const
4620 {
4621 CArgAllow_Integers* clone = new CArgAllow_Integers;
4622 clone->m_MinMax = m_MinMax;
4623 return clone;
4624 }
4625
4626
4627 ///////////////////////////////////////////////////////
4628 // CArgAllow_Doubles::
4629 //
4630
CArgAllow_Doubles(double x_value)4631 CArgAllow_Doubles::CArgAllow_Doubles(double x_value)
4632 : CArgAllow()
4633 {
4634 Allow(x_value);
4635 }
4636
CArgAllow_Doubles(double x_min,double x_max)4637 CArgAllow_Doubles::CArgAllow_Doubles(double x_min, double x_max)
4638 : CArgAllow()
4639 {
4640 AllowRange( x_min, x_max );
4641 }
4642
4643
Verify(const string & value) const4644 bool CArgAllow_Doubles::Verify(const string& value) const
4645 {
4646 double val = NStr::StringToDouble(value, NStr::fDecimalPosixOrLocal);
4647 ITERATE( set< TInterval >, pi, m_MinMax) {
4648 if (pi->first <= val && val<= pi->second) {
4649 return true;
4650 }
4651 }
4652 return false;
4653 }
4654
4655
GetUsage(void) const4656 string CArgAllow_Doubles::GetUsage(void) const
4657 {
4658 if (m_MinMax.size() == 1) {
4659 double x_min = m_MinMax.begin()->first;
4660 double x_max = m_MinMax.begin()->second;
4661 if (x_min == x_max) {
4662 return NStr::DoubleToString(x_min);
4663 } else if (x_min == kMin_Double && x_max != kMax_Double) {
4664 return string("less or equal to ") + NStr::DoubleToString(x_max);
4665 } else if (x_min != kMin_Double && x_max == kMax_Double) {
4666 return string("greater or equal to ") + NStr::DoubleToString(x_min);
4667 } else if (x_min == kMin_Double && x_max == kMax_Double) {
4668 return kEmptyStr;
4669 }
4670 }
4671 string usage;
4672 ITERATE( set< TInterval >, pi, m_MinMax) {
4673 if (!usage.empty()) {
4674 usage += ", ";
4675 }
4676 if (pi->first == pi->second) {
4677 usage += NStr::DoubleToString(pi->first);
4678 } else {
4679 usage += NStr::DoubleToString(pi->first) + ".." + NStr::DoubleToString(pi->second);
4680 }
4681
4682 }
4683 return usage;
4684 }
4685
4686
PrintUsageXml(CNcbiOstream & out) const4687 void CArgAllow_Doubles::PrintUsageXml(CNcbiOstream& out) const
4688 {
4689 out << "<" << "Doubles" << ">" << endl;
4690 ITERATE( set< TInterval >, pi, m_MinMax) {
4691 s_WriteXmlLine( out, "min", NStr::DoubleToString(pi->first).c_str());
4692 s_WriteXmlLine( out, "max", NStr::DoubleToString(pi->second).c_str());
4693 }
4694 out << "</" << "Doubles" << ">" << endl;
4695 }
4696
AllowRange(double from,double to)4697 CArgAllow_Doubles& CArgAllow_Doubles::AllowRange(double from, double to)
4698 {
4699 m_MinMax.insert( make_pair(from,to) );
4700 return *this;
4701 }
4702
Allow(double value)4703 CArgAllow_Doubles& CArgAllow_Doubles::Allow(double value)
4704 {
4705 m_MinMax.insert( make_pair(value,value) );
4706 return *this;
4707 }
4708
Clone(void) const4709 CArgAllow* CArgAllow_Doubles::Clone(void) const
4710 {
4711 CArgAllow_Doubles* clone = new CArgAllow_Doubles;
4712 clone->m_MinMax = m_MinMax;
4713 return clone;
4714 }
4715
4716
4717 /////////////////////////////////////////////////////////////////////////////
4718
Create(const string & name,const string & description)4719 CRef<CArgDependencyGroup> CArgDependencyGroup::Create(
4720 const string& name, const string& description)
4721 {
4722 CRef<CArgDependencyGroup> gr(new CArgDependencyGroup());
4723 gr->m_Name = name;
4724 gr->m_Description = description;
4725 return gr;
4726 }
4727
CArgDependencyGroup()4728 CArgDependencyGroup::CArgDependencyGroup()
4729 : m_MinMembers(0), m_MaxMembers(0)
4730 {
4731 }
4732
~CArgDependencyGroup(void)4733 CArgDependencyGroup::~CArgDependencyGroup(void)
4734 {
4735 }
4736
SetMinMembers(size_t min_members)4737 CArgDependencyGroup& CArgDependencyGroup::SetMinMembers(size_t min_members)
4738 {
4739 m_MinMembers = min_members;
4740 return *this;
4741 }
4742
SetMaxMembers(size_t max_members)4743 CArgDependencyGroup& CArgDependencyGroup::SetMaxMembers(size_t max_members)
4744 {
4745 m_MaxMembers = max_members;
4746 return *this;
4747 }
4748
Add(const string & arg_name,EInstantSet instant_set)4749 CArgDependencyGroup& CArgDependencyGroup::Add(const string& arg_name, EInstantSet instant_set)
4750 {
4751 m_Arguments[arg_name] = instant_set;
4752 return *this;
4753 }
4754
Add(CArgDependencyGroup * dep_group,EInstantSet instant_set)4755 CArgDependencyGroup& CArgDependencyGroup::Add(
4756 CArgDependencyGroup* dep_group, EInstantSet instant_set)
4757 {
4758 m_Groups[ CConstRef<CArgDependencyGroup>(dep_group)] = instant_set;
4759 return *this;
4760 }
4761
Evaluate(const CArgs & args) const4762 void CArgDependencyGroup::Evaluate( const CArgs& args) const
4763 {
4764 x_Evaluate(args, nullptr, nullptr);
4765 }
4766
x_Evaluate(const CArgs & args,string * arg_set,string * arg_unset) const4767 bool CArgDependencyGroup::x_Evaluate( const CArgs& args, string* arg_set, string* arg_unset) const
4768 {
4769 bool top_level = !arg_set || !arg_unset;
4770 bool has_instant_set = false;
4771 size_t count_set = 0;
4772 set<string> names_set, names_unset;
4773 string args_set, args_unset;
4774
4775 for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4776 i != m_Groups.end(); ++i) {
4777 string msg_set, msg_unset;
4778 if (i->first.GetPointer()->x_Evaluate(args, &msg_set, &msg_unset)) {
4779 ++count_set;
4780 has_instant_set = has_instant_set || (i->second == eInstantSet);
4781 names_set.insert(msg_set);
4782 } else {
4783 names_unset.insert(msg_unset);
4784 }
4785 }
4786 for (map<string, EInstantSet>::const_iterator i = m_Arguments.begin();
4787 i != m_Arguments.end(); ++i) {
4788 if (args.Exist(i->first)) {
4789 ++count_set;
4790 has_instant_set = has_instant_set || (i->second == eInstantSet);
4791 names_set.insert(i->first);
4792 } else {
4793 names_unset.insert(i->first);
4794 }
4795 }
4796 size_t count_total = m_Groups.size() + m_Arguments.size();
4797 size_t count_max = m_MaxMembers != 0 ? m_MaxMembers : count_total;
4798
4799 if (names_set.size() > 1) {
4800 args_set = "(" + NStr::Join(names_set, ", ") + ")";
4801 } else if (names_set.size() == 1) {
4802 args_set = *names_set.begin();
4803 }
4804
4805 if (names_unset.size() > 1) {
4806 args_unset = "(" + NStr::Join(names_unset, m_MinMembers <= 1 ? " | " : ", ") + ")";
4807 } else if (names_unset.size() == 1) {
4808 args_unset = *names_unset.begin();
4809 }
4810
4811 bool result = count_set != 0 || top_level;
4812 if (result) {
4813 if (count_set > count_max) {
4814 string msg("Argument conflict: ");
4815 msg += args_set + " may not be specified simultaneously";
4816 NCBI_THROW(CArgException, eConstraint, msg);
4817 }
4818 if (!has_instant_set && count_set < m_MinMembers) {
4819 string msg("Argument has no value: ");
4820 if (count_total != count_max) {
4821 msg += (m_MinMembers - count_set > 1) ? "some" : "one";
4822 msg += " of ";
4823 }
4824 msg += args_unset + " must be specified";
4825 NCBI_THROW(CArgException,eNoValue, msg);
4826 }
4827 }
4828 if (arg_set) {
4829 *arg_set = args_set;
4830 }
4831 if (arg_unset) {
4832 *arg_unset = args_unset;
4833 }
4834 return result;
4835 }
4836
PrintUsage(list<string> & arr,size_t offset) const4837 void CArgDependencyGroup::PrintUsage(list<string>& arr, size_t offset) const
4838 {
4839 arr.push_back(kEmptyStr);
4840 string off(2*offset,' ');
4841 string msg(off);
4842 msg += m_Name + ": {";
4843
4844 bool first = true;
4845 list<string> instant;
4846 for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4847 i != m_Groups.end(); ++i) {
4848 if (!first) {
4849 msg += ",";
4850 }
4851 first = false;
4852 msg += i->first.GetPointer()->m_Name;
4853 if (i->second == eInstantSet) {
4854 instant.push_back(i->first.GetPointer()->m_Name);
4855 }
4856 }
4857 for (map<string, EInstantSet>::const_iterator i = m_Arguments.begin();
4858 i != m_Arguments.end(); ++i) {
4859 if (!first) {
4860 msg += ",";
4861 }
4862 first = false;
4863 msg += i->first;
4864 if (i->second == eInstantSet) {
4865 instant.push_back(i->first);
4866 }
4867 }
4868 msg += "}";
4869 arr.push_back(msg);
4870 if (!m_Description.empty()) {
4871 msg = off;
4872 msg += m_Description;
4873 arr.push_back(msg);
4874 }
4875 size_t count_total = m_Groups.size() + m_Arguments.size();
4876 size_t count_max = m_MaxMembers != 0 ? m_MaxMembers : count_total;
4877
4878 msg = off + "in which ";
4879 size_t count = m_MinMembers;
4880 if (m_MinMembers == count_max) {
4881 msg += "exactly ";
4882 msg += NStr::NumericToString(m_MinMembers);
4883 } else if (count_max == count_total && m_MinMembers != 0) {
4884 msg += "at least ";
4885 msg += NStr::NumericToString(m_MinMembers);
4886 } else if (count_max != count_total && m_MinMembers == 0) {
4887 msg += "no more than ";
4888 msg += NStr::NumericToString(m_MaxMembers);
4889 count = m_MaxMembers;
4890 } else {
4891 msg += NStr::NumericToString(m_MinMembers);
4892 msg += " to ";
4893 msg += NStr::NumericToString(m_MaxMembers);
4894 count = m_MaxMembers;
4895 }
4896 msg += " element";
4897 if (count != 1) {
4898 msg += "s";
4899 }
4900 msg += " must be set";
4901 arr.push_back(msg);
4902
4903 if (!instant.empty()) {
4904 msg = off;
4905 msg += "Instant set: ";
4906 msg += NStr::Join(instant, ",");
4907 arr.push_back(msg);
4908 }
4909 for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4910 i != m_Groups.end(); ++i) {
4911 i->first.GetPointer()->PrintUsage(arr, offset+1);
4912 }
4913 }
4914
PrintUsageXml(CNcbiOstream & out) const4915 void CArgDependencyGroup::PrintUsageXml(CNcbiOstream& out) const
4916 {
4917 out << "<" << "dependencygroup" << ">" << endl;
4918 out << "<" << "name" << ">" << m_Name << "</" << "name" << ">" << endl;
4919 out << "<" << "description" << ">" << m_Description << "</" << "description" << ">" << endl;
4920
4921 for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4922 i != m_Groups.end(); ++i) {
4923 out << "<" << "group";
4924 if (i->second == eInstantSet) {
4925 out << " instantset=\"true\"";
4926 }
4927 out << ">" << i->first.GetPointer()->m_Name << "</" << "group" << ">" << endl;
4928 }
4929 for (map<string, EInstantSet>::const_iterator i = m_Arguments.begin();
4930 i != m_Arguments.end(); ++i) {
4931 out << "<" << "argument";
4932 if (i->second == eInstantSet) {
4933 out << " instantset=\"true\"";
4934 }
4935 out << ">" << i->first << "</" << "argument" << ">" << endl;
4936 }
4937 out << "<" << "minmembers" << ">" << m_MinMembers << "</" << "minmembers" << ">" << endl;
4938 out << "<" << "maxmembers" << ">" << m_MaxMembers << "</" << "maxmembers" << ">" << endl;
4939 for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4940 i != m_Groups.end(); ++i) {
4941 i->first.GetPointer()->PrintUsageXml(out);
4942 }
4943 out << "</" << "dependencygroup" << ">" << endl;
4944 }
4945
4946 ///////////////////////////////////////////////////////
4947 // CArgException
4948
GetErrCodeString(void) const4949 const char* CArgException::GetErrCodeString(void) const
4950 {
4951 switch (GetErrCode()) {
4952 case eInvalidArg: return "eInvalidArg";
4953 case eNoValue: return "eNoValue";
4954 case eExcludedValue: return "eExcludedValue";
4955 case eWrongCast: return "eWrongCast";
4956 case eConvert: return "eConvert";
4957 case eNoFile: return "eNoFile";
4958 case eConstraint: return "eConstraint";
4959 case eArgType: return "eArgType";
4960 case eNoArg: return "eNoArg";
4961 case eSynopsis: return "eSynopsis";
4962 default: return CException::GetErrCodeString();
4963 }
4964 }
4965
GetErrCodeString(void) const4966 const char* CArgHelpException::GetErrCodeString(void) const
4967 {
4968 switch (GetErrCode()) {
4969 case eHelp: return "eHelp";
4970 case eHelpFull: return "eHelpFull";
4971 case eHelpXml: return "eHelpXml";
4972 case eHelpErr: return "eHelpErr";
4973 default: return CException::GetErrCodeString();
4974 }
4975 }
4976
4977
4978 END_NCBI_SCOPE
4979