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