Line data Source code
1 : /** \file application.cpp
2 : * \author Jared R. Males
3 : * \brief Implementation of a class for managing applications
4 : *
5 : */
6 :
7 : //***********************************************************************//
8 : // Copyright 2021 Jared R. Males (jaredmales@gmail.com)
9 : //
10 : // This file is part of mxlib.
11 : //
12 : // mxlib is free software: you can redistribute it and/or modify
13 : // it under the terms of the GNU General Public License as published by
14 : // the Free Software Foundation, either version 3 of the License, or
15 : // (at your option) any later version.
16 : //
17 : // mxlib is distributed in the hope that it will be useful,
18 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : // GNU General Public License for more details.
21 : //
22 : // You should have received a copy of the GNU General Public License
23 : // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
24 : //***********************************************************************//
25 :
26 : #include "app/application.hpp"
27 : #include "sys/environment.hpp"
28 :
29 : namespace mx
30 : {
31 : namespace app
32 : {
33 :
34 9 : application::~application()
35 : {
36 9 : return;
37 9 : }
38 :
39 8 : int application::main( int argc, char **argv )
40 : {
41 8 : m_argc = argc;
42 8 : m_argv = argv;
43 :
44 8 : setup( argc, argv );
45 :
46 8 : if(m_configOnly)
47 : {
48 1 : return 1;
49 : }
50 :
51 7 : if( doHelp )
52 : {
53 3 : help();
54 3 : return 1;
55 : }
56 :
57 4 : if( !m_preserveConfig )
58 : {
59 2 : config.clear();
60 : }
61 :
62 4 : return execute();
63 : }
64 :
65 2 : void application::setConfigPathGlobal( const std::string &s )
66 : {
67 2 : m_configPathGlobal = s;
68 2 : }
69 :
70 1 : void application::setConfigPathUser( const std::string &s )
71 : {
72 1 : m_configPathUser = s;
73 1 : }
74 :
75 1 : void application::setConfigPathLocal( const std::string &s )
76 : {
77 1 : m_configPathLocal = s;
78 1 : }
79 :
80 0 : void application::setupConfig() // virtual
81 : {
82 0 : return;
83 : }
84 :
85 0 : void application::loadConfig() // virtual
86 : {
87 0 : return;
88 : }
89 :
90 0 : int application::execute() // virtual
91 : {
92 0 : return 0;
93 : }
94 :
95 8 : void application::setup( int argc, char **argv )
96 : {
97 8 : invokedName = argv[0];
98 :
99 8 : setupStandardConfig();
100 8 : setupStandardHelp();
101 8 : setupBasicConfig();
102 8 : setupConfig();
103 :
104 8 : setDefaults( argc, argv );
105 :
106 : //------------------------------------
107 : // First check if help is requested.
108 : // This short circuits everything else
109 : //------------------------------------
110 16 : config.parseCommandLine( argc, argv, "help" );
111 :
112 8 : loadStandardHelp();
113 :
114 8 : if(doHelp)
115 : {
116 2 : return;
117 : }
118 :
119 : //------------------------------------
120 : // Now start reading config files
121 : //------------------------------------
122 6 : if((config.readConfig( m_configPathGlobal, m_requireConfigPathGlobal ) < 0) && m_requireConfigPathGlobal)
123 : {
124 1 : doHelp = true;
125 1 : return;
126 : }
127 :
128 5 : if((config.readConfig( m_configPathUser, m_requireConfigPathUser ) < 0) && m_requireConfigPathUser)
129 : {
130 0 : doHelp = true;
131 0 : return;
132 : }
133 :
134 5 : if((config.readConfig( m_configPathLocal, m_requireConfigPathLocal ) < 0) && m_requireConfigPathLocal)
135 : {
136 0 : doHelp = true;
137 0 : return;
138 : }
139 :
140 : // Parse CL just to get the CL config.
141 10 : config.parseCommandLine( argc, argv, "config" );
142 :
143 : // And now get the value of it and parse it.
144 5 : loadStandardConfig();
145 5 : config.readConfig( m_configPathCL );
146 :
147 : // Now parse the command line for real.
148 10 : config.parseCommandLine( argc, argv );
149 :
150 5 : loadStandardHelp(); //Check one more time, could have been config-filed
151 :
152 5 : if(doHelp)
153 : {
154 0 : return;
155 : }
156 :
157 5 : loadBasicConfig();
158 5 : loadConfig();
159 :
160 5 : checkConfig();
161 : }
162 :
163 1 : int application::reReadConfig()
164 : {
165 1 : config.readConfig( m_configPathGlobal );
166 1 : config.readConfig( m_configPathUser );
167 1 : config.readConfig( m_configPathLocal );
168 :
169 1 : config.readConfig( m_configPathCL );
170 :
171 : // Now parse the command line for real.
172 2 : config.parseCommandLine( m_argc, m_argv );
173 :
174 1 : return 0;
175 : }
176 :
177 9 : void application::setDefaults( int argc, char **argv )
178 : {
179 : static_cast<void>( argc );
180 : static_cast<void>( argv );
181 :
182 9 : std::string tmp;
183 :
184 9 : if( m_configPathGlobal_env != "" )
185 : {
186 1 : m_configPathGlobal = sys::getEnv( m_configPathGlobal_env );
187 : }
188 :
189 9 : if( m_configPathUser_env != "" )
190 : {
191 1 : m_configPathUser = sys::getEnv( m_configPathUser_env );
192 : }
193 :
194 9 : if( m_configPathUser != "" )
195 : {
196 : // If it's a relative path, add it to the HOME directory
197 1 : if( m_configPathUser[0] != '/' && m_configPathUser[0] != '~' )
198 : {
199 0 : tmp = sys::getEnv( "HOME" );
200 0 : tmp += "/" + m_configPathUser;
201 0 : m_configPathUser = tmp;
202 : }
203 : }
204 :
205 9 : if( m_configPathLocal_env != "" )
206 : {
207 1 : std::string cpl = sys::getEnv( m_configPathLocal_env.c_str() );
208 1 : if(cpl.size() > 0)
209 : {
210 1 : m_configPathLocal = cpl;
211 1 : if( m_configPathLocal.back() != '/' )
212 : {
213 1 : m_configPathLocal += '/';
214 : }
215 : }
216 1 : }
217 :
218 9 : if( m_configPathCLBase_env != "" )
219 : {
220 1 : std::string cpclbase = sys::getEnv( m_configPathCLBase_env.c_str() );
221 :
222 1 : if( cpclbase.size() > 0 )
223 : {
224 1 : m_configPathCLBase = cpclbase;
225 1 : if( m_configPathCLBase.back() != '/' )
226 : {
227 1 : m_configPathCLBase += '/';
228 : }
229 : }
230 1 : }
231 :
232 18 : return;
233 9 : }
234 :
235 8 : void application::setupStandardConfig() // virtual
236 : {
237 112 : config.add( "config", "c", "config", argType::Required, "", "config", false, "string", "A local config file" );
238 8 : }
239 :
240 8 : void application::setupStandardHelp() // virtual
241 : {
242 112 : config.add( "help", "h", "help", argType::True, "", "", false, "none", "Print this message and exit" );
243 8 : }
244 :
245 5 : void application::loadStandardConfig() // virtual
246 : {
247 10 : config( m_configPathCL, "config" );
248 5 : m_configPathCL = m_configPathCLBase + m_configPathCL;
249 5 : }
250 :
251 13 : void application::loadStandardHelp() // virtual
252 : {
253 26 : config( doHelp, "help" );
254 13 : }
255 :
256 8 : void application::setupBasicConfig() // virtual
257 : {
258 8 : return;
259 : }
260 :
261 5 : void application::loadBasicConfig() // virtual
262 : {
263 5 : return;
264 : }
265 :
266 5 : void application::checkConfig() // virtual
267 : {
268 5 : return;
269 : }
270 :
271 3 : void application::optionHelp( configTarget &tgt,
272 : ioutils::textTable &tt ) // virtual
273 : {
274 3 : std::string tmp;
275 3 : int row = tgt.orderAdded;
276 :
277 3 : if( tgt.shortOpt != "" )
278 : {
279 3 : tmp = "-" + tgt.shortOpt;
280 3 : tt.addCell( row, 0, tmp );
281 : }
282 :
283 3 : if( tgt.longOpt != "" )
284 : {
285 3 : tmp = "--" + tgt.longOpt;
286 3 : tt.addCell( row, 1, tmp );
287 : }
288 :
289 3 : tmp = "";
290 3 : if( tgt.section != "" )
291 : {
292 1 : tmp = tgt.section + ".";
293 : }
294 :
295 3 : if( tgt.keyword != "" )
296 : {
297 2 : tmp += tgt.keyword;
298 2 : tt.addCell( row, 2, tmp );
299 : }
300 :
301 3 : if( tgt.helpType != "" )
302 : {
303 3 : tmp = "<" + tgt.helpType + "> ";
304 3 : tt.addCell( row, 3, tmp );
305 : }
306 :
307 3 : tt.addCell( row, 4, tgt.helpExplanation );
308 3 : }
309 :
310 1 : void application::help() // virtual
311 : {
312 1 : appConfigurator::targetIterator targIt;
313 1 : appConfigurator::clOnlyTargetIterator clOnlyTargIt;
314 :
315 1 : ioutils::textTable tt;
316 :
317 1 : int otherColWidth = m_helpSOColWidth + m_helpLOColWidth + m_helpCFColWidth + m_helpTypeColWidth;
318 :
319 : tt.m_colWidths = {
320 1 : m_helpSOColWidth, m_helpLOColWidth, m_helpCFColWidth, m_helpTypeColWidth, m_helpWidth - otherColWidth - 4 - 4 };
321 1 : tt.m_lineStart = " ";
322 1 : tt.m_colSep = " ";
323 1 : tt.m_rowSep = "";
324 :
325 1 : std::cerr << "usage: " << invokedName << " [options] " << m_nonOptionHelp << "\n";
326 1 : std::cerr << "\n";
327 1 : std::cerr << " Required arguments:\n";
328 :
329 1 : for( clOnlyTargIt = config.clOnlyTargets.begin(); clOnlyTargIt != config.clOnlyTargets.end(); ++clOnlyTargIt )
330 : {
331 0 : if( clOnlyTargIt->isRequired == true )
332 : {
333 0 : optionHelp( *clOnlyTargIt, tt );
334 : }
335 : }
336 :
337 4 : for( targIt = config.m_targets.begin(); targIt != config.m_targets.end(); ++targIt )
338 : {
339 3 : if( targIt->second.isRequired == true )
340 : {
341 0 : optionHelp( targIt->second, tt );
342 : }
343 : }
344 :
345 1 : tt.outPut( std::cerr );
346 1 : tt.m_rows.clear();
347 :
348 : // row = 0;
349 1 : std::cerr << "\n Optional arguments:\n";
350 :
351 1 : for( clOnlyTargIt = config.clOnlyTargets.begin(); clOnlyTargIt != config.clOnlyTargets.end(); ++clOnlyTargIt )
352 : {
353 0 : if( clOnlyTargIt->isRequired == false )
354 : {
355 0 : optionHelp( *clOnlyTargIt, tt );
356 : }
357 : }
358 :
359 4 : for( targIt = config.m_targets.begin(); targIt != config.m_targets.end(); ++targIt )
360 : {
361 3 : if( targIt->second.isRequired == false )
362 : {
363 3 : optionHelp( targIt->second, tt );
364 : }
365 : }
366 :
367 1 : tt.outPut( std::cerr );
368 1 : }
369 :
370 : } // namespace app
371 : } // namespace mx
|