E-MailRelay
garg.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2023 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/// \file garg.cpp
19///
20
21#include "gdef.h"
22#include "garg.h"
23#include "gprocess.h"
24#include "gpath.h"
25#include "gstr.h"
26#include "glog.h"
27#include "gassert.h"
28#include <cstring>
29
30std::string G::Arg::m_v0 ;
31std::string G::Arg::m_cwd ;
32
33G::Arg::Arg( int argc , char **argv )
34{
35 G_ASSERT( argc > 0 ) ;
36 G_ASSERT( argv != nullptr ) ;
37 for( int i = 0 ; i < argc ; i++ )
38 m_array.push_back( argv[i] ) ;
39
40 static bool first = true ;
41 if( first )
42 {
43 m_v0 = std::string( argv[0] ) ;
44 m_cwd = Process::cwd(true/*nothrow*/) ; // don't throw yet - we may "cd /" to deamonise
45 first = false ;
46 }
47}
48
49G::Arg::Arg( const StringArray & args ) :
50 m_array(args)
51{
52}
53
55= default ;
56
57#ifndef G_LIB_SMALL
58void G::Arg::parse( HINSTANCE , const std::string & command_line_tail )
59{
60 std::string proc_exe = Process::exe() ;
61 if( proc_exe.empty() ) throw Exception( "cannot determine the path of this executable" ) ;
62 m_array.clear() ;
63 m_array.push_back( proc_exe ) ;
64 parseImp( command_line_tail ) ;
65}
66#endif
67
68void G::Arg::parse( const std::string & command_line )
69{
70 G_ASSERT( !command_line.empty() ) ;
71 m_array.clear() ;
72 parseImp( command_line ) ;
73}
74
75#ifndef G_LIB_SMALL
76void G::Arg::reparse( const std::string & command_line_tail )
77{
78 auto p = m_array.begin() ;
79 m_array.erase( ++p , m_array.end() ) ;
80 parseImp( command_line_tail ) ;
81}
82#endif
83
84#ifndef G_LIB_SMALL
85std::string G::Arg::v0()
86{
87 return m_v0 ;
88}
89#endif
90
91G::StringArray G::Arg::array( unsigned int shift ) const
92{
93 StringArray result = m_array ;
94 while( !result.empty() && shift-- )
95 result.erase( result.begin() ) ;
96 return result ;
97}
98
99bool G::Arg::contains( const std::string & option , std::size_t option_args , bool cs ) const
100{
101 return find( cs , option , option_args , nullptr ) != 0U ;
102}
103
104#ifndef G_LIB_SMALL
105std::size_t G::Arg::count( const std::string & option )
106{
107 return find( true , option , 0U , nullptr ) ;
108}
109#endif
110
111std::size_t G::Arg::find( bool cs , const std::string & option , std::size_t option_args ,
112 std::size_t * index_p ) const
113{
114 std::size_t count = 0U ;
115 for( std::size_t i = 1U ; i < m_array.size() ; i++ ) // start from v[1]
116 {
117 if( strmatch(cs,option,m_array[i]) && (i+option_args) < m_array.size() )
118 {
119 count++ ;
120 if( index_p != nullptr )
121 *index_p = i ;
122 i += option_args ;
123 }
124 }
125 return count ;
126}
127
128std::size_t G::Arg::match( const std::string & prefix ) const
129{
130 for( std::size_t i = 1U ; i < m_array.size() ; i++ )
131 {
132 if( Str::headMatch(m_array[i],prefix) )
133 {
134 return i ;
135 }
136 }
137 return 0U ;
138}
139
140bool G::Arg::strmatch( bool cs , const std::string & s1 , const std::string & s2 )
141{
142 return cs ? (s1==s2) : (Str::upper(s1)==Str::upper(s2)) ;
143}
144
145bool G::Arg::remove( const std::string & option , std::size_t option_args )
146{
147 std::size_t i = 0U ;
148 const bool found = find( true , option , option_args , &i ) != 0U ;
149 if( found )
150 removeAt( i , option_args ) ;
151 return found ;
152}
153
154std::string G::Arg::removeValue( const std::string & option , const std::string & default_ )
155{
156 std::size_t option_args = 1U ;
157 std::size_t i = 0U ;
158 const bool found = find( true , option , option_args , &i ) != 0U ;
159 return found ? removeAt( i , option_args ) : default_ ;
160}
161
162std::string G::Arg::removeAt( std::size_t option_index , std::size_t option_args )
163{
164 std::string value ;
165 if( option_index > 0U && (option_index+option_args) < m_array.size() )
166 {
167 value = v( option_index + (option_args?1U:0U) , std::string() ) ;
168 auto p = m_array.begin() ;
169 for( std::size_t i = 0U ; i < option_index ; i++ ) ++p ; // (rather than cast)
170 p = m_array.erase( p ) ;
171 for( std::size_t i = 0U ; i < option_args && p != m_array.end() ; i++ )
172 p = m_array.erase( p ) ;
173 }
174 return value ;
175}
176
177std::size_t G::Arg::index( const std::string & option , std::size_t option_args ,
178 std::size_t default_ ) const
179{
180 std::size_t i = 0U ;
181 const bool found = find( true , option , option_args , &i ) != 0U ;
182 return found ? i : default_ ;
183}
184
185std::size_t G::Arg::c() const
186{
187 return m_array.size() ;
188}
189
190std::string G::Arg::v( std::size_t i ) const
191{
192 G_ASSERT( i < m_array.size() ) ;
193 return m_array.at(i) ;
194}
195
196std::string G::Arg::v( std::size_t i , const std::string & default_ ) const
197{
198 return i < m_array.size() ? m_array.at(i) : default_ ;
199}
200
201std::string G::Arg::prefix() const
202{
203 G_ASSERT( !m_array.empty() ) ;
204 Path path( m_array.at(0U) ) ;
205 return path.withoutExtension().basename() ;
206}
207
208const char * G::Arg::prefix( char ** argv ) noexcept
209{
210 const char * exe = argv[0] ;
211 const char * p1 = std::strrchr( exe , '/' ) ;
212 const char * p2 = std::strrchr( exe , '\\' ) ;
213 p1 = p1 == nullptr ? exe : (p1+1U) ;
214 p2 = p2 == nullptr ? exe : (p2+1U) ;
215 return p1 > p2 ? p1 : p2 ;
216}
217
218void G::Arg::parseImp( const std::string & command_line )
219{
220 string_view ws( " \t" ) ;
221 string_view nbws( "\0\0" , 2U ) ;
222 const char esc = '\\' ;
223 const char qq = '\"' ;
224 Str::splitIntoTokens( Str::dequote(command_line,qq,esc,ws,nbws) , m_array , ws , esc ) ;
225 Str::replace( m_array , '\0' , ' ' ) ;
226}
227
228std::string G::Arg::exe( bool do_throw )
229{
230 std::string proc_exe = Process::exe() ;
231 if( proc_exe.empty() && ( m_v0.empty() || ( m_cwd.empty() && Path(m_v0).isRelative() ) ) )
232 {
233 if( do_throw )
234 {
235 throw Exception( "cannot determine the absolute path of the current executable" ,
236 G::is_windows() ? "" : "try mounting procfs" ) ;
237 }
238 return {} ;
239 }
240 else if( proc_exe.empty() && Path(m_v0).isRelative() )
241 {
242 return Path::join(m_cwd,m_v0).collapsed().str() ;
243 }
244 else if( proc_exe.empty() )
245 {
246 return m_v0 ;
247 }
248 else
249 {
250 return proc_exe ;
251 }
252}
253
254#ifndef G_LIB_SMALL
255G::StringArray::const_iterator G::Arg::cbegin() const
256{
257 return m_array.size() <= 1U ? cend() : std::next( m_array.cbegin() ) ;
258}
259#endif
260
261G::StringArray::const_iterator G::Arg::cend() const
262{
263 return m_array.cend() ;
264}
265
std::size_t c() const
Returns the number of tokens in the command line, including the program name.
Definition: garg.cpp:185
std::string removeValue(const std::string &option, const std::string &default_={})
Removes the given single-valued option and its value.
Definition: garg.cpp:154
static std::string exe(bool do_throw=true)
Returns Process::exe() or an absolute path constructed from v0() and possibly using the cwd.
Definition: garg.cpp:228
std::size_t match(const std::string &prefix) const
Returns the index of the first argument that matches the given prefix.
Definition: garg.cpp:128
std::string v(std::size_t i) const
Returns the i'th argument.
Definition: garg.cpp:190
std::size_t index(const std::string &option, std::size_t option_args=0U, std::size_t default_=0U) const
Returns the index of the given option.
Definition: garg.cpp:177
std::string removeAt(std::size_t option_index, std::size_t option_args=0U)
Removes the given argument and the following 'option_args' ones.
Definition: garg.cpp:162
StringArray::const_iterator cend() const
Returns the end operator.
Definition: garg.cpp:261
std::string prefix() const
Returns the basename of v(0) without any extension.
Definition: garg.cpp:201
bool contains(const std::string &option, std::size_t option_args=0U, bool case_sensitive=true) const
Returns true if the command line contains the given option with enough command line arguments left to...
Definition: garg.cpp:99
StringArray array(unsigned int shift=0U) const
Returns the arguments as a string array, with an optional shift.
Definition: garg.cpp:91
bool remove(const std::string &option, std::size_t option_args=0U)
Removes the given option and its arguments.
Definition: garg.cpp:145
void parse(HINSTANCE hinstance, const std::string &command_line_tail)
Parses the given command-line tail, splitting it up into an array of tokens.
Definition: garg.cpp:58
static std::string v0()
Returns a copy of argv[0] from the first call to the constructor that takes argc/argv.
Definition: garg.cpp:85
StringArray::const_iterator cbegin() const
Returns a begin operator, advanced to exclude argv0.
Definition: garg.cpp:255
std::size_t count(const std::string &option)
Returns the number of times the given string appears in the list of arguments.
Definition: garg.cpp:105
Arg()
Default constructor. Initialise with parse().
void reparse(const std::string &command_line_tail)
Reinitialises the object with the given command-line tail.
Definition: garg.cpp:76
A general-purpose exception class derived from std::exception and containing an error message.
Definition: gexception.h:64
A Path object represents a file system path.
Definition: gpath.h:73
static Path join(const StringArray &parts)
Builds a path from a set of parts.
Definition: gpath.cpp:445
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:346
Path withoutExtension() const
Returns a path without the basename extension, if any.
Definition: gpath.cpp:364
Path collapsed() const
Returns the path with "foo/.." and "." parts removed, so far as is possible without changing the mean...
Definition: gpath.cpp:470
std::string str() const
Returns the path string.
Definition: gpath.h:224
static std::string cwd(bool no_throw=false)
Returns the current working directory.
static std::string exe()
Returns the absolute path of the current executable, independent of the argv array passed to main().
static bool replace(std::string &s, string_view from, string_view to, std::size_t *pos_p=nullptr)
A string_view overload.
Definition: gstr.cpp:226
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:1122
static std::string dequote(const std::string &, char qq='\"' , char esc = '\\' , string_view ws = Str::ws() , string_view nbws = Str::ws() )
Dequotes a string by removing unescaped quotes and escaping quoted whitespace, so "qq-aaa-esc-qq-bbb-...
Definition: gstr.cpp:128
static std::string upper(string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
Definition: gstr.cpp:839
static bool headMatch(const std::string &in, string_view head) noexcept
Returns true if the string has the given start (or head is empty).
Definition: gstr.cpp:1362
A class like c++17's std::string_view.
Definition: gstringview.h:51
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstringarray.h:30