E-MailRelay
garg.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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 "gscope.h"
25#include "gpath.h"
26#include "gstr.h"
27#include "glog.h"
28#include "gassert.h"
29#include <cstring>
30
31G::Path G::Arg::m_v0 ;
32G::Path G::Arg::m_cwd ;
33
34G::Arg::Arg( int argc , char **argv )
35{
36 G_ASSERT( argc > 0 ) ;
37 G_ASSERT( argv != nullptr ) ;
38
39 for( int i = 0 ; i < argc ; i++ )
40 m_array.emplace_back( argv[i] ) ;
41
42 if( m_v0.empty() && !m_array.empty() )
43 {
44 m_v0 = Path( m_array[0] ) ;
45 m_cwd = Process::cwd( std::nothrow ) ; // don't throw yet - we may "cd /" to deamonise
46 }
47}
48
49G::Arg::Arg( const StringArray & args ) :
50 m_array(args)
51{
52}
53
54#ifndef G_LIB_SMALL
55G::Arg::Arg( const Path & argv0 , const std::string & command_line_tail )
56{
57 if( argv0.empty() )
58 throw Exception( "invalid path for this executable" ) ;
59 parseImp( command_line_tail ) ;
60 m_array.insert( m_array.begin() , argv0.str() ) ;
61}
62#endif
63
64G::Arg::Arg( const std::string & command_line )
65{
66 G_ASSERT( !command_line.empty() ) ;
67 parseImp( command_line ) ;
68}
69
71{
72 Arg arg( (Windows()) ) ;
73 if( m_v0.empty() && !arg.m_array.empty() )
74 {
75 m_v0 = Path( arg.m_array[0] ) ;
76 m_cwd = Process::cwd( std::nothrow ) ;
77 }
78 return arg ;
79}
80
81#ifndef G_LIB_SMALL
83{
84 return m_v0 ;
85}
86#endif
87
88G::StringArray G::Arg::array( unsigned int shift ) const
89{
90 StringArray result = m_array ;
91 while( !result.empty() && shift-- )
92 result.erase( result.begin() ) ;
93 return result ;
94}
95
96bool G::Arg::contains( std::string_view option , std::size_t option_args , bool cs ) const
97{
98 return find( cs , option , option_args , nullptr ) != 0U ;
99}
100
101#ifndef G_LIB_SMALL
102std::size_t G::Arg::count( std::string_view option ) const
103{
104 return find( true , option , 0U , nullptr ) ;
105}
106#endif
107
108std::size_t G::Arg::find( bool cs , std::string_view option , std::size_t option_args ,
109 std::size_t * index_p ) const
110{
111 std::size_t count = 0U ;
112 for( std::size_t i = 1U ; i < m_array.size() ; i++ ) // start from v[1]
113 {
114 if( strmatch(cs,option,m_array[i]) && (i+option_args) < m_array.size() )
115 {
116 count++ ;
117 if( index_p != nullptr )
118 *index_p = i ;
119 i += option_args ;
120 }
121 }
122 return count ;
123}
124
125std::size_t G::Arg::match( const std::string & prefix ) const
126{
127 for( std::size_t i = 1U ; i < m_array.size() ; i++ )
128 {
129 if( Str::headMatch(m_array[i],prefix) )
130 {
131 return i ;
132 }
133 }
134 return 0U ;
135}
136
137bool G::Arg::strmatch( bool cs , std::string_view s1 , std::string_view s2 )
138{
139 return cs ? (s1==s2) : Str::imatch(s1,s2) ;
140}
141
142bool G::Arg::remove( std::string_view option , std::size_t option_args )
143{
144 std::size_t i = 0U ;
145 const bool found = find( true , option , option_args , &i ) != 0U ;
146 if( found )
147 removeAt( i , option_args ) ;
148 return found ;
149}
150
151std::string G::Arg::removeValue( std::string_view option , const std::string & default_ )
152{
153 std::size_t option_args = 1U ;
154 std::size_t i = 0U ;
155 const bool found = find( true , option , option_args , &i ) != 0U ;
156 return found ? removeAt( i , option_args ) : default_ ;
157}
158
159std::string G::Arg::removeAt( std::size_t option_index , std::size_t option_args )
160{
161 std::string value ;
162 if( option_index > 0U && (option_index+option_args) < m_array.size() )
163 {
164 value = v( option_index + (option_args?1U:0U) , std::string() ) ;
165 auto p = m_array.begin() ;
166 for( std::size_t i = 0U ; i < option_index ; i++ ) ++p ; // (rather than cast)
167 p = m_array.erase( p ) ;
168 for( std::size_t i = 0U ; i < option_args && p != m_array.end() ; i++ )
169 p = m_array.erase( p ) ;
170 }
171 return value ;
172}
173
174std::size_t G::Arg::index( std::string_view option , std::size_t option_args ,
175 std::size_t default_ ) const
176{
177 std::size_t i = 0U ;
178 const bool found = find( true , option , option_args , &i ) != 0U ;
179 return found ? i : default_ ;
180}
181
182std::size_t G::Arg::c() const
183{
184 return m_array.size() ;
185}
186
187std::string G::Arg::v( std::size_t i ) const
188{
189 G_ASSERT( i < m_array.size() ) ;
190 return m_array.at(i) ;
191}
192
193std::string G::Arg::v( std::size_t i , const std::string & default_ ) const
194{
195 return i < m_array.size() ? m_array.at(i) : default_ ;
196}
197
198std::string G::Arg::prefix() const
199{
200 G_ASSERT( !m_array.empty() ) ;
201 Path path( m_array.at(0U) ) ;
202 return path.withoutExtension().basename() ;
203}
204
205const char * G::Arg::prefix( char ** argv ) noexcept
206{
207 if( argv == nullptr ) return "" ;
208 const char * exe = argv[0] ;
209 if( exe == nullptr || exe[0] == '\0' ) return "" ;
210 const char * p1 = std::strrchr( exe , '/' ) ;
211 const char * p2 = std::strrchr( exe , '\\' ) ;
212 p1 = p1 == nullptr ? exe : (p1+1U) ;
213 p2 = p2 == nullptr ? exe : (p2+1U) ;
214 return p1 > p2 ? p1 : p2 ;
215}
216
217void G::Arg::parseImp( const std::string & command_line )
218{
219 constexpr std::string_view ws( " \t" , 2U ) ;
220 constexpr std::string_view nbws( "\0\0" , 2U ) ;
221 static_assert( ws.size() == nbws.size() , "" ) ;
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
229{
230 return exeImp( true ) ;
231}
232
233#ifndef G_LIB_SMALL
234G::Path G::Arg::exe( std::nothrow_t )
235{
236 return exeImp( false ) ;
237}
238#endif
239
240G::Path G::Arg::exeImp( bool do_throw )
241{
242 Path process_exe = Process::exe() ;
243 if( !process_exe.empty() )
244 {
245 return process_exe ;
246 }
247 else if( m_v0.isAbsolute() )
248 {
249 return m_v0 ;
250 }
251 else if( !m_cwd.empty() )
252 {
253 return Path::join(m_cwd,m_v0).collapsed() ;
254 }
255 else if( do_throw )
256 {
257 throw Exception( "cannot determine the absolute path of the current executable" ,
258 G::is_windows() ? "" : "try mounting procfs" ) ;
259 }
260 else
261 {
262 return {} ;
263 }
264}
265
266#ifndef G_LIB_SMALL
267G::StringArray::const_iterator G::Arg::cbegin() const
268{
269 return m_array.size() <= 1U ? cend() : std::next( m_array.cbegin() ) ;
270}
271#endif
272
273G::StringArray::const_iterator G::Arg::cend() const
274{
275 return m_array.cend() ;
276}
277
A class which holds a represention of the argc/argv command line array, and supports simple command-l...
Definition: garg.h:50
std::size_t c() const
Returns the number of tokens in the command line, including the program name.
Definition: garg.cpp:182
static Arg windows()
Factory function for Windows using GetCommandLineW().
Definition: garg.cpp:70
std::size_t match(const std::string &prefix) const
Returns the index of the first argument that matches the given prefix.
Definition: garg.cpp:125
std::string v(std::size_t i) const
Returns the i'th argument.
Definition: garg.cpp:187
std::size_t index(std::string_view option, std::size_t option_args=0U, std::size_t default_=0U) const
Returns the index of the given option.
Definition: garg.cpp:174
Arg(int argc, char **argv)
Constructor taking argc/argv directly from main().
Definition: garg.cpp:34
std::size_t count(std::string_view option) const
Returns the number of times the given string appears in the list of arguments.
Definition: garg.cpp:102
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:159
StringArray::const_iterator cend() const
Returns the end iterator.
Definition: garg.cpp:273
std::string prefix() const
Returns the basename of v(0) without any extension.
Definition: garg.cpp:198
std::string removeValue(std::string_view option, const std::string &default_={})
Removes the given single-valued option and its value.
Definition: garg.cpp:151
StringArray array(unsigned int shift=0U) const
Returns the arguments as a string array, with an optional shift.
Definition: garg.cpp:88
bool remove(std::string_view option, std::size_t option_args=0U)
Removes the given option and its arguments.
Definition: garg.cpp:142
static Path exe()
Returns Process::exe() or in exceptional circumstances an absolute path constructed from v0() and the...
Definition: garg.cpp:228
bool contains(std::string_view 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:96
StringArray::const_iterator cbegin() const
Returns a begin iterator, advanced to exclude argv0.
Definition: garg.cpp:267
static Path v0()
Returns a copy of argv[0] from the first call to the argc/argv constructor overload or windows().
Definition: garg.cpp:82
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:82
static Path join(const StringArray &parts)
Builds a path from a set of parts.
Definition: gpath.cpp:434
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:339
Path withoutExtension() const
Returns a path without the basename extension, if any.
Definition: gpath.cpp:357
Path collapsed() const
Returns the path with "foo/.." and "." parts removed, so far as is possible without changing the mean...
Definition: gpath.cpp:459
std::string str() const
Returns the path string.
Definition: gpath.h:243
bool empty() const noexcept
Returns true if the path is empty.
Definition: gpath.h:237
static Path cwd()
Returns the current working directory. Throws on error.
static Path exe()
Returns the absolute path of the current executable, independent of the argv array passed to main().
static void splitIntoTokens(const std::string &in, StringArray &out, std::string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:1119
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
Definition: gstr.cpp:1415
static bool headMatch(std::string_view in, std::string_view head) noexcept
Returns true if the string has the given start (or head is empty).
Definition: gstr.cpp:1359
static std::string dequote(const std::string &, char qq='\"' , char esc = '\\' , std::string_view ws = Str::ws() , std::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 bool replace(std::string &s, std::string_view from, std::string_view to, std::size_t *pos_p=nullptr)
A std::string_view overload.
Definition: gstr.cpp:226
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstringarray.h:30