GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: tmp_project/OptionParser/src/OptionParser.cpp Lines: 209 221 94.6 %
Date: 2024-12-09 15:30:52 Branches: 220 269 81.8 %

Line Branch Exec Source
1
/***************************************
2
	Auteur : Pierre Aubert
3
	Mail : pierre.aubert@lapp.in2p3.fr
4
	Licence : CeCILL-C
5
****************************************/
6
7
#include "string_utils.h"
8
#include "OptionParser.h"
9
10
using namespace std;
11
12
///Default constructeur of OptionParser
13
/**	@param enableHelpOption : True to enable automatically the printing of the help option when the program is called with --help or -h
14
 * 	@param programVersion : version of the program to be printed on --version or -v options
15
*/
16
61
OptionParser::OptionParser(bool enableHelpOption, const std::string & programVersion)
17
61
	:p_enableHelpOption(enableHelpOption), p_programVersion(programVersion)
18
{
19
61
	initialisationOptionParser();
20
61
}
21
22
///Copy constructor of OptionParser
23
/**	@param other : class to copy
24
*/
25
2
OptionParser::OptionParser(const OptionParser & other){
26
2
	copyOptionParser(other);
27
2
}
28
29
///Destructeur of OptionParser
30
68
OptionParser::~OptionParser(){
31
32
}
33
34
///Definition of equal operator of OptionParser
35
/**	@param other : class to copy
36
 * 	@return copied class
37
*/
38
2
OptionParser & OptionParser::operator = (const OptionParser & other){
39
2
	copyOptionParser(other);
40
2
	return *this;
41
}
42
43
///Set the example usage of the program
44
/**	@param example : example of usage
45
*/
46
19
void OptionParser::setExampleLongOption(const std::string & example){p_exempleLongOption = example;}
47
48
///Set the example usage of the program
49
/**	@param example : example of usage
50
*/
51
19
void OptionParser::setExampleShortOption(const std::string & example){p_exempleShortOption = example;}
52
53
///Add a mode in the option
54
/**	@param modeName : name of the new mode to be added
55
*/
56
64
void OptionParser::addMode(const std::string & modeName){
57
64
	OptionMode mode(modeName);
58
64
	mode.setEnableHelpOption(p_enableHelpOption);
59
64
	mode.setProgramVersion(p_programVersion);
60
64
	p_vecMode.push_back(mode);
61
64
	p_currentMode = p_vecMode.size() - 1lu;
62
64
}
63
64
///Close the current mode and go back to be default one
65
64
void OptionParser::closeMode(){
66
64
	p_currentMode = 0lu;
67
64
}
68
69
///Add an option in the OptionParser
70
/**	@param longOption : long option (start with --) as --version
71
 * 	@param shortOption : short option (start with -) as -v
72
 * 	@param optionType : type of the value to be parsed (INT, BOOL_ CHAR, FLOAT, STRING, FILENAME, DIRECTORY, etc)
73
 * 	@param isRequired : true if the option is required, false if it is optionnal
74
 * 	@param docString : documentation string of the option
75
*/
76
127
void OptionParser::addOption(const std::string & longOption, const std::string & shortOption, OptionType::OptionType optionType,
77
				bool isRequired, const std::string & docString)
78
{
79
254
	OptionValue value;
80
127
	value.setType(optionType);
81
254
	Option option(longOption, shortOption, value, isRequired, docString);
82
127
	option.setIsAllowEmpty(false);
83
127
	p_vecMode[p_currentMode].addOption(option);
84
127
}
85
86
///Add an option in the OptionParser
87
/**	@param longOption : long option (start with --) as --version
88
 * 	@param shortOption : short option (start with -) as -v
89
 * 	@param optionType : type of the value to be parsed (INT, BOOL_ CHAR, FLOAT, STRING, FILENAME, DIRECTORY, etc)
90
 * 	@param isRequired : true if the option is required, false if it is optionnal
91
 * 	@param isAllowEmpty : the given value can be empty
92
 * 	@param docString : documentation string of the option
93
*/
94
2
void OptionParser::addOption(const std::string & longOption, const std::string & shortOption, OptionType::OptionType optionType,
95
				bool isRequired, bool isAllowEmpty, const std::string & docString)
96
{
97
4
	OptionValue value;
98
2
	value.setType(optionType);
99
4
	Option option(longOption, shortOption, value, isRequired, docString);
100
2
	option.setIsAllowEmpty(isAllowEmpty);
101
2
	p_vecMode[p_currentMode].addOption(option);
102
2
}
103
104
///Print all the options
105
9
void OptionParser::print() const{
106


9
	if(p_exempleLongOption != "" || p_exempleShortOption != ""){
107
7
		cout << "Usage :" << endl;
108

7
		if(p_exempleLongOption != ""){cout << "\t" << p_exempleLongOption << endl;}
109

7
		if(p_exempleShortOption != ""){cout << "\t" << p_exempleShortOption << endl;}
110
	}
111
9
	cout << "Parameters :" << endl;
112
9
	VecMode::const_iterator it(p_vecMode.begin());
113
9
	it->print();
114
9
	++it;
115
13
	while(it != p_vecMode.end()){
116
4
		it->print();
117
4
		++it;
118
	}
119
9
}
120
121
///Parse the arguments passed to the program
122
/**	@param argc : number of parameters passed to the program
123
 * 	@param argv : list of arguments passed to the program
124
*/
125
59
void OptionParser::parseArgument(int argc, char** argv){
126
89
	ArgParser parser(argc, argv);
127
59
	if(parser.isBashCompletionMode()){
128
22
		parseArgumentBashCompletion(parser);
129
	}else{
130
37
		parseArgumentNormalUse(parser);
131
	}
132
30
}
133
134
///Get default mode
135
/**	@return default mode
136
*/
137
6
const OptionMode & OptionParser::getDefaultMode() const{
138
6
	return p_vecMode[0lu];
139
}
140
141
///Get default mode
142
/**	@return default mode
143
*/
144
21
OptionMode & OptionParser::getDefaultMode(){
145
21
	return p_vecMode[0lu];
146
}
147
148
///Get mode by name
149
/**	@param name : name of the mode to be returned
150
 *	@return corresponding mode if the mode exists or the default mode otherwise
151
*/
152
4
const OptionMode & OptionParser::getMode(const std::string & name) const{
153
8
	for(VecMode::const_iterator it(p_vecMode.begin()); it != p_vecMode.end(); ++it){
154
6
		if(it->getName() == name){
155
2
			return *it;
156
		}
157
	}
158
2
	return getDefaultMode();
159
}
160
161
///Get mode by name
162
/**	@param name : name of the mode to be returned
163
 *	@return corresponding mode if the mode exists or the default mode otherwise
164
*/
165
38
OptionMode & OptionParser::getMode(const std::string & name){
166
91
	for(VecMode::iterator it(p_vecMode.begin()); it != p_vecMode.end(); ++it){
167
89
		if(it->getName() == name){
168
36
			return *it;
169
		}
170
	}
171
2
	return getDefaultMode();
172
}
173
174
///Check if the given mode name does exist
175
/**	@param name : name of the mode
176
 * 	@return true if the given mode name does exist, false otherwise
177
*/
178
41
bool OptionParser::isModeExist(const std::string & name) const{
179
41
	bool isSearch(true);
180
41
	VecMode::const_iterator it(p_vecMode.begin());
181

111
	while(isSearch && it != p_vecMode.end()){
182
70
		isSearch &= it->getName() != name;
183
70
		++it;
184
	}
185
41
	return !isSearch;
186
}
187
188
///Copy function of OptionParser
189
/**	@param other : class to copy
190
*/
191
4
void OptionParser::copyOptionParser(const OptionParser & other){
192
4
	p_vecMode = other.p_vecMode;
193
4
	p_currentMode = other.p_currentMode;
194
4
	p_exempleShortOption = other.p_exempleShortOption;
195
4
	p_exempleLongOption = other.p_exempleLongOption;
196
4
	p_programVersion = other.p_programVersion;
197
4
	p_enableHelpOption = other.p_enableHelpOption;
198
4
}
199
200
///Initialisation function of the class OptionParser
201
61
void OptionParser::initialisationOptionParser(){
202
61
	p_currentMode = 0lu;
203
61
	p_currentParserMode = NULL;
204
183
	OptionMode defaultMode;
205
61
	p_vecMode.push_back(defaultMode);
206
61
}
207
208
///Classical argument parsing mode
209
/**	@param parser : parser to be used
210
*/
211
37
void OptionParser::parseArgumentNormalUse(ArgParser & parser){
212
37
	p_currentParserMode = &p_vecMode.front();
213
72
	while(!parser.isEndOfOption()){
214
42
		if(p_enableHelpOption){
215

42
			if(parser.getCurrentOption() == "--help" || parser.getCurrentOption() == "-h"){
216
1
				print();
217
1
				exit(0);
218
			}
219
		}
220
41
		if(p_programVersion != ""){
221

41
			if(parser.getCurrentOption() == "--version" || parser.getCurrentOption() == "-v"){
222
				cout << "Program version : " << p_programVersion << endl;
223
				exit(0);
224
			}
225
		}
226
41
		OptionMode & currentMode = getParserMode(parser);
227
41
		if(!currentMode.parseOption(parser)){
228
			std::string modeName(currentMode.getName());
229
			std::string modeError("");
230
			if(modeName != ""){
231
				modeError = " in mode '"+modeName+"' ";
232
			}
233
			throw std::runtime_error("OptionParser::parseArgument : unknown option '"+parser.getCurrentOption()+"'" + modeError);
234
		}
235
	}
236
30
	if(!checkArgument()){
237
		throw std::runtime_error("OptionParser::parseArgument : missing argument");
238
	}
239
30
}
240
241
///Bash completion argument parsing mode
242
/**	@param parser : parser to be used
243
*/
244
22
void OptionParser::parseArgumentBashCompletion(ArgParser & parser){
245
	//First step, we parse normally the existing arguments
246
22
	std::string cursorOption(parser.getCursorOption());
247
22
	std::string prevCursorOption(parser.getPrevCursorOption());
248
22
	Option * partialOption = NULL;
249
45
	while(!parser.isEndOfOption()){		//We loop to find the option which has not been parsed well
250
23
		bool isSearch(true);
251
23
		VecMode::iterator itMode = p_vecMode.begin();
252


68
		while(!parser.isEndOfOption() && itMode != p_vecMode.end() && isSearch){
253
45
			isSearch = !itMode->parseOption(parser, partialOption);
254
45
			++itMode;
255
		}
256
23
		if(isSearch){		//If no option matches, we go to the next argument
257
11
			parser.getNextOption();
258
		}
259
	}
260
	//Complete the ongoing option (filename, directory, enum value, etc)
261
// 	if(partialOption != NULL){		//The option name has no ambiguity but we expect a value
262
// 		std::string possibleValue("");
263
// 		partialOption->getPossibleValue(possibleValue, cursorOption);
264
// 		cout << possibleValue << endl;
265
// 	}else{
266
		//Let's get the mode which is currently parsed
267
		//If all the options are fine, we have to show the remaning options. That's the following
268
22
		std::string possibleValue("");
269
22
		if(completeOptionValue(possibleValue, cursorOption, prevCursorOption)){
270
// 			saveFileContent("listPossibleValues.txt", possibleValue);
271
8
			std::cout << possibleValue << std::endl;
272
		}else{
273
			//Then, get the remaning arguments (not parsed yet)
274
28
			std::string possibleOption("");
275
14
			getPossibleOption(possibleOption, cursorOption);
276
14
			getPossibleOtherOption(possibleOption, cursorOption);
277
278
// 			saveFileContent("listPossibleOption.txt", possibleOption);
279
14
			std::cout << possibleOption << std::endl;
280
		}
281
// 	}
282
22
	exit(0);
283
// 	La marche a suivre est assez simple
284
// 		On renvoie une ligne par argument possible
285
// 		Si il y en a qu'une seule, elle sera ajouté à la fin de la ligne de commande
286
// 	DONE : mettre en place un méchanisme pour générer le bash qui appellera le programme différemment
287
}
288
289
///Check the argument of the parser
290
/**	@return true if all the required arguments are set, false otherwise
291
*/
292
30
bool OptionParser::checkArgument() const{
293
30
	bool isArgOk(true);
294
30
	VecMode::const_iterator it(p_vecMode.begin());
295

80
	while(it != p_vecMode.end() && isArgOk){
296
50
		isArgOk = it->checkArgument();
297
50
		++it;
298
	}
299
30
	return isArgOk;
300
}
301
302
///Get a mode if it exist
303
/**	@param parser : parser to be used
304
 * 	@return corresponding mode
305
*/
306
41
OptionMode & OptionParser::getParserMode(ArgParser & parser){
307
82
	std::string currentOption(parser.getCurrentOption());
308
41
	if(isModeExist(currentOption)){
309
16
		OptionMode & mode = getMode(currentOption);
310
16
		p_currentParserMode = &mode;
311
16
		parser.getNextOption();
312
16
		return mode;
313
	}else{
314
25
		return *p_currentParserMode;
315
	}
316
}
317
318
///Get the currently parsed OptionMode
319
/**	@return pointer to the currently parsed OptionMode, it there is one, or NULL is there is not
320
*/
321
9
const OptionMode * OptionParser::getCurrentlyParsedMode() const{
322
9
	const OptionMode * mode = NULL;
323
9
	VecMode::const_iterator it(p_vecMode.begin());
324

29
	while(it != p_vecMode.end() && mode == NULL){
325


20
		if(it->isCurrentlyParsed() && it->getName() != ""){
326
8
			mode = &(*it);
327
		}
328
20
		++it;
329
	}
330
9
	return mode;
331
}
332
333
///Complete the possible value of an option (FILENAME, DIRECTORY, FILE_OR_DIR)
334
/**	@param[out] possibleValue : possible value
335
 * 	@param cursorOption : option of the cursor which is currently completed
336
 * 	@param prevCursorOption : previous option of the cursor
337
*/
338
22
bool OptionParser::completeOptionValue(std::string & possibleValue, const std::string & cursorOption, const std::string & prevCursorOption) const{
339
// 	std::cerr << "OptionParser::completeOptionValue : cursorOption = '"<<cursorOption<<"', prevCursorOption = '"<<prevCursorOption<<"'" << std::endl;
340
44
	std::string valueToBeCompleted("");
341
	//Check is the cursor option starts with a long option (--option=value) because the value can be completed
342
22
	const Option * op = getLongOptionValue(valueToBeCompleted, cursorOption);
343
22
	if(op == NULL){		//Check is the previous option corresponds to an existing option, to complete the value given by the current option
344
22
		op = getSplitOptionValue(valueToBeCompleted, cursorOption, prevCursorOption);
345
	}
346
22
	if(op != NULL){
347
8
		op->getPossibleValue(possibleValue, valueToBeCompleted);
348
// 		std::cerr << "OptionParser::completeOptionValue : possibleValue = '"<<possibleValue<<"'" << std::endl;
349
8
		return true;
350
	}
351
14
	return false;
352
}
353
354
///Get the long option value to be completed
355
/**	@param[out] valueToBeCompleted : base of the value to be completed
356
 * 	@param cursorOption : option of the cursor which is currently completed
357
 * 	@return pointer to the Option to be completed or NULL if there is not
358
*/
359
22
const Option * OptionParser::getLongOptionValue(std::string & valueToBeCompleted, const std::string & cursorOption) const{
360
22
	if(cursorOption == ""){return NULL;}
361
15
	const Option * op = NULL;
362
15
	VecMode::const_iterator itMode(p_vecMode.begin());
363

54
	while(itMode != p_vecMode.end() && op == NULL){
364
39
		if(itMode->isCurrentlyParsed()){
365
27
			const VecOption & vecOp = itMode->getVecOption();
366
27
			VecOption::const_iterator itOp(vecOp.begin());
367

66
			while(itOp != vecOp.end() && op == NULL){
368
78
				std::string fullOp("--" + itOp->getLongName() + "=");
369
39
				if(isSameBegining(cursorOption, fullOp)){
370
					op = &(*itOp);
371
					valueToBeCompleted = cursorOption.substr(fullOp.size());
372
				}
373
39
				++itOp;
374
			}
375
		}
376
39
		++itMode;
377
	}
378
15
	return op;
379
}
380
381
///Get the split option (without =) value to be completed
382
/**	@param[out] valueToBeCompleted : base of the value to be completed
383
 * 	@param cursorOption : option of the cursor which is currently completed
384
 * 	@param prevCursorOption : previous option of the cursor
385
 * 	@return pointer to the Option to be completed or NULL if there is not
386
*/
387
22
const Option * OptionParser::getSplitOptionValue(std::string & valueToBeCompleted, const std::string & cursorOption, const std::string & prevCursorOption) const{
388
22
	if(prevCursorOption == ""){return NULL;}
389
16
	const Option * op = NULL;
390
16
	VecMode::const_iterator itMode(p_vecMode.begin());
391

55
	while(itMode != p_vecMode.end() && op == NULL){
392
39
		if(itMode->isCurrentlyParsed()){		//Search only in the mode which is currently parsed
393
31
			const VecOption & vecOp = itMode->getVecOption();
394
31
			VecOption::const_iterator itOp(vecOp.begin());
395

75
			while(itOp != vecOp.end() && op == NULL){
396

88
				std::string fullLongOp("--" + itOp->getLongName()), fullShortOption("-" + itOp->getShortName());
397
44
				if(fullLongOp == prevCursorOption){
398
8
					op = &(*itOp);
399
8
					valueToBeCompleted = cursorOption;
400
36
				}else if(fullShortOption == prevCursorOption){
401
					op = &(*itOp);
402
					valueToBeCompleted = cursorOption;
403
				}
404
44
				++itOp;
405
			}
406
		}
407
39
		++itMode;
408
	}
409
16
	return op;
410
}
411
412
///Get the possible options which can be used
413
/**	@param[out] possibleOption : possible options for the bash completion
414
 * 	@param cursorOption : option of the cursor which is currently completed
415
*/
416
14
void OptionParser::getPossibleOption(std::string & possibleOption, const std::string & cursorOption) const{
417
14
	if(p_vecMode.size() == 1lu){
418
5
		p_vecMode.front().getPossibleOption(possibleOption, cursorOption);
419
	}else{
420
9
		const OptionMode * currentlyParsedMode = getCurrentlyParsedMode();
421
9
		if(currentlyParsedMode != NULL){
422
8
			currentlyParsedMode->getPossibleOption(possibleOption, cursorOption);
423
		}else{
424
4
			for(VecMode::const_iterator itMode = p_vecMode.begin(); itMode != p_vecMode.end(); ++itMode){
425
3
				itMode->getPossibleMode(possibleOption, cursorOption);
426
			}
427
		}
428
	}
429
14
}
430
431
///Get the possible other options which can be used
432
/**	@param[out] possibleOption : possible options for the bash completion
433
 * 	@param cursorOption : option of the cursor which is currently completed
434
*/
435
14
void OptionParser::getPossibleOtherOption(std::string & possibleOption, const std::string & cursorOption) const{
436
28
	std::vector<std::string> vecOtherOption;
437
14
	vecOtherOption.push_back("--help");
438
14
	vecOtherOption.push_back("-h");
439
14
	vecOtherOption.push_back("--version");
440
14
	vecOtherOption.push_back("-v");
441
442
70
	for(std::vector<std::string>::iterator it(vecOtherOption.begin()); it != vecOtherOption.end(); ++it){
443
112
		std::string optionStr(*it);
444
56
		if(cursorOption == ""){
445
20
			possibleOption += optionStr + " ";
446
		}else{
447
36
			if(isSameBegining(optionStr, cursorOption)){
448
19
				possibleOption += optionStr + " ";
449
			}
450
		}
451
	}
452
14
}
453