E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

goptionparser.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // goptionparser.cpp
19 //
20
21 #include "gdef.h"
22 #include "goptionparser.h"
23 #include "gstr.h"
24 #include "gassert.h"
25 #include "gdebug.h"
26 #include <algorithm>
27 #include <map>
28 #include <stdexcept>
29
30 G::OptionParser::OptionParser( const Options & spec , OptionMap & values_out , StringArray & errors_out ) :
31 m_spec(spec) ,
32 m_map(values_out) ,
33 m_errors(errors_out)
34 {
35 }
36
37 G::OptionParser::OptionParser( const Options & spec , OptionMap & values_out ) :
38 m_spec(spec) ,
39 m_map(values_out) ,
40 m_errors(m_errors_ignored)
41 {
42 }
43
44 size_t G::OptionParser::parse( const StringArray & args_in , size_t start )
45 {
46 size_t i = start ;
47 for( ; i < args_in.size() ; i++ )
48 {
49 const std::string & arg = args_in.at(i) ;
50
51 if( arg == "--" ) // end-of-options marker
52 {
53 i++ ;
54 break ;
55 }
56
57 if( isAnOptionSet(arg) ) // eg. "-ltv"
58 {
59 for( size_t n = 1U ; n < arg.length() ; n++ )
60 processOptionOn( arg.at(n) ) ;
61 }
62 else if( isOldOption(arg) ) // eg. "-v"
63 {
64 char c = arg.at(1U) ;
65 if( m_spec.valued(c) && (i+1U) >= args_in.size() )
66 errorNoValue( c ) ;
67 else if( m_spec.valued(c) )
68 processOption( c , args_in.at(++i) ) ;
69 else
70 processOptionOn( c ) ;
71 }
72 else if( isNewOption(arg) ) // eg. "--foo"
73 {
74 std::string name = arg.substr( 2U ) ; // eg. "foo" or "foo=..."
75 std::string::size_type pos_eq = eqPos(name) ;
76 bool has_eq = pos_eq != std::string::npos ;
77 std::string key = has_eq ? name.substr(0U,pos_eq) : name ;
78 if( has_eq && m_spec.unvalued(key) && G::Str::isPositive(eqValue(name,pos_eq)) ) // "foo=yes"
79 processOptionOn( key ) ;
80 else if( has_eq && m_spec.unvalued(key) && G::Str::isNegative(eqValue(name,pos_eq)) ) // "foo=no"
81 processOptionOff( key ) ;
82 else if( has_eq ) // "foo=bar"
83 processOption( key , eqValue(name,pos_eq) , false ) ;
84 else if( m_spec.valued(name) && (i+1U) >= args_in.size() ) // "foo bar"
85 errorNoValue( name ) ;
86 else if( m_spec.valued(name) )
87 processOption( name , args_in.at(++i) , true ) ;
88 else
89 processOptionOn( name ) ;
90 }
91 else
92 {
93 break ;
94 }
95 }
96 return i ;
97 }
98
99 void G::OptionParser::processOptionOn( const std::string & name )
100 {
101 if( !m_spec.valid(name) )
102 errorUnknownOption( name ) ;
103 else if( m_spec.valued(name) )
104 errorNoValue( name ) ;
105 else if( haveSeenOff(name) )
106 errorConflict( name ) ;
107 else
108 m_map.insert( std::make_pair(name,OptionValue::on()) ) ;
109 }
110
111 void G::OptionParser::processOptionOff( const std::string & name )
112 {
113 if( !m_spec.valid(name) )
114 errorUnknownOption( name ) ;
115 else if( m_spec.valued(name) )
116 errorNoValue( name ) ;
117 else if( haveSeenOn(name) )
118 errorConflict( name ) ;
119 else
120 m_map.insert( std::make_pair(name,OptionValue::off()) ) ;
121 }
122
123 void G::OptionParser::processOption( const std::string & name , const std::string & value , bool fail_if_dubious_value )
124 {
125 if( !m_spec.valid(name) )
126 errorUnknownOption( name ) ;
127 else if( !value.empty() && value[0] == '-' && fail_if_dubious_value )
128 errorDubiousValue( name , value ) ;
129 else if( !m_spec.valued(name) && !value.empty() )
130 errorExtraValue( name , value ) ;
131 else if( haveSeen(name) && !m_spec.multivalued(name) )
132 errorDuplicate( name ) ;
133 else
134 m_map.insert( OptionMap::value_type(name,OptionValue(value)) ) ;
135 }
136
137 void G::OptionParser::processOptionOn( char c )
138 {
139 std::string name = m_spec.lookup( c ) ;
140 if( !m_spec.valid(name) )
141 errorUnknownOption( c ) ;
142 else if( m_spec.valued(name) )
143 errorNoValue( c ) ;
144 else if( haveSeenOff(name) )
145 errorConflict( name ) ;
146 else
147 m_map.insert( std::make_pair(name,OptionValue::on()) ) ;
148 }
149
150 void G::OptionParser::processOption( char c , const std::string & value )
151 {
152 std::string name = m_spec.lookup( c ) ;
153 if( !m_spec.valid(name) )
154 errorUnknownOption( c ) ;
155 else if( !m_spec.valued(name) && !value.empty() )
156 errorExtraValue( name , value ) ;
157 else if( haveSeen(name) && !m_spec.multivalued(c) )
158 errorDuplicate( c ) ;
159 else
160 m_map.insert( OptionMap::value_type(name,OptionValue(value)) ) ;
161 }
162
163 std::string::size_type G::OptionParser::eqPos( const std::string & s )
164 {
165 std::string::size_type p = s.find_first_not_of("abcdefghijklmnopqrstuvwxyz0123456789-_") ;
166 return p != std::string::npos && s.at(p) == '=' ? p : std::string::npos ;
167 }
168
169 std::string G::OptionParser::eqValue( const std::string & s , std::string::size_type pos )
170 {
171 return (pos+1U) == s.length() ? std::string() : s.substr(pos+1U) ;
172 }
173
174 bool G::OptionParser::isOldOption( const std::string & arg )
175 {
176 return
177 ( arg.length() > 1U && arg.at(0U) == '-' ) &&
178 ! isNewOption( arg ) ;
179 }
180
181 bool G::OptionParser::isNewOption( const std::string & arg )
182 {
183 return arg.length() > 2U && arg.at(0U) == '-' && arg.at(1U) == '-' ;
184 }
185
186 bool G::OptionParser::isAnOptionSet( const std::string & arg )
187 {
188 return isOldOption(arg) && arg.length() > 2U ;
189 }
190
191 void G::OptionParser::errorDubiousValue( const std::string & name , const std::string & value )
192 {
193 m_errors.push_back( std::string("use of \"--")+name+" "+value+"\" is probably a mistake, or try \"--"+name+"="+value+"\" instead" ) ;
194 }
195
196 void G::OptionParser::errorDuplicate( char c )
197 {
198 m_errors.push_back( std::string("duplicate use of \"-") + std::string(1U,c) + "\"" ) ;
199 }
200
201 void G::OptionParser::errorDuplicate( const std::string & name )
202 {
203 m_errors.push_back( std::string("duplicate use of \"--") + name + "\"" ) ;
204 }
205
206 void G::OptionParser::errorExtraValue( char c , const std::string & )
207 {
208 m_errors.push_back( std::string("cannot give a value with \"-") + std::string(1U,c) + "\"" ) ;
209 }
210
211 void G::OptionParser::errorExtraValue( const std::string & name , const std::string & value )
212 {
213 m_errors.push_back( std::string("cannot give a value with \"--") + name + "\" (" + value + ")" ) ;
214 }
215
216 void G::OptionParser::errorNoValue( char c )
217 {
218 m_errors.push_back( std::string("no value supplied for -") + std::string(1U,c) ) ;
219 }
220
221 void G::OptionParser::errorNoValue( const std::string & name )
222 {
223 m_errors.push_back( std::string("no value supplied for \"--") + name + "\"" ) ;
224 }
225
226 void G::OptionParser::errorUnknownOption( char c )
227 {
228 m_errors.push_back( std::string("invalid option: \"-") + std::string(1U,c) + "\"" ) ;
229 }
230
231 void G::OptionParser::errorUnknownOption( const std::string & name )
232 {
233 m_errors.push_back( std::string("invalid option: \"--") + name + "\"" ) ;
234 }
235
236 void G::OptionParser::errorConflict( const std::string & name )
237 {
238 m_errors.push_back( std::string("conflicting values: \"--") + name + "\"" ) ;
239 }
240
241 bool G::OptionParser::haveSeenOn( const std::string & name ) const
242 {
243 OptionMap::const_iterator p = m_map.find( name ) ;
244 return p != m_map.end() && !(*p).second.is_off() ;
245 }
246
247 bool G::OptionParser::haveSeenOff( const std::string & name ) const
248 {
249 OptionMap::const_iterator p = m_map.find( name ) ;
250 return p != m_map.end() && (*p).second.is_off() ;
251 }
252
253 bool G::OptionParser::haveSeen( const std::string & name ) const
254 {
255 return m_map.find(name) != m_map.end() ;
256 }
257
258 /// \file goptionparser.cpp
G::Str::isPositive
static bool isPositive(const std::string &)
Returns true if the string has a positive meaning, such as "1", "true", "yes".
Definition: gstr.cpp:1182
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
G::OptionParser::parse
size_t parse(const StringArray &args, size_t start_position=1U)
Parses the given command-line arguments into the value map and/or error list defined by the construct...
Definition: goptionparser.cpp:44
G::OptionValue::on
static OptionValue on()
A factory function for an unvalued option-enabled option.
Definition: goptionvalue.h:96
G::OptionParser::OptionParser
OptionParser(const Options &spec, OptionMap &values_out, StringArray &errors_out)
Constructor.
Definition: goptionparser.cpp:30
G::OptionMap
A multimap-like container for command-line options and their values.
Definition: goptionmap.h:39
G::OptionValue::off
static OptionValue off()
A factory function for an unvalued option-disabled option.
Definition: goptionvalue.h:104
gstr.h
gassert.h
G::Str::isNegative
static bool isNegative(const std::string &)
Returns true if the string has a negative meaning, such as "0", "false", "no".
Definition: gstr.cpp:1188
goptionparser.h
G::Options
A class to represent allowed command-line options and to provide command-line usage text...
Definition: goptions.h:65
gdebug.h