1 /*  $Id: ncbiargs.cpp 620642 2020-11-25 17:54:32Z lavr $
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     m_UsageName        = usage_name;
3159     m_UsageDescription = usage_description;
3160     usage_sort_args ? SetMiscFlags(fUsageSortArgs) : ResetMiscFlags(fUsageSortArgs);
3161 
3162     const SIZE_TYPE kMinUsageWidth = 30;
3163     if (usage_width < kMinUsageWidth) {
3164         usage_width = kMinUsageWidth;
3165         ERR_POST_X(23, Warning <<
3166                        "CArgDescriptions::SetUsageContext() -- usage_width=" <<
3167                        usage_width << " adjusted to " << kMinUsageWidth);
3168     }
3169     m_UsageWidth = usage_width;
3170 }
3171 
SetDetailedDescription(const string & usage_description)3172 void CArgDescriptions::SetDetailedDescription( const string& usage_description)
3173 {
3174     m_DetailedDescription = usage_description;
3175 }
3176 
VerifyName(const string & name,bool extended)3177 bool CArgDescriptions::VerifyName(const string& name, bool extended)
3178 {
3179     if ( name.empty() )
3180         return true;
3181 
3182     string::const_iterator it = name.begin();
3183     if (extended  &&  *it == '#') {
3184         for (++it;  it != name.end();  ++it) {
3185             if ( !isdigit((unsigned char)(*it)) ) {
3186                 return false;
3187             }
3188         }
3189     } else {
3190         if (name[0] == '-') {
3191             // Prohibit names like '-' or '--foo'.
3192             // The second char must be present and may not be '-'.
3193             if (name.length() == 1  ||  name[1] == '-') {
3194                 return false;
3195             }
3196         }
3197         for ( ;  it != name.end();  ++it) {
3198             if ( !s_IsArgNameChar((unsigned char)(*it)) )
3199                 return false;
3200         }
3201     }
3202 
3203     return true;
3204 }
3205 
3206 
x_AddDesc(CArgDesc & arg)3207 void CArgDescriptions::x_AddDesc(CArgDesc& arg)
3208 {
3209     const string& name = arg.GetName();
3210 
3211     if ( Exist(name) ) {
3212         NCBI_THROW(CArgException,eSynopsis,
3213             "Argument with this name is already defined: " + name);
3214     }
3215 
3216     arg.SetGroup(m_CurrentGroup);
3217 
3218     if (s_IsKey(arg)  ||  s_IsFlag(arg)) {
3219         _ASSERT(find(m_KeyFlagArgs.begin(), m_KeyFlagArgs.end(), name)
3220                 == m_KeyFlagArgs.end());
3221         m_KeyFlagArgs.push_back(name);
3222     } else if ( !s_IsAlias(arg)  &&  !name.empty() ) {
3223         TPosArgs& container = s_IsOpening(arg) ? m_OpeningArgs : m_PosArgs;
3224         _ASSERT(find(container.begin(), container.end(), name)
3225                 == container.end());
3226         if ( s_IsOptional(arg) ) {
3227             container.push_back(name);
3228         } else {
3229             TPosArgs::iterator it;
3230             for (it = container.begin();  it != container.end();  ++it) {
3231                 if ( s_IsOptional(**x_Find(*it)) )
3232                     break;
3233             }
3234             container.insert(it, name);
3235         }
3236     }
3237 
3238     if ((arg.GetFlags() & fOptionalSeparator) != 0  &&
3239         name.length() == 1  &&
3240         s_IsKey(arg)) {
3241         m_NoSeparator += arg.GetName();
3242     }
3243 
3244     arg.SetErrorHandler(m_ErrorHandler.GetPointerOrNull());
3245     m_Args.insert(&arg);
3246 }
3247 
3248 
PrintUsageIfNoArgs(bool do_print)3249 void CArgDescriptions::PrintUsageIfNoArgs(bool do_print)
3250 {
3251     do_print ? SetMiscFlags(fUsageIfNoArgs) : ResetMiscFlags(fUsageIfNoArgs);
3252 }
3253 
3254 
3255 
3256 ///////////////////////////////////////////////////////
3257 //  CArgDescriptions::PrintUsage()
3258 
3259 
s_PrintCommentBody(list<string> & arr,const string & s,SIZE_TYPE width)3260 static void s_PrintCommentBody(list<string>& arr, const string& s,
3261                                SIZE_TYPE width)
3262 {
3263     NStr::Wrap(s, width, arr, NStr::fWrap_Hyphenate, "   ");
3264 }
3265 
3266 
x_PrintComment(list<string> & arr,const CArgDesc & arg,SIZE_TYPE width) const3267 void CArgDescriptions::x_PrintComment(list<string>&   arr,
3268                                       const CArgDesc& arg,
3269                                       SIZE_TYPE       width) const
3270 {
3271     string intro = ' ' + arg.GetUsageSynopsis(true/*name_only*/);
3272 
3273     // Print type (and value constraint, if any)
3274     string attr = arg.GetUsageCommentAttr();
3275     if ( !attr.empty() ) {
3276         char separator =
3277             (arg.GetFlags() & CArgDescriptions::fMandatorySeparator) ? '=' : ' ';
3278         string t;
3279         t += separator;
3280         t += '<' + attr + '>';
3281         if (arg.GetFlags() &  CArgDescriptions::fConfidential) {
3282             arr.push_back( intro + "  - read value interactively from console");
3283             arr.push_back( intro + "-file <" +
3284                            CArgDescriptions::GetTypeName(CArgDescriptions::eInputFile) + "> - read value from file");
3285             t = "-verbatim";
3286             t += separator;
3287             t += '<' + attr + '>';
3288         }
3289         attr = t;
3290     }
3291 
3292     // Add aliases for non-positional arguments
3293     list<string> negatives;
3294     if ( !s_IsPositional(arg) ) {
3295         ITERATE(CArgDescriptions::TArgs, it, m_Args) {
3296             const CArgDesc_Alias* alias =
3297                 dynamic_cast<const CArgDesc_Alias*>(it->get());
3298             if (!alias  ||  alias->GetAliasedName() != arg.GetName()) {
3299                 continue;
3300             }
3301             if ( alias->GetNegativeFlag() ) {
3302                 negatives.push_back(alias->GetName());
3303             }
3304             else {
3305                 intro += ", -" + alias->GetName();
3306             }
3307         }
3308     }
3309 
3310     intro += attr;
3311 
3312     // Wrap intro if necessary...
3313     {{
3314         SIZE_TYPE indent = intro.find(", ");
3315         if (indent == NPOS  ||  indent > width / 2) {
3316             indent = intro.find(" <");
3317             if (indent == NPOS  ||  indent > width / 2) {
3318                 indent = 0;
3319             }
3320         }
3321         NStr::Wrap(intro, width, arr, NStr::fWrap_Hyphenate,
3322                    string(indent + 2, ' '), kEmptyStr);
3323     }}
3324 
3325     // Print description
3326     s_PrintCommentBody(arr, arg.GetComment(), width);
3327 
3328     // Print default value, if any
3329     const CArgDescDefault* dflt = dynamic_cast<const CArgDescDefault*> (&arg);
3330     if ( dflt ) {
3331         s_PrintCommentBody
3332             (arr, "Default = `" + dflt->GetDisplayValue() + '\'', width);
3333     }
3334 
3335     // Print required/excluded args
3336     string require;
3337     string exclude;
3338     pair<TDependency_CI, TDependency_CI> dep_rg =
3339         m_Dependencies.equal_range(arg.GetName());
3340     for (TDependency_CI dep = dep_rg.first; dep != dep_rg.second; ++dep) {
3341         switch ( dep->second.m_Dep ) {
3342         case eRequires:
3343             if ( !require.empty() ) {
3344                 require += ", ";
3345             }
3346             require += dep->second.m_Arg;
3347             break;
3348         case eExcludes:
3349             if ( !exclude.empty() ) {
3350                 exclude += ", ";
3351             }
3352             exclude += dep->second.m_Arg;
3353             break;
3354         }
3355     }
3356     if ( !require.empty() ) {
3357         s_PrintCommentBody(arr, " * Requires:  " + require, width);
3358     }
3359     if ( !exclude.empty() ) {
3360         s_PrintCommentBody(arr, " * Incompatible with:  " + exclude, width);
3361     }
3362     if ( !negatives.empty() ) {
3363         string neg_info;
3364         ITERATE(list<string>, neg, negatives) {
3365             if ( !neg_info.empty() ) {
3366                 neg_info += ", ";
3367             }
3368             neg_info += *neg;
3369         }
3370         SIZE_TYPE indent = neg_info.find(", ");
3371         if (indent == NPOS  ||  indent > width / 2) {
3372             indent = 0;
3373         }
3374         neg_info = " -" + neg_info;
3375         NStr::Wrap(neg_info, width, arr, NStr::fWrap_Hyphenate,
3376                 string(indent + 2, ' '), kEmptyStr);
3377 
3378         // Print description
3379         string neg_comment = arg.GetComment();
3380         if ( neg_comment.empty() ) {
3381             neg_comment = "Negative for " + arg.GetName();
3382         }
3383         s_PrintCommentBody(arr, neg_comment, width);
3384     }
3385     if (s_IsFlag(arg)) {
3386         const CArgDesc_Flag* fl = dynamic_cast<const CArgDesc_Flag*>(&arg);
3387         if (fl && !fl->GetSetValue()) {
3388             s_PrintCommentBody(arr, "When the flag is present, its value is FALSE", width);
3389         }
3390     }
3391 }
3392 
CPrintUsage(const CArgDescriptions & desc)3393 CArgDescriptions::CPrintUsage::CPrintUsage(const CArgDescriptions& desc)
3394     : m_desc(desc)
3395 {
3396     typedef list<const CArgDesc*> TList;
3397     typedef TList::iterator       TListI;
3398 
3399     m_args.push_front(0);
3400     TListI it_pos = m_args.begin();
3401 
3402     // Opening
3403     for (TPosArgs::const_iterator name = desc.m_OpeningArgs.begin();
3404          name != desc.m_OpeningArgs.end();  ++name) {
3405         TArgsCI it = desc.x_Find(*name);
3406         _ASSERT(it != desc.m_Args.end());
3407         if (it->get()->GetFlags() & CArgDescriptions::fHidden)
3408         {
3409             continue;
3410         }
3411         m_args.insert(it_pos, it->get());
3412     }
3413 
3414     // Keys and Flags
3415     if ( desc.IsSetMiscFlag(fUsageSortArgs) ) {
3416         // Alphabetically ordered,
3417         // mandatory keys to go first, then flags, then optional keys
3418         TListI& it_opt_keys = it_pos;
3419         TListI it_keys  = m_args.insert(it_pos,nullptr);
3420         TListI it_flags = m_args.insert(it_pos,nullptr);
3421 
3422         for (TArgsCI it = desc.m_Args.begin();  it != desc.m_Args.end();  ++it) {
3423             const CArgDesc* arg = it->get();
3424             if (it->get()->GetFlags() & CArgDescriptions::fHidden)
3425             {
3426                 continue;
3427             }
3428 
3429             if (dynamic_cast<const CArgDesc_KeyOpt*> (arg)  ||
3430                 dynamic_cast<const CArgDesc_KeyDef*> (arg)) {
3431                 m_args.insert(it_opt_keys, arg);
3432             } else if (dynamic_cast<const CArgDesc_Key*> (arg)) {
3433                 m_args.insert(it_keys, arg);
3434             } else if (dynamic_cast<const CArgDesc_Flag*> (arg)) {
3435                 if ((desc.m_AutoHelp &&
3436                     strcmp(s_AutoHelp,     (arg->GetName()).c_str()) == 0) ||
3437                     strcmp(s_AutoHelpFull, (arg->GetName()).c_str()) == 0)
3438                     m_args.push_front(arg);
3439                 else
3440                     m_args.insert(it_flags, arg);
3441             }
3442         }
3443         m_args.erase(it_keys);
3444         m_args.erase(it_flags);
3445     } else {
3446         // Unsorted, just the order they were described by user
3447         for (TKeyFlagArgs::const_iterator name = desc.m_KeyFlagArgs.begin();
3448              name != desc.m_KeyFlagArgs.end();  ++name) {
3449             TArgsCI it = desc.x_Find(*name);
3450             _ASSERT(it != desc.m_Args.end());
3451             if (it->get()->GetFlags() & CArgDescriptions::fHidden)
3452             {
3453                 continue;
3454             }
3455 
3456             m_args.insert(it_pos, it->get());
3457         }
3458     }
3459 
3460     // Positional
3461     for (TPosArgs::const_iterator name = desc.m_PosArgs.begin();
3462          name != desc.m_PosArgs.end();  ++name) {
3463         TArgsCI it = desc.x_Find(*name);
3464         _ASSERT(it != desc.m_Args.end());
3465         if (it->get()->GetFlags() & CArgDescriptions::fHidden)
3466         {
3467             continue;
3468         }
3469         const CArgDesc* arg = it->get();
3470 
3471         // Mandatory args to go first, then go optional ones
3472         if (dynamic_cast<const CArgDesc_PosOpt*> (arg)) {
3473             m_args.push_back(arg);
3474         } else if (dynamic_cast<const CArgDesc_Pos*> (arg)) {
3475             m_args.insert(it_pos, arg);
3476         }
3477     }
3478     m_args.erase(it_pos);
3479 
3480     // Extra
3481     {{
3482         TArgsCI it = desc.x_Find(kEmptyStr);
3483         if (it != desc.m_Args.end()) {
3484             if ((it->get()->GetFlags() & CArgDescriptions::fHidden) == 0)
3485             {
3486                 m_args.push_back(it->get());
3487             }
3488         }
3489     }}
3490 }
3491 
~CPrintUsage()3492 CArgDescriptions::CPrintUsage::~CPrintUsage()
3493 {
3494 }
3495 
AddSynopsis(list<string> & arr,const string & intro,const string & prefix) const3496 void CArgDescriptions::CPrintUsage::AddSynopsis(list<string>& arr,
3497     const string& intro, const string& prefix) const
3498 {
3499     list<const CArgDesc*>::const_iterator it;
3500     list<string> syn;
3501     if (m_desc.GetArgsType() == eCgiArgs) {
3502         for (it = m_args.begin();  it != m_args.end();  ++it) {
3503             const CArgDescSynopsis* as =
3504                 dynamic_cast<const CArgDescSynopsis*>(&**it);
3505 
3506             if (as) {
3507                 const string& name  = (*it)->GetName();
3508                 const string& synopsis  = as->GetSynopsis();
3509                 syn.push_back(name+"="+synopsis);
3510             }
3511         } // for
3512         NStr::WrapList(
3513             syn, m_desc.m_UsageWidth, "&", arr, 0, "?", "  "+m_desc.m_UsageName+"?");
3514 
3515     } else { // regular application
3516         if (!intro.empty()) {
3517             syn.push_back(intro);
3518         }
3519         for (it = m_args.begin();  it != m_args.end();  ++it) {
3520             if ( s_IsOptional(**it) || s_IsFlag(**it) ) {
3521                 syn.push_back('[' + (*it)->GetUsageSynopsis() + ']');
3522             } else if ( s_IsPositional(**it) || s_IsOpening(**it) ) {
3523                 syn.push_back('<' + (*it)->GetUsageSynopsis() + '>');
3524             } else {
3525                 syn.push_back((*it)->GetUsageSynopsis());
3526             }
3527         } // for
3528         NStr::WrapList(syn, m_desc.m_UsageWidth, " ", arr, 0, prefix, "  ");
3529     }
3530 }
3531 
AddDescription(list<string> & arr,bool detailed) const3532 void CArgDescriptions::CPrintUsage::AddDescription(list<string>& arr, bool detailed) const
3533 {
3534     if ( m_desc.m_UsageDescription.empty() ) {
3535         arr.push_back("DESCRIPTION    -- none");
3536     } else {
3537         arr.push_back("DESCRIPTION");
3538         s_PrintCommentBody(arr,
3539             (detailed  && !m_desc.m_DetailedDescription.empty()) ?
3540                 m_desc.m_DetailedDescription : m_desc.m_UsageDescription,
3541             m_desc.m_UsageWidth);
3542     }
3543 }
3544 
AddCommandDescription(list<string> & arr,const string & cmd,const map<string,string> * aliases,size_t max_cmd_len,bool detailed) const3545 void CArgDescriptions::CPrintUsage::AddCommandDescription(list<string>& arr,
3546     const string& cmd, const map<string,string>* aliases,
3547     size_t max_cmd_len, bool detailed) const
3548 {
3549     if (detailed) {
3550         arr.push_back(kEmptyStr);
3551     }
3552     string cmd_full(cmd);
3553     if (aliases) {
3554         map<string,string>::const_iterator a = aliases->find(cmd);
3555         if (a != aliases->end()) {
3556             cmd_full += " (" + a->second + ")";
3557         }
3558     }
3559     cmd_full += string( max_cmd_len - cmd_full.size(), ' ');
3560     cmd_full += "- ";
3561     cmd_full += m_desc.m_UsageDescription;
3562     arr.push_back(string("  ")+ cmd_full);
3563     if (detailed) {
3564         AddSynopsis(arr,string(max_cmd_len+3,' '),string(max_cmd_len+6,' '));
3565     }
3566 }
3567 
AddDetails(list<string> & arr) const3568 void CArgDescriptions::CPrintUsage::AddDetails(list<string>& arr) const
3569 {
3570     list<const CArgDesc*>::const_iterator it;
3571     list<string> req;
3572     list<string> opt;
3573     // Collect mandatory args
3574     for (it = m_args.begin();  it != m_args.end();  ++it) {
3575         if (s_IsOptional(**it)  ||  s_IsFlag(**it)) {
3576             continue;
3577         }
3578         m_desc.x_PrintComment(req, **it, m_desc.m_UsageWidth);
3579     }
3580     // Collect optional args
3581     for (size_t grp = 0;  grp < m_desc.m_ArgGroups.size();  ++grp) {
3582         list<string> grp_opt;
3583         bool group_not_empty = false;
3584         if ( !m_desc.m_ArgGroups[grp].empty() ) {
3585             NStr::Wrap(m_desc.m_ArgGroups[grp], m_desc.m_UsageWidth, grp_opt,
3586                 NStr::fWrap_Hyphenate, " *** ");
3587         }
3588         for (it = m_args.begin();  it != m_args.end();  ++it) {
3589             if (!s_IsOptional(**it)  &&  !s_IsFlag(**it)) {
3590                 continue;
3591             }
3592             if ((*it)->GetGroup() == grp) {
3593                 m_desc.x_PrintComment(grp_opt, **it, m_desc.m_UsageWidth);
3594                 group_not_empty = true;
3595             }
3596         }
3597         if ( group_not_empty ) {
3598             opt.insert(opt.end(), grp_opt.begin(), grp_opt.end());
3599             opt.push_back(kEmptyStr);
3600         }
3601     }
3602     if ( !req.empty() ) {
3603         arr.push_back(kEmptyStr);
3604         arr.push_back("REQUIRED ARGUMENTS");
3605         arr.splice(arr.end(), req);
3606     }
3607     if ( !m_desc.m_nExtra  &&  !opt.empty() ) {
3608         arr.push_back(kEmptyStr);
3609         arr.push_back("OPTIONAL ARGUMENTS");
3610         arr.splice(arr.end(), opt);
3611     }
3612 
3613     // # of extra arguments
3614     if (m_desc.m_nExtra  ||  (m_desc.m_nExtraOpt  &&  m_desc.m_nExtraOpt != kMax_UInt)) {
3615         string str_extra = "NOTE:  Specify ";
3616         if ( m_desc.m_nExtra ) {
3617             if (m_desc.m_nExtraOpt)
3618                 str_extra += "at least ";
3619             str_extra += NStr::UIntToString(m_desc.m_nExtra);
3620             if (m_desc.m_nExtraOpt  &&  m_desc.m_nExtraOpt != kMax_UInt) {
3621                 str_extra += ", and ";
3622             }
3623         }
3624         if (m_desc.m_nExtraOpt  &&  m_desc.m_nExtraOpt != kMax_UInt) {
3625             str_extra += "no more than ";
3626             str_extra += NStr::UIntToString(m_desc.m_nExtra + m_desc.m_nExtraOpt);
3627         }
3628         str_extra +=
3629             " argument" + string(&"s"[m_desc.m_nExtra
3630                                       + (m_desc.m_nExtraOpt != kMax_UInt
3631                                          ? m_desc.m_nExtraOpt : 0) == 1]) +
3632             " in \"....\"";
3633         s_PrintCommentBody(arr, str_extra, m_desc.m_UsageWidth);
3634     }
3635     if ( m_desc.m_nExtra  &&  !opt.empty() ) {
3636         arr.push_back(kEmptyStr);
3637         arr.push_back("OPTIONAL ARGUMENTS");
3638         arr.splice(arr.end(), opt);
3639     }
3640 
3641     if (!m_desc.m_DependencyGroups.empty()) {
3642         arr.push_back(kEmptyStr);
3643         arr.push_back("DEPENDENCY GROUPS");
3644         for (set< CConstRef<CArgDependencyGroup> >::const_iterator i = m_desc.m_DependencyGroups.begin();
3645             i != m_desc.m_DependencyGroups.end(); ++i) {
3646             i->GetPointer()->PrintUsage(arr, 0);
3647         }
3648     }
3649 }
3650 
PrintUsage(string & str,bool detailed) const3651 string& CArgDescriptions::PrintUsage(string& str, bool detailed) const
3652 {
3653     CPrintUsage x(*this);
3654     list<string> arr;
3655 
3656     // SYNOPSIS
3657     arr.push_back("USAGE");
3658     x.AddSynopsis(arr, m_UsageName,"    ");
3659 
3660     // DESCRIPTION
3661     arr.push_back(kEmptyStr);
3662     x.AddDescription(arr, detailed);
3663 
3664     // details
3665     if (detailed) {
3666         x.AddDetails(arr);
3667     } else {
3668         arr.push_back(kEmptyStr);
3669         arr.push_back("Use '-help' to print detailed descriptions of command line arguments");
3670     }
3671 
3672     str += NStr::Join(arr, "\n");
3673     str += "\n";
3674     return str;
3675 }
3676 
CPrintUsageXml(const CArgDescriptions & desc,CNcbiOstream & out)3677 CArgDescriptions::CPrintUsageXml::CPrintUsageXml(const CArgDescriptions& desc, CNcbiOstream& out)
3678     : m_desc(desc), m_out(out)
3679 {
3680     m_out << "<?xml version=\"1.0\"?>" << endl;
3681     m_out << "<" << "ncbi_application xmlns=\"ncbi:application\"" << endl
3682         << " xmlns:xs=\"http://www.w3.org/2001/XMLSchema-instance\"" << endl
3683         << " xs:schemaLocation=\"ncbi:application ncbi_application.xsd\"" << endl
3684         << ">" << endl;
3685     m_out << "<" << "program" << " type=\"";
3686     if (desc.GetArgsType() == eRegularArgs) {
3687         m_out << "regular";
3688     } else if (desc.GetArgsType() == eCgiArgs) {
3689         m_out << "cgi";
3690     } else {
3691         m_out << "UNKNOWN";
3692     }
3693     m_out << "\"" << ">" << endl;
3694     s_WriteXmlLine(m_out, "name", desc.m_UsageName);
3695     s_WriteXmlLine(m_out, "version",
3696         CNcbiApplication::Instance()->GetVersion().Print());
3697     s_WriteXmlLine(m_out, "description", desc.m_UsageDescription);
3698     s_WriteXmlLine(m_out, "detailed_description", desc.m_DetailedDescription);
3699     m_out << "</" << "program" << ">" << endl;
3700 }
~CPrintUsageXml()3701 CArgDescriptions::CPrintUsageXml::~CPrintUsageXml()
3702 {
3703     m_out << "</" << "ncbi_application" << ">" << endl;
3704 }
3705 
PrintArguments(const CArgDescriptions & desc) const3706 void CArgDescriptions::CPrintUsageXml::PrintArguments(const CArgDescriptions& desc) const
3707 {
3708     m_out << "<" << "arguments";
3709     if (desc.GetPositionalMode() == ePositionalMode_Loose) {
3710         m_out << " positional_mode=\"loose\"";
3711     }
3712     m_out << ">" << endl;
3713 
3714     string tag;
3715 
3716 // opening
3717     ITERATE(TPosArgs, p, desc.m_OpeningArgs) {
3718         ITERATE (TArgs, a, desc.m_Args) {
3719             if ((**a).GetName() == *p) {
3720                 tag = (*a)->PrintXml(m_out);
3721                 m_out << "</" << tag << ">" << endl;
3722             }
3723         }
3724     }
3725 // positional
3726     ITERATE(TPosArgs, p, desc.m_PosArgs) {
3727         ITERATE (TArgs, a, desc.m_Args) {
3728             if ((**a).GetName() == *p) {
3729                 tag = (*a)->PrintXml(m_out);
3730                 desc.x_PrintAliasesAsXml(m_out, (*a)->GetName());
3731                 m_out << "</" << tag << ">" << endl;
3732             }
3733         }
3734     }
3735 // keys
3736     ITERATE (TArgs, a, desc.m_Args) {
3737         if (s_IsKey(**a)) {
3738             tag = (*a)->PrintXml(m_out);
3739             desc.x_PrintAliasesAsXml(m_out, (*a)->GetName());
3740             m_out << "</" << tag << ">" << endl;
3741         }
3742     }
3743 // flags
3744     ITERATE (TArgs, a, desc.m_Args) {
3745         if (s_IsFlag(**a)) {
3746             tag = (*a)->PrintXml(m_out);
3747             desc.x_PrintAliasesAsXml(m_out, (*a)->GetName());
3748             desc.x_PrintAliasesAsXml(m_out, (*a)->GetName(), true);
3749             m_out << "</" << tag << ">" << endl;
3750         }
3751     }
3752 // extra positional
3753     ITERATE (TArgs, a, desc.m_Args) {
3754         if (s_IsPositional(**a) && (**a).GetName().empty()) {
3755             tag = (*a)->PrintXml(m_out);
3756             s_WriteXmlLine(m_out, "min_occurs", NStr::UIntToString(desc.m_nExtra));
3757             s_WriteXmlLine(m_out, "max_occurs", NStr::UIntToString(desc.m_nExtraOpt));
3758             m_out << "</" << tag << ">" << endl;
3759         }
3760     }
3761     if (!desc.m_Dependencies.empty()) {
3762         m_out << "<" << "dependencies" << ">" << endl;
3763         ITERATE(TDependencies, dep, desc.m_Dependencies) {
3764             if (dep->second.m_Dep == eRequires) {
3765                 m_out << "<" << "first_requires_second" << ">" << endl;
3766                 s_WriteXmlLine(m_out, "arg1", dep->first);
3767                 s_WriteXmlLine(m_out, "arg2", dep->second.m_Arg);
3768                 m_out << "</" << "first_requires_second" << ">" << endl;
3769             }
3770         }
3771         ITERATE(TDependencies, dep, desc.m_Dependencies) {
3772             if (dep->second.m_Dep == eExcludes) {
3773                 m_out << "<" << "first_excludes_second" << ">" << endl;
3774                 s_WriteXmlLine(m_out, "arg1", dep->first);
3775                 s_WriteXmlLine(m_out, "arg2", dep->second.m_Arg);
3776                 m_out << "</" << "first_excludes_second" << ">" << endl;
3777             }
3778         }
3779         m_out << "</" << "dependencies" << ">" << endl;
3780     }
3781 
3782     for (set< CConstRef<CArgDependencyGroup> >::const_iterator i = m_desc.m_DependencyGroups.begin();
3783         i != m_desc.m_DependencyGroups.end(); ++i) {
3784         i->GetPointer()->PrintUsageXml(m_out);
3785     }
3786     m_out << "</" << "arguments" << ">" << endl;
3787 }
3788 
PrintUsageXml(CNcbiOstream & out) const3789 void CArgDescriptions::PrintUsageXml(CNcbiOstream& out) const
3790 {
3791     CPrintUsageXml x(*this,out);
3792     x.PrintArguments(*this);
3793 }
3794 
x_PrintAliasesAsXml(CNcbiOstream & out,const string & name,bool negated) const3795 void CArgDescriptions::x_PrintAliasesAsXml( CNcbiOstream& out,
3796     const string& name, bool negated /* =false*/) const
3797 {
3798     ITERATE (TArgs, a, m_Args) {
3799         if (s_IsAlias(**a)) {
3800             const CArgDesc_Alias& alias =
3801                 dynamic_cast<const CArgDesc_Alias&>(**a);
3802             if (negated == alias.GetNegativeFlag()) {
3803                 string tag = negated ? "negated_alias" : "alias";
3804                 if (alias.GetAliasedName() == name) {
3805                     s_WriteXmlLine(out, tag, alias.GetName());
3806                 }
3807             }
3808         }
3809     }
3810 }
3811 
3812 /////////////////////////////////////////////////////////////////////////////
3813 // CCommandArgDescriptions
3814 
CCommandArgDescriptions(bool auto_help,CArgErrorHandler * err_handler,TCommandArgFlags cmd_flags)3815 CCommandArgDescriptions::CCommandArgDescriptions(
3816     bool auto_help, CArgErrorHandler* err_handler, TCommandArgFlags cmd_flags)
3817     : CArgDescriptions(auto_help,err_handler), m_Cmd_req(cmd_flags), m_CurrentCmdGroup(0)
3818 {
3819 }
3820 
~CCommandArgDescriptions(void)3821 CCommandArgDescriptions::~CCommandArgDescriptions(void)
3822 {
3823 }
3824 
SetCurrentCommandGroup(const string & group)3825 void CCommandArgDescriptions::SetCurrentCommandGroup(const string& group)
3826 {
3827     m_CurrentCmdGroup = x_GetCommandGroupIndex(group);
3828     if (m_CurrentCmdGroup == 0) {
3829         m_CmdGroups.push_back(group);
3830         m_CurrentCmdGroup = m_CmdGroups.size();
3831     }
3832 }
3833 
x_IsCommandMandatory(void) const3834 bool CCommandArgDescriptions::x_IsCommandMandatory(void) const
3835 {
3836     return (m_Cmd_req & eCommandOptional) == 0;
3837 }
3838 
x_GetCommandGroupIndex(const string & group) const3839 size_t CCommandArgDescriptions::x_GetCommandGroupIndex(const string& group) const
3840 {
3841     size_t i = 1;
3842     ITERATE( list<string>, g, m_CmdGroups) {
3843         if ( NStr::EqualNocase(*g, group) ) {
3844             return i;
3845         }
3846         ++i;
3847     }
3848     return 0;
3849 }
3850 
AddCommand(const string & cmd,CArgDescriptions * description,const string & alias,ECommandFlags flags)3851 void CCommandArgDescriptions::AddCommand(
3852     const string& cmd, CArgDescriptions* description,
3853     const string& alias, ECommandFlags flags)
3854 {
3855 
3856     string command( NStr::TruncateSpaces(cmd));
3857     if (command.empty()) {
3858         NCBI_THROW(CArgException,eSynopsis,
3859             "Command cannot be empty: "+ cmd);
3860     }
3861     if (description) {
3862         if ( m_AutoHelp ) {
3863             if (description->Exist(s_AutoHelp)) {
3864                 description->Delete(s_AutoHelp);
3865             }
3866         }
3867         if (description->Exist(s_AutoHelpFull)) {
3868             description->Delete(s_AutoHelpFull);
3869         }
3870         if (description->Exist(s_AutoHelpXml)) {
3871             description->Delete(s_AutoHelpXml);
3872         }
3873 
3874         if (m_CurrentCmdGroup == 0) {
3875             SetCurrentCommandGroup(kEmptyStr);
3876         }
3877         m_Commands.remove(command);
3878         if (flags != eHidden) {
3879             m_Commands.push_back(command);
3880         }
3881         m_Description[command] = description;
3882         m_Groups[command] = m_CurrentCmdGroup;
3883         if (!alias.empty()) {
3884             m_Aliases[command] = alias;
3885         } else {
3886             m_Aliases.erase(command);
3887         }
3888     } else {
3889         m_Commands.remove(command);
3890         m_Description.erase(command);
3891         m_Groups.erase(command);
3892         m_Aliases.erase(command);
3893     }
3894 }
3895 
x_IdentifyCommand(const string & command) const3896 string CCommandArgDescriptions::x_IdentifyCommand(const string& command) const
3897 {
3898     if (m_Description.find(command) != m_Description.end()) {
3899         return command;
3900     }
3901     map<string,string>::const_iterator a = m_Aliases.begin();
3902     for ( ; a != m_Aliases.end(); ++a) {
3903         if (a->second == command) {
3904             return a->first;
3905         }
3906     }
3907     string cmd(command);
3908     if (cmd != "-") {
3909         vector<string> candidates;
3910         TDescriptions::const_iterator d;
3911         for (d = m_Description.begin(); d != m_Description.end(); ++d) {
3912             if (NStr::StartsWith(d->first,cmd)) {
3913                 candidates.push_back(d->first);
3914             }
3915         }
3916         if (candidates.size() == 1) {
3917             return candidates.front();
3918         }
3919     }
3920     return kEmptyStr;
3921 }
3922 
CreateArgs(const CNcbiArguments & argv) const3923 CArgs* CCommandArgDescriptions::CreateArgs(const CNcbiArguments& argv) const
3924 {
3925     if (argv.Size() > 1) {
3926         if (x_IsCommandMandatory()) {
3927             if (argv[1].empty()) {
3928                 NCBI_THROW(CArgException,eInvalidArg, "Nonempty command is required");
3929             }
3930             x_CheckAutoHelp(argv[1]);
3931         }
3932         string command( x_IdentifyCommand(argv[1]));
3933         TDescriptions::const_iterator d;
3934         d = m_Description.find(command);
3935         if (d != m_Description.end()) {
3936             CNcbiArguments argv2(argv);
3937             argv2.Shift();
3938             m_Command = command;
3939             return d->second->CreateArgs(argv2)->SetCommand(command);
3940         }
3941         m_Command.clear();
3942         if (x_IsCommandMandatory() && !m_Description.empty()) {
3943             NCBI_THROW(CArgException,eInvalidArg, "Command not recognized: " + argv[1]);
3944         }
3945     }
3946     if (x_IsCommandMandatory() && !m_Description.empty()) {
3947         NCBI_THROW(CArgException,eInvalidArg, "Command is required");
3948     }
3949     return CArgDescriptions::CreateArgs(argv)->SetCommand(kEmptyStr);
3950 }
3951 
PrintUsage(string & str,bool detailed) const3952 string& CCommandArgDescriptions::PrintUsage(string& str, bool detailed) const
3953 {
3954     const CArgDescriptions* argdesc = NULL;
3955     string cmd(m_Command);
3956     if (cmd.empty())
3957     {
3958         const CNcbiArguments& cmdargs = CNcbiApplication::Instance()->GetArguments();
3959         size_t cmdsize = cmdargs.Size();
3960         if (cmdsize > 2) {
3961             cmd = cmdargs[ 2];
3962             if (cmd.empty()) {
3963                 if (!x_IsCommandMandatory()) {
3964                     argdesc = this;
3965                 }
3966             } else {
3967                 cmd = x_IdentifyCommand(cmd);
3968             }
3969         }
3970     }
3971     TDescriptions::const_iterator d;
3972     if (!m_Description.empty()) {
3973         d = m_Description.find(cmd);
3974         if (d != m_Description.end()) {
3975             argdesc = d->second.get();
3976         }
3977     } else {
3978         argdesc = this;
3979     }
3980 
3981     if (argdesc) {
3982         CPrintUsage x(*argdesc);
3983         list<string> arr;
3984 
3985 /*
3986         if (!cmd.empty()) {
3987             arr.push_back("COMMAND");
3988             arr.push_back(string("    ")+cmd);
3989             arr.push_back(kEmptyStr);
3990         }
3991 */
3992 
3993         // SYNOPSIS
3994         arr.push_back("USAGE");
3995         x.AddSynopsis(arr, m_UsageName + " " + cmd,"    ");
3996 
3997         // DESCRIPTION
3998         arr.push_back(kEmptyStr);
3999         x.AddDescription(arr, detailed);
4000 
4001         // details
4002         if (detailed) {
4003             x.AddDetails(arr);
4004         } else {
4005             arr.push_back(kEmptyStr);
4006             arr.push_back("Use '-help " + cmd + "' to print detailed descriptions of command line arguments");
4007         }
4008 
4009         str += NStr::Join(arr, "\n");
4010         str += "\n";
4011         return str;
4012     }
4013 
4014     CPrintUsage x(*this);
4015     list<string> arr;
4016 
4017     arr.push_back("USAGE");
4018     arr.push_back(string("  ")+ m_UsageName +" <command> [options]");
4019     if (!x_IsCommandMandatory()) {
4020         arr.push_back("or");
4021         x.AddSynopsis(arr, m_UsageName,"    ");
4022     }
4023 
4024     arr.push_back(kEmptyStr);
4025     x.AddDescription(arr, detailed);
4026 
4027 // max command name length
4028     size_t max_cmd_len = 0;
4029     for (d = m_Description.begin(); d != m_Description.end(); ++d) {
4030         size_t alias_size=0;
4031         map<string,string>::const_iterator a = m_Aliases.find(d->first);
4032         if (a != m_Aliases.end()) {
4033             alias_size = a->second.size() + 3;
4034         }
4035         max_cmd_len = max(max_cmd_len, d->first.size() + alias_size);
4036     }
4037     max_cmd_len += 2;
4038 
4039     list<string> cmds = m_Commands;
4040     if ((m_Cmd_req & eNoSortCommands)==0) {
4041         cmds.sort();
4042     }
4043     if (m_CmdGroups.size() > 1) {
4044         list<string> cmdgroups = m_CmdGroups;
4045         if ((m_Cmd_req & eNoSortGroups)==0) {
4046             cmdgroups.sort();
4047         }
4048         ITERATE( list<string>, gi, cmdgroups) {
4049             string grouptitle;
4050             bool titleprinted = false;
4051             if (gi->empty()) {
4052                 grouptitle = "Commands";
4053             } else {
4054                 grouptitle = *gi;
4055             }
4056             size_t group = x_GetCommandGroupIndex(*gi);
4057             ITERATE( list<string>, di, cmds) {
4058                 map<string, size_t >::const_iterator j = m_Groups.find(*di);
4059                 if (j != m_Groups.end() && j->second == group) {
4060                     if (!titleprinted) {
4061                         arr.push_back(kEmptyStr);
4062                         arr.push_back(grouptitle + ":");
4063                         titleprinted = true;
4064                     }
4065                     CPrintUsage y(*(m_Description.lower_bound(*di)->second));
4066                     y.AddCommandDescription(arr, *di, &m_Aliases, max_cmd_len, detailed);
4067                 }
4068             }
4069             ++group;
4070         }
4071     } else {
4072         arr.push_back(kEmptyStr);
4073         arr.push_back("AVAILABLE COMMANDS:");
4074         ITERATE( list<string>, di, cmds) {
4075             CPrintUsage y(*(m_Description.find(*di)->second));
4076             y.AddCommandDescription(arr, *di, &m_Aliases, max_cmd_len, detailed);
4077         }
4078     }
4079 
4080     if (!x_IsCommandMandatory() && detailed) {
4081         arr.push_back(kEmptyStr);
4082         arr.push_back("Missing command:");
4083         x.AddDetails(arr);
4084     }
4085 
4086     arr.push_back(kEmptyStr);
4087     if (m_AutoHelp) {
4088         arr.push_back("Use '-h command' to print help on a specific command");
4089     }
4090     arr.push_back("Use '-help command' to print detailed descriptions of command line arguments");
4091 
4092     str += NStr::Join(arr, "\n");
4093     str += "\n";
4094     return str;
4095 }
4096 
4097 
PrintUsageXml(CNcbiOstream & out) const4098 void CCommandArgDescriptions::PrintUsageXml(CNcbiOstream& out) const
4099 {
4100     CPrintUsageXml x(*this,out);
4101     if (!x_IsCommandMandatory()) {
4102         x.PrintArguments(*this);
4103     }
4104     TDescriptions::const_iterator d;
4105     for (d = m_Description.begin(); d != m_Description.end(); ++d) {
4106         out << "<command>" << endl;
4107         out << "<name>" << d->first << "</name>" << endl;
4108         if (m_Aliases.find(d->first) != m_Aliases.end()) {
4109             out << "<alias>" << (m_Aliases.find(d->first)->second) << "</alias>" << endl;
4110         }
4111         s_WriteXmlLine(out, "description", d->second->m_UsageDescription);
4112         s_WriteXmlLine(out, "detailed_description", d->second->m_DetailedDescription);
4113         x.PrintArguments(*(d->second));
4114         out << "</command>" << endl;
4115     }
4116     if (m_CmdGroups.size() > 1) {
4117         out << "<command_groups>" << endl;
4118         for (const string& g : m_CmdGroups) {
4119             out << "<name>" << g << "</name>" << endl;
4120             size_t group = x_GetCommandGroupIndex(g);
4121             for (const string& c : m_Commands) {
4122                 if (m_Groups.find(c) != m_Groups.end() && m_Groups.find(c)->second == group) {
4123                     out << "<command>" << c << "</command>" << endl;
4124                 }
4125             }
4126         }
4127         out << "</command_groups>" << endl;
4128     }
4129 }
4130 
GetAllDescriptions(void)4131 list<CArgDescriptions*> CCommandArgDescriptions::GetAllDescriptions(void) {
4132     list<CArgDescriptions*> all( CArgDescriptions::GetAllDescriptions() );
4133     for (TDescriptions::const_iterator d = m_Description.begin(); d != m_Description.end(); ++d) {
4134         all.push_back( d->second.get());
4135     }
4136     return all;
4137 }
4138 
4139 ///////////////////////////////////////////////////////
4140 ///////////////////////////////////////////////////////
4141 // CArgAllow::
4142 //   CArgAllow_Symbols::
4143 //   CArgAllow_String::
4144 //   CArgAllow_Strings::
4145 //   CArgAllow_Int8s::
4146 //   CArgAllow_Integers::
4147 //   CArgAllow_Doubles::
4148 //
4149 
4150 
4151 ///////////////////////////////////////////////////////
4152 //  CArgAllow::
4153 //
4154 
~CArgAllow(void)4155 CArgAllow::~CArgAllow(void)
4156 {
4157 }
4158 
PrintUsageXml(CNcbiOstream &) const4159 void CArgAllow::PrintUsageXml(CNcbiOstream& ) const
4160 {
4161 }
4162 
Clone(void) const4163 CArgAllow* CArgAllow::Clone(void) const
4164 {
4165     return NULL;
4166 }
4167 
4168 ///////////////////////////////////////////////////////
4169 //  s_IsSymbol() -- check if the symbol belongs to one of standard character
4170 //                  classes from <ctype.h>, or to user-defined symbol set
4171 //
4172 
s_IsAllowedSymbol(unsigned char ch,CArgAllow_Symbols::ESymbolClass symbol_class,const string & symbol_set)4173 inline bool s_IsAllowedSymbol(unsigned char                   ch,
4174                               CArgAllow_Symbols::ESymbolClass symbol_class,
4175                               const string&                   symbol_set)
4176 {
4177     switch ( symbol_class ) {
4178     case CArgAllow_Symbols::eAlnum:   return isalnum(ch) != 0;
4179     case CArgAllow_Symbols::eAlpha:   return isalpha(ch) != 0;
4180     case CArgAllow_Symbols::eCntrl:   return iscntrl(ch) != 0;
4181     case CArgAllow_Symbols::eDigit:   return isdigit(ch) != 0;
4182     case CArgAllow_Symbols::eGraph:   return isgraph(ch) != 0;
4183     case CArgAllow_Symbols::eLower:   return islower(ch) != 0;
4184     case CArgAllow_Symbols::ePrint:   return isprint(ch) != 0;
4185     case CArgAllow_Symbols::ePunct:   return ispunct(ch) != 0;
4186     case CArgAllow_Symbols::eSpace:   return isspace(ch) != 0;
4187     case CArgAllow_Symbols::eUpper:   return isupper(ch) != 0;
4188     case CArgAllow_Symbols::eXdigit:  return isxdigit(ch) != 0;
4189     case CArgAllow_Symbols::eUser:
4190         return symbol_set.find_first_of(ch) != NPOS;
4191     }
4192     _TROUBLE;  return false;
4193 }
4194 
4195 
s_GetUsageSymbol(CArgAllow_Symbols::ESymbolClass symbol_class,const string & symbol_set)4196 static string s_GetUsageSymbol(CArgAllow_Symbols::ESymbolClass symbol_class,
4197                                const string&                   symbol_set)
4198 {
4199     switch ( symbol_class ) {
4200     case CArgAllow_Symbols::eAlnum:   return "alphanumeric";
4201     case CArgAllow_Symbols::eAlpha:   return "alphabetic";
4202     case CArgAllow_Symbols::eCntrl:   return "control symbol";
4203     case CArgAllow_Symbols::eDigit:   return "decimal";
4204     case CArgAllow_Symbols::eGraph:   return "graphical symbol";
4205     case CArgAllow_Symbols::eLower:   return "lower case";
4206     case CArgAllow_Symbols::ePrint:   return "printable";
4207     case CArgAllow_Symbols::ePunct:   return "punctuation";
4208     case CArgAllow_Symbols::eSpace:   return "space";
4209     case CArgAllow_Symbols::eUpper:   return "upper case";
4210     case CArgAllow_Symbols::eXdigit:  return "hexadecimal";
4211     case CArgAllow_Symbols::eUser:
4212         return "'" + NStr::PrintableString(symbol_set) + "'";
4213     }
4214     _TROUBLE;  return kEmptyStr;
4215 }
4216 
s_GetSymbolClass(CArgAllow_Symbols::ESymbolClass symbol_class)4217 static string s_GetSymbolClass(CArgAllow_Symbols::ESymbolClass symbol_class)
4218 {
4219     switch ( symbol_class ) {
4220     case CArgAllow_Symbols::eAlnum:   return "Alnum";
4221     case CArgAllow_Symbols::eAlpha:   return "Alpha";
4222     case CArgAllow_Symbols::eCntrl:   return "Cntrl";
4223     case CArgAllow_Symbols::eDigit:   return "Digit";
4224     case CArgAllow_Symbols::eGraph:   return "Graph";
4225     case CArgAllow_Symbols::eLower:   return "Lower";
4226     case CArgAllow_Symbols::ePrint:   return "Print";
4227     case CArgAllow_Symbols::ePunct:   return "Punct";
4228     case CArgAllow_Symbols::eSpace:   return "Space";
4229     case CArgAllow_Symbols::eUpper:   return "Upper";
4230     case CArgAllow_Symbols::eXdigit:  return "Xdigit";
4231     case CArgAllow_Symbols::eUser:    return "User";
4232     }
4233     _TROUBLE;  return kEmptyStr;
4234 }
4235 
4236 
4237 
4238 ///////////////////////////////////////////////////////
4239 //  CArgAllow_Symbols::
4240 //
4241 
CArgAllow_Symbols(ESymbolClass symbol_class)4242 CArgAllow_Symbols::CArgAllow_Symbols(ESymbolClass symbol_class)
4243     : CArgAllow()
4244 {
4245     Allow( symbol_class);
4246     return;
4247 }
4248 
4249 
CArgAllow_Symbols(const string & symbol_set)4250 CArgAllow_Symbols::CArgAllow_Symbols(const string& symbol_set)
4251     : CArgAllow()
4252 {
4253     Allow( symbol_set);
4254     return;
4255 }
4256 
4257 
Verify(const string & value) const4258 bool CArgAllow_Symbols::Verify(const string& value) const
4259 {
4260     if (value.length() != 1)
4261         return false;
4262 
4263     ITERATE( set< TSymClass >, pi,  m_SymClass) {
4264         if (s_IsAllowedSymbol(value[0], pi->first, pi->second)) {
4265             return true;
4266         }
4267     }
4268     return false;
4269 }
4270 
4271 
GetUsage(void) const4272 string CArgAllow_Symbols::GetUsage(void) const
4273 {
4274     string usage;
4275     ITERATE( set< TSymClass >, pi,  m_SymClass) {
4276         if (!usage.empty()) {
4277             usage += ", or ";
4278         }
4279         usage += s_GetUsageSymbol(pi->first, pi->second);
4280     }
4281 
4282     return "one symbol: " + usage;
4283 }
4284 
PrintUsageXml(CNcbiOstream & out) const4285 void CArgAllow_Symbols::PrintUsageXml(CNcbiOstream& out) const
4286 {
4287     out << "<" << "Symbols" << ">" << endl;
4288     ITERATE( set< TSymClass >, pi,  m_SymClass) {
4289         if (pi->first != eUser) {
4290             s_WriteXmlLine( out, "type", s_GetSymbolClass(pi->first).c_str());
4291         } else {
4292             ITERATE( string, p, pi->second) {
4293                 string c;
4294                 s_WriteXmlLine( out, "value", c.append(1,*p).c_str());
4295             }
4296         }
4297     }
4298     out << "</" << "Symbols" << ">" << endl;
4299 }
4300 
4301 CArgAllow_Symbols&
Allow(CArgAllow_Symbols::ESymbolClass symbol_class)4302 CArgAllow_Symbols::Allow(CArgAllow_Symbols::ESymbolClass symbol_class)
4303 {
4304     m_SymClass.insert( make_pair(symbol_class, kEmptyStr) );
4305     return *this;
4306 }
4307 
Allow(const string & symbol_set)4308 CArgAllow_Symbols& CArgAllow_Symbols::Allow(const string& symbol_set)
4309 {
4310     m_SymClass.insert( make_pair(eUser, symbol_set ));
4311     return *this;
4312 }
4313 
Clone(void) const4314 CArgAllow* CArgAllow_Symbols::Clone(void) const
4315 {
4316     CArgAllow_Symbols* clone = new CArgAllow_Symbols;
4317     clone->m_SymClass = m_SymClass;
4318     return clone;
4319 }
4320 
4321 
4322 
4323 ///////////////////////////////////////////////////////
4324 //  CArgAllow_String::
4325 //
4326 
CArgAllow_String(ESymbolClass symbol_class)4327 CArgAllow_String::CArgAllow_String(ESymbolClass symbol_class)
4328     : CArgAllow_Symbols(symbol_class)
4329 {
4330     return;
4331 }
4332 
4333 
CArgAllow_String(const string & symbol_set)4334 CArgAllow_String::CArgAllow_String(const string& symbol_set)
4335     : CArgAllow_Symbols(symbol_set)
4336 {
4337     return;
4338 }
4339 
4340 
Verify(const string & value) const4341 bool CArgAllow_String::Verify(const string& value) const
4342 {
4343     ITERATE( set< TSymClass >, pi,  m_SymClass) {
4344         string::const_iterator it;
4345         for (it = value.begin();  it != value.end(); ++it) {
4346             if ( !s_IsAllowedSymbol(*it, pi->first, pi->second) )
4347                 break;;
4348         }
4349         if (it == value.end()) {
4350             return true;
4351         }
4352     }
4353     return false;
4354 }
4355 
4356 
GetUsage(void) const4357 string CArgAllow_String::GetUsage(void) const
4358 {
4359     string usage;
4360     ITERATE( set< TSymClass >, pi,  m_SymClass) {
4361         if (!usage.empty()) {
4362             usage += ", or ";
4363         }
4364         usage += s_GetUsageSymbol(pi->first, pi->second);
4365     }
4366 
4367     return "to contain only symbols: " + usage;
4368 }
4369 
4370 
PrintUsageXml(CNcbiOstream & out) const4371 void CArgAllow_String::PrintUsageXml(CNcbiOstream& out) const
4372 {
4373     out << "<" << "String" << ">" << endl;
4374     ITERATE( set< TSymClass >, pi,  m_SymClass) {
4375         if (pi->first != eUser) {
4376             s_WriteXmlLine( out, "type", s_GetSymbolClass(pi->first).c_str());
4377         } else {
4378             s_WriteXmlLine( out, "charset", pi->second.c_str());
4379         }
4380     }
4381     out << "</" << "String" << ">" << endl;
4382 }
4383 
Clone(void) const4384 CArgAllow* CArgAllow_String::Clone(void) const
4385 {
4386     CArgAllow_String* clone = new CArgAllow_String;
4387     clone->m_SymClass = m_SymClass;
4388     return clone;
4389 }
4390 
4391 
4392 
4393 ///////////////////////////////////////////////////////
4394 //  CArgAllow_Strings::
4395 //
4396 
CArgAllow_Strings(NStr::ECase use_case)4397 CArgAllow_Strings::CArgAllow_Strings(NStr::ECase use_case)
4398     : CArgAllow(),
4399       m_Strings(PNocase_Conditional(use_case))
4400 {
4401     return;
4402 }
4403 
4404 
CArgAllow_Strings(initializer_list<string> values,NStr::ECase use_case)4405 CArgAllow_Strings::CArgAllow_Strings(initializer_list<string> values, NStr::ECase use_case)
4406     : m_Strings(values, PNocase_Conditional(use_case))
4407 {
4408 }
4409 
4410 
Allow(const string & value)4411 CArgAllow_Strings* CArgAllow_Strings::Allow(const string& value)
4412 {
4413     m_Strings.insert(value);
4414     return this;
4415 }
4416 
4417 
Verify(const string & value) const4418 bool CArgAllow_Strings::Verify(const string& value) const
4419 {
4420     TStrings::const_iterator it = m_Strings.find(value);
4421     return it != m_Strings.end();
4422 }
4423 
4424 
4425 string
GetUsage(void) const4426 CArgAllow_Strings::GetUsage(void) const
4427 {
4428     if ( m_Strings.empty() ) {
4429         return "ERROR:  Constraint with no values allowed(?!)";
4430     }
4431 
4432     string str;
4433     TStrings::const_iterator it = m_Strings.begin();
4434     for (;;) {
4435         str += "`";
4436         str += *it;
4437 
4438         ++it;
4439         if (it == m_Strings.end()) {
4440             str += "'";
4441             if ( m_Strings.key_comp()("a", "A") ) {
4442                 str += "  {case insensitive}";
4443             }
4444             break;
4445         }
4446         str += "', ";
4447     }
4448     return str;
4449 }
4450 
4451 
PrintUsageXml(CNcbiOstream & out) const4452 void CArgAllow_Strings::PrintUsageXml(CNcbiOstream& out) const
4453 {
4454     out << "<" << "Strings";
4455     out << " case_sensitive=\"";
4456     if ( m_Strings.key_comp()("a", "A") ) {
4457         out << "false";
4458     } else {
4459         out << "true";
4460     }
4461     out << "\">" << endl;
4462     ITERATE( TStrings, p, m_Strings) {
4463         s_WriteXmlLine( out, "value", (*p).c_str());
4464     }
4465     out << "</" << "Strings" << ">" << endl;
4466 }
4467 
4468 
AllowValue(const string & value)4469 CArgAllow_Strings& CArgAllow_Strings::AllowValue(const string& value)
4470 {
4471     return *Allow(value);
4472 }
4473 
Clone(void) const4474 CArgAllow* CArgAllow_Strings::Clone(void) const
4475 {
4476     CArgAllow_Strings* clone = new CArgAllow_Strings(m_Strings.key_comp().GetCase());
4477     clone->m_Strings = m_Strings;
4478     return clone;
4479 }
4480 
4481 
4482 ///////////////////////////////////////////////////////
4483 //  CArgAllow_Int8s::
4484 //
4485 
CArgAllow_Int8s(Int8 x_)4486 CArgAllow_Int8s::CArgAllow_Int8s(Int8 x_)
4487     : CArgAllow()
4488 {
4489     Allow( x_);
4490 }
4491 
CArgAllow_Int8s(Int8 x_min,Int8 x_max)4492 CArgAllow_Int8s::CArgAllow_Int8s(Int8 x_min, Int8 x_max)
4493     : CArgAllow()
4494 {
4495     AllowRange(x_min, x_max);
4496 }
4497 
4498 
Verify(const string & value) const4499 bool CArgAllow_Int8s::Verify(const string& value) const
4500 {
4501     Int8 val = s_StringToInt8(value);
4502     ITERATE( set< TInterval >, pi, m_MinMax) {
4503         if (pi->first <= val && val<= pi->second) {
4504             return true;
4505         }
4506     }
4507     return false;
4508 }
4509 
4510 
GetUsage(void) const4511 string CArgAllow_Int8s::GetUsage(void) const
4512 {
4513     if (m_MinMax.size() == 1) {
4514         Int8 x_min = m_MinMax.begin()->first;
4515         Int8 x_max = m_MinMax.begin()->second;
4516         if (x_min == x_max) {
4517             return NStr::Int8ToString(x_min);
4518         } else if (x_min == kMin_I8 && x_max != kMax_I8) {
4519             return string("less or equal to ") + NStr::Int8ToString(x_max);
4520         } else if (x_min != kMin_I8 && x_max == kMax_I8) {
4521             return string("greater or equal to ") + NStr::Int8ToString(x_min);
4522         } else if (x_min == kMin_I8 && x_max == kMax_I8) {
4523             return kEmptyStr;
4524         }
4525     }
4526     string usage;
4527     ITERATE( set< TInterval >, pi, m_MinMax) {
4528         if (!usage.empty()) {
4529             usage += ", ";
4530         }
4531         if (pi->first == pi->second) {
4532             usage += NStr::Int8ToString(pi->first);
4533         } else {
4534             usage += NStr::Int8ToString(pi->first) + ".." + NStr::Int8ToString(pi->second);
4535         }
4536 
4537     }
4538     return usage;
4539 }
4540 
4541 
PrintUsageXml(CNcbiOstream & out) const4542 void CArgAllow_Int8s::PrintUsageXml(CNcbiOstream& out) const
4543 {
4544     string tag("Int8s");
4545     if (dynamic_cast<const CArgAllow_Integers*>(this) != 0) {
4546         tag = "Integers";
4547     }
4548     out << "<" << tag << ">" << endl;
4549     ITERATE( set< TInterval >, pi, m_MinMax) {
4550         s_WriteXmlLine( out, "min", NStr::Int8ToString(pi->first).c_str());
4551         s_WriteXmlLine( out, "max", NStr::Int8ToString(pi->second).c_str());
4552     }
4553     out << "</" << tag << ">" << endl;
4554 }
4555 
AllowRange(Int8 from,Int8 to)4556 CArgAllow_Int8s& CArgAllow_Int8s::AllowRange(Int8 from, Int8 to)
4557 {
4558     m_MinMax.insert( make_pair(from,to) );
4559     return *this;
4560 }
4561 
Allow(Int8 value)4562 CArgAllow_Int8s& CArgAllow_Int8s::Allow(Int8 value)
4563 {
4564     m_MinMax.insert( make_pair(value,value) );
4565     return *this;
4566 }
4567 
Clone(void) const4568 CArgAllow* CArgAllow_Int8s::Clone(void) const
4569 {
4570     CArgAllow_Int8s* clone = new CArgAllow_Int8s;
4571     clone->m_MinMax = m_MinMax;
4572     return clone;
4573 }
4574 
4575 
4576 
4577 ///////////////////////////////////////////////////////
4578 //  CArgAllow_Integers::
4579 //
4580 
CArgAllow_Integers(int x_)4581 CArgAllow_Integers::CArgAllow_Integers(int x_)
4582     : CArgAllow_Int8s(x_)
4583 {
4584 }
4585 
CArgAllow_Integers(int x_min,int x_max)4586 CArgAllow_Integers::CArgAllow_Integers(int x_min, int x_max)
4587     : CArgAllow_Int8s(x_min, x_max)
4588 {
4589 }
4590 
GetUsage(void) const4591 string CArgAllow_Integers::GetUsage(void) const
4592 {
4593     if (m_MinMax.size() == 1) {
4594         Int8 x_min = m_MinMax.begin()->first;
4595         Int8 x_max = m_MinMax.begin()->second;
4596         if (x_min == x_max) {
4597             return NStr::Int8ToString(x_min);
4598         } else if (x_min == kMin_Int && x_max != kMax_Int) {
4599             return string("less or equal to ") + NStr::Int8ToString(x_max);
4600         } else if (x_min != kMin_Int && x_max == kMax_Int) {
4601             return string("greater or equal to ") + NStr::Int8ToString(x_min);
4602         } else if (x_min == kMin_Int && x_max == kMax_Int) {
4603             return kEmptyStr;
4604         }
4605     }
4606     return CArgAllow_Int8s::GetUsage();;
4607 }
4608 
Clone(void) const4609 CArgAllow* CArgAllow_Integers::Clone(void) const
4610 {
4611     CArgAllow_Integers* clone = new CArgAllow_Integers;
4612     clone->m_MinMax = m_MinMax;
4613     return clone;
4614 }
4615 
4616 
4617 ///////////////////////////////////////////////////////
4618 //  CArgAllow_Doubles::
4619 //
4620 
CArgAllow_Doubles(double x_value)4621 CArgAllow_Doubles::CArgAllow_Doubles(double x_value)
4622     : CArgAllow()
4623 {
4624     Allow(x_value);
4625 }
4626 
CArgAllow_Doubles(double x_min,double x_max)4627 CArgAllow_Doubles::CArgAllow_Doubles(double x_min, double x_max)
4628     : CArgAllow()
4629 {
4630     AllowRange( x_min, x_max );
4631 }
4632 
4633 
Verify(const string & value) const4634 bool CArgAllow_Doubles::Verify(const string& value) const
4635 {
4636     double val = NStr::StringToDouble(value, NStr::fDecimalPosixOrLocal);
4637     ITERATE( set< TInterval >, pi, m_MinMax) {
4638         if (pi->first <= val && val<= pi->second) {
4639             return true;
4640         }
4641     }
4642     return false;
4643 }
4644 
4645 
GetUsage(void) const4646 string CArgAllow_Doubles::GetUsage(void) const
4647 {
4648     if (m_MinMax.size() == 1) {
4649         double x_min = m_MinMax.begin()->first;
4650         double x_max = m_MinMax.begin()->second;
4651         if (x_min == x_max) {
4652             return NStr::DoubleToString(x_min);
4653         } else if (x_min == kMin_Double && x_max != kMax_Double) {
4654             return string("less or equal to ") + NStr::DoubleToString(x_max);
4655         } else if (x_min != kMin_Double && x_max == kMax_Double) {
4656             return string("greater or equal to ") + NStr::DoubleToString(x_min);
4657         } else if (x_min == kMin_Double && x_max == kMax_Double) {
4658             return kEmptyStr;
4659         }
4660     }
4661     string usage;
4662     ITERATE( set< TInterval >, pi, m_MinMax) {
4663         if (!usage.empty()) {
4664             usage += ", ";
4665         }
4666         if (pi->first == pi->second) {
4667             usage += NStr::DoubleToString(pi->first);
4668         } else {
4669             usage += NStr::DoubleToString(pi->first) + ".." + NStr::DoubleToString(pi->second);
4670         }
4671 
4672     }
4673     return usage;
4674 }
4675 
4676 
PrintUsageXml(CNcbiOstream & out) const4677 void CArgAllow_Doubles::PrintUsageXml(CNcbiOstream& out) const
4678 {
4679     out << "<" << "Doubles" << ">" << endl;
4680     ITERATE( set< TInterval >, pi, m_MinMax) {
4681         s_WriteXmlLine( out, "min", NStr::DoubleToString(pi->first).c_str());
4682         s_WriteXmlLine( out, "max", NStr::DoubleToString(pi->second).c_str());
4683     }
4684     out << "</" << "Doubles" << ">" << endl;
4685 }
4686 
AllowRange(double from,double to)4687 CArgAllow_Doubles& CArgAllow_Doubles::AllowRange(double from, double to)
4688 {
4689     m_MinMax.insert( make_pair(from,to) );
4690     return *this;
4691 }
4692 
Allow(double value)4693 CArgAllow_Doubles& CArgAllow_Doubles::Allow(double value)
4694 {
4695     m_MinMax.insert( make_pair(value,value) );
4696     return *this;
4697 }
4698 
Clone(void) const4699 CArgAllow* CArgAllow_Doubles::Clone(void) const
4700 {
4701     CArgAllow_Doubles* clone = new CArgAllow_Doubles;
4702     clone->m_MinMax = m_MinMax;
4703     return clone;
4704 }
4705 
4706 
4707 /////////////////////////////////////////////////////////////////////////////
4708 
Create(const string & name,const string & description)4709 CRef<CArgDependencyGroup> CArgDependencyGroup::Create(
4710         const string& name, const string& description)
4711 {
4712     CRef<CArgDependencyGroup> gr(new CArgDependencyGroup());
4713     gr->m_Name = name;
4714     gr->m_Description = description;
4715     return gr;
4716 }
4717 
CArgDependencyGroup()4718 CArgDependencyGroup::CArgDependencyGroup()
4719     : m_MinMembers(0), m_MaxMembers(0)
4720 {
4721 }
4722 
~CArgDependencyGroup(void)4723 CArgDependencyGroup::~CArgDependencyGroup(void)
4724 {
4725 }
4726 
SetMinMembers(size_t min_members)4727 CArgDependencyGroup& CArgDependencyGroup::SetMinMembers(size_t min_members)
4728 {
4729     m_MinMembers = min_members;
4730     return *this;
4731 }
4732 
SetMaxMembers(size_t max_members)4733 CArgDependencyGroup& CArgDependencyGroup::SetMaxMembers(size_t max_members)
4734 {
4735     m_MaxMembers = max_members;
4736     return *this;
4737 }
4738 
Add(const string & arg_name,EInstantSet instant_set)4739 CArgDependencyGroup& CArgDependencyGroup::Add(const string& arg_name, EInstantSet  instant_set)
4740 {
4741     m_Arguments[arg_name] = instant_set;
4742     return *this;
4743 }
4744 
Add(CArgDependencyGroup * dep_group,EInstantSet instant_set)4745 CArgDependencyGroup& CArgDependencyGroup::Add(
4746     CArgDependencyGroup* dep_group, EInstantSet instant_set)
4747 {
4748     m_Groups[ CConstRef<CArgDependencyGroup>(dep_group)] = instant_set;
4749     return *this;
4750 }
4751 
Evaluate(const CArgs & args) const4752 void CArgDependencyGroup::Evaluate( const CArgs& args) const
4753 {
4754     x_Evaluate(args, nullptr, nullptr);
4755 }
4756 
x_Evaluate(const CArgs & args,string * arg_set,string * arg_unset) const4757 bool CArgDependencyGroup::x_Evaluate( const CArgs& args, string* arg_set, string* arg_unset) const
4758 {
4759     bool top_level = !arg_set || !arg_unset;
4760     bool has_instant_set = false;
4761     size_t count_set = 0;
4762     set<string> names_set, names_unset;
4763     string args_set, args_unset;
4764 
4765     for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4766         i != m_Groups.end(); ++i) {
4767         string msg_set, msg_unset;
4768         if (i->first.GetPointer()->x_Evaluate(args, &msg_set, &msg_unset)) {
4769             ++count_set;
4770             has_instant_set = has_instant_set || (i->second == eInstantSet);
4771             names_set.insert(msg_set);
4772         } else {
4773             names_unset.insert(msg_unset);
4774         }
4775     }
4776     for (map<string, EInstantSet>::const_iterator i = m_Arguments.begin();
4777         i != m_Arguments.end(); ++i) {
4778         if (args.Exist(i->first)) {
4779             ++count_set;
4780             has_instant_set = has_instant_set || (i->second == eInstantSet);
4781             names_set.insert(i->first);
4782         } else {
4783             names_unset.insert(i->first);
4784         }
4785     }
4786     size_t count_total = m_Groups.size() + m_Arguments.size();
4787     size_t count_max = m_MaxMembers != 0 ? m_MaxMembers : count_total;
4788 
4789     if (names_set.size() > 1) {
4790         args_set = "(" + NStr::Join(names_set, ", ") + ")";
4791     } else if (names_set.size() == 1) {
4792         args_set = *names_set.begin();
4793     }
4794 
4795     if (names_unset.size() > 1) {
4796         args_unset = "(" + NStr::Join(names_unset, m_MinMembers <= 1 ? " | " : ", ") + ")";
4797     } else if (names_unset.size() == 1) {
4798         args_unset = *names_unset.begin();
4799     }
4800 
4801     bool result = count_set != 0 || top_level;
4802     if (result) {
4803         if (count_set > count_max) {
4804             string msg("Argument conflict: ");
4805             msg += args_set + " may not be specified simultaneously";
4806             NCBI_THROW(CArgException, eConstraint, msg);
4807         }
4808         if (!has_instant_set && count_set < m_MinMembers) {
4809             string msg("Argument has no value: ");
4810             if (count_total != count_max) {
4811                 msg += (m_MinMembers - count_set > 1) ? "some" : "one";
4812                 msg += " of ";
4813             }
4814             msg += args_unset + " must be specified";
4815             NCBI_THROW(CArgException,eNoValue, msg);
4816         }
4817     }
4818     if (arg_set) {
4819         *arg_set = args_set;
4820     }
4821     if (arg_unset) {
4822         *arg_unset = args_unset;
4823     }
4824     return result;
4825 }
4826 
PrintUsage(list<string> & arr,size_t offset) const4827 void CArgDependencyGroup::PrintUsage(list<string>& arr, size_t offset) const
4828 {
4829     arr.push_back(kEmptyStr);
4830     string off(2*offset,' ');
4831     string msg(off);
4832     msg += m_Name + ": {";
4833 
4834     bool first = true;
4835     list<string> instant;
4836     for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4837         i != m_Groups.end(); ++i) {
4838         if (!first) {
4839             msg += ",";
4840         }
4841         first = false;
4842         msg += i->first.GetPointer()->m_Name;
4843         if (i->second == eInstantSet) {
4844             instant.push_back(i->first.GetPointer()->m_Name);
4845         }
4846     }
4847     for (map<string, EInstantSet>::const_iterator i = m_Arguments.begin();
4848         i != m_Arguments.end(); ++i) {
4849         if (!first) {
4850             msg += ",";
4851         }
4852         first = false;
4853         msg += i->first;
4854         if (i->second == eInstantSet) {
4855             instant.push_back(i->first);
4856         }
4857     }
4858     msg += "}";
4859     arr.push_back(msg);
4860     if (!m_Description.empty()) {
4861         msg = off;
4862         msg += m_Description;
4863         arr.push_back(msg);
4864     }
4865     size_t count_total = m_Groups.size() + m_Arguments.size();
4866     size_t count_max = m_MaxMembers != 0 ? m_MaxMembers : count_total;
4867 
4868     msg = off + "in which ";
4869     size_t count = m_MinMembers;
4870     if (m_MinMembers == count_max) {
4871         msg += "exactly ";
4872         msg += NStr::NumericToString(m_MinMembers);
4873     } else if (count_max == count_total && m_MinMembers != 0) {
4874         msg += "at least ";
4875         msg += NStr::NumericToString(m_MinMembers);
4876     } else if (count_max != count_total && m_MinMembers == 0) {
4877         msg += "no more than ";
4878         msg += NStr::NumericToString(m_MaxMembers);
4879         count = m_MaxMembers;
4880     } else {
4881         msg += NStr::NumericToString(m_MinMembers);
4882         msg += " to ";
4883         msg += NStr::NumericToString(m_MaxMembers);
4884         count = m_MaxMembers;
4885     }
4886     msg += " element";
4887     if (count != 1) {
4888         msg += "s";
4889     }
4890     msg += " must be set";
4891     arr.push_back(msg);
4892 
4893     if (!instant.empty()) {
4894         msg = off;
4895         msg += "Instant set: ";
4896         msg += NStr::Join(instant, ",");
4897         arr.push_back(msg);
4898     }
4899     for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4900         i != m_Groups.end(); ++i) {
4901         i->first.GetPointer()->PrintUsage(arr, offset+1);
4902     }
4903 }
4904 
PrintUsageXml(CNcbiOstream & out) const4905 void CArgDependencyGroup::PrintUsageXml(CNcbiOstream& out) const
4906 {
4907     out << "<" << "dependencygroup" << ">" << endl;
4908     out << "<" << "name" << ">" << m_Name << "</" << "name" << ">" << endl;
4909     out << "<" << "description" << ">" << m_Description << "</" << "description" << ">" << endl;
4910 
4911     for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4912         i != m_Groups.end(); ++i) {
4913         out << "<" << "group";
4914         if (i->second == eInstantSet) {
4915             out << " instantset=\"true\"";
4916         }
4917         out << ">" << i->first.GetPointer()->m_Name << "</" << "group" << ">" << endl;
4918     }
4919     for (map<string, EInstantSet>::const_iterator i = m_Arguments.begin();
4920         i != m_Arguments.end(); ++i) {
4921         out << "<" << "argument";
4922         if (i->second == eInstantSet) {
4923             out << " instantset=\"true\"";
4924         }
4925         out << ">" << i->first << "</" << "argument" << ">" << endl;
4926     }
4927     out << "<" << "minmembers" << ">" << m_MinMembers << "</" << "minmembers" << ">" << endl;
4928     out << "<" << "maxmembers" << ">" << m_MaxMembers << "</" << "maxmembers" << ">" << endl;
4929     for (map< CConstRef<CArgDependencyGroup>, EInstantSet>::const_iterator i = m_Groups.begin();
4930         i != m_Groups.end(); ++i) {
4931         i->first.GetPointer()->PrintUsageXml(out);
4932     }
4933     out << "</" << "dependencygroup" << ">" << endl;
4934 }
4935 
4936 ///////////////////////////////////////////////////////
4937 // CArgException
4938 
GetErrCodeString(void) const4939 const char* CArgException::GetErrCodeString(void) const
4940 {
4941     switch (GetErrCode()) {
4942     case eInvalidArg: return "eInvalidArg";
4943     case eNoValue:    return "eNoValue";
4944     case eExcludedValue: return "eExcludedValue";
4945     case eWrongCast:  return "eWrongCast";
4946     case eConvert:    return "eConvert";
4947     case eNoFile:     return "eNoFile";
4948     case eConstraint: return "eConstraint";
4949     case eArgType:    return "eArgType";
4950     case eNoArg:      return "eNoArg";
4951     case eSynopsis:   return "eSynopsis";
4952     default:    return CException::GetErrCodeString();
4953     }
4954 }
4955 
GetErrCodeString(void) const4956 const char* CArgHelpException::GetErrCodeString(void) const
4957 {
4958     switch (GetErrCode()) {
4959     case eHelp:     return "eHelp";
4960     case eHelpFull: return "eHelpFull";
4961     case eHelpXml:  return "eHelpXml";
4962     case eHelpErr:  return "eHelpErr";
4963     default:    return CException::GetErrCodeString();
4964     }
4965 }
4966 
4967 
4968 END_NCBI_SCOPE
4969