]> git.donarmstrong.com Git - bamtools.git/blob - bamtools_options.cpp
Implemented Mosaik-style command line parser, instead of former GetOpt parser. Setup...
[bamtools.git] / bamtools_options.cpp
1 #include "bamtools_options.h"
2 #include <cstdio>
3 #include <cstdlib>
4 #include <cstring>
5 #include <iomanip>
6 #include <sstream>
7 using namespace std;
8 using namespace BamTools;
9
10 string Options::m_programName;                   // the program name
11 string Options::m_description;                   // the main description
12 string Options::m_exampleArguments;              // the example arguments
13 vector<OptionGroup> Options::m_optionGroups;     // stores the option groups
14 map<string, OptionValue> Options::m_optionsMap;  // stores the options in a map
15 string Options::m_stdin  = "stdin";              // string representation of stdin
16 string Options::m_stdout = "stdout";             // string representation of stdout
17
18 // adds a simple option to the parser
19 void Options::AddOption(const string& argument, const string& optionDescription, bool& foundArgument, OptionGroup* group) {
20
21     Option o;
22     o.Argument    = argument;
23     o.Description = optionDescription;
24     o.StoreValue  = false;
25     group->Options.push_back(o);
26
27     OptionValue ov;
28     ov.pFoundArgument = &foundArgument;
29     ov.StoreValue     = false;
30
31     m_optionsMap[argument] = ov;
32 }
33
34 // creates an option group
35 OptionGroup* Options::CreateOptionGroup(const string& groupName) {
36     OptionGroup og;
37     og.Name = groupName;
38     m_optionGroups.push_back(og);
39     return &m_optionGroups[m_optionGroups.size() - 1];
40 }
41
42 // displays the help menu
43 void Options::DisplayHelp(void) {
44
45     // initialize
46     char argumentBuffer[ARGUMENT_LENGTH + 1];
47     ostringstream sb;
48
49     char indentBuffer[MAX_LINE_LENGTH - DESC_LENGTH + 1];
50     memset(indentBuffer, ' ', MAX_LINE_LENGTH - DESC_LENGTH);
51     indentBuffer[MAX_LINE_LENGTH - DESC_LENGTH] = 0;
52
53     // display the menu
54     printf("Description: %s.\n\n", m_description.c_str());
55
56     printf("Usage: ");
57     printf("%s", m_programName.c_str());
58     printf(" %s\n\n", m_exampleArguments.c_str());
59
60     vector<Option>::const_iterator optionIter;
61     vector<OptionGroup>::const_iterator groupIter;
62     
63     for (groupIter = m_optionGroups.begin(); groupIter != m_optionGroups.end(); ++groupIter) {
64         
65         printf("%s:\n", groupIter->Name.c_str());
66
67         for (optionIter = groupIter->Options.begin(); optionIter != groupIter->Options.end(); ++optionIter) {
68
69             if (optionIter->StoreValue) 
70                 snprintf(argumentBuffer, ARGUMENT_LENGTH + 1, "  %s <%s>", optionIter->Argument.c_str(), optionIter->ValueDescription.c_str());
71             else 
72                 snprintf(argumentBuffer, ARGUMENT_LENGTH + 1, "  %s", optionIter->Argument.c_str());
73             printf("%-35s ", argumentBuffer);
74
75             string description = optionIter->Description;
76
77             // handle default values
78             if (optionIter->HasDefaultValue) {
79                 
80                 sb.str("");
81                 sb << description << " [";
82
83                 if (optionIter->DefaultValue.is_type<unsigned int>()) {
84                     sb << (unsigned int)optionIter->DefaultValue;
85                 } else if (optionIter->DefaultValue.is_type<unsigned char>()) {
86                     sb << (unsigned short)(unsigned char)optionIter->DefaultValue;
87                 } else if (optionIter->DefaultValue.is_type<float>()) {
88                     sb << std::fixed << std::setprecision(2) << (float)optionIter->DefaultValue;
89                 } else if (optionIter->DefaultValue.is_type<double>()) {
90                     sb << std::fixed << std::setprecision(4) << (double)optionIter->DefaultValue;
91                 } else if (optionIter->DefaultValue.is_type<std::string>()) {
92                     const std::string stringValue = optionIter->DefaultValue;
93                     sb << stringValue;
94                 } else {
95                     printf("ERROR: Found an unsupported data type for argument %s when casting the default value.\n", optionIter->Argument.c_str());
96                     exit(1);
97                 }
98
99                 sb << "]";
100                 description = sb.str(); 
101             }
102
103             if (description.size() <= DESC_LENGTH_FIRST_ROW) {
104                 printf("%s\n", description.c_str());
105             } else {
106
107                 // handle the first row
108                 const char* pDescription = description.data();
109                 unsigned int cutIndex = DESC_LENGTH_FIRST_ROW;
110                 while(pDescription[cutIndex] != ' ') cutIndex--;
111                 printf("%s\n", description.substr(0, cutIndex).c_str());
112                 description = description.substr(cutIndex + 1);
113
114                 // handle subsequent rows
115                 while(description.size() > DESC_LENGTH) {
116                     pDescription = description.data();
117                     cutIndex = DESC_LENGTH;
118                     while(pDescription[cutIndex] != ' ') cutIndex--;
119                     printf("%s%s\n", indentBuffer, description.substr(0, cutIndex).c_str());
120                     description = description.substr(cutIndex + 1);
121                 }
122
123                 // handle last row
124                 printf("%s%s\n", indentBuffer, description.c_str());
125             }                       
126         }
127
128         printf("\n");
129     }
130
131     printf("Help:\n"); 
132     printf("  --help, -h                        shows this help text\n");
133     exit(1);
134 }
135
136 // parses the command line
137 void Options::Parse(int argc, char* argv[], int offset) {
138
139     // initialize
140     map<string, OptionValue>::const_iterator ovMapIter;
141     map<string, OptionValue>::const_iterator checkMapIter;
142     const int LAST_INDEX = argc - 1;
143     ostringstream errorBuilder;
144     bool foundError = false;
145     char* end_ptr = NULL;
146     const string ERROR_SPACER(7, ' ');
147
148     // check if we should show the help menu
149     bool showHelpMenu = false;
150     if (argc > 1) {
151         for (int i = 1; i < argc; i++) {
152             const std::string argument = argv[i];
153             if ( (argument == "-h") || (argument == "--help") || (argument == "help") ) 
154                 showHelpMenu = true;
155         }
156     } else showHelpMenu = true;
157
158     if (showHelpMenu) 
159         DisplayHelp();
160
161     // check each argument
162     for (int i = offset+1; i < argc; i++) {
163       
164         const string argument = argv[i];
165         ovMapIter = m_optionsMap.find(argument);
166
167         if (ovMapIter == m_optionsMap.end()) {
168             errorBuilder << ERROR_SPACER << "An unrecognized argument was found: " << argument << std::endl;
169             foundError = true;
170
171         } else {
172
173             *ovMapIter->second.pFoundArgument = true;
174
175             // grab the value
176             if (ovMapIter->second.StoreValue) {
177
178                 if (i < LAST_INDEX) {
179
180                     // check if the next argument is really a command line option
181                     const string val = argv[i + 1]; 
182                     checkMapIter = m_optionsMap.find(val);
183
184                     if (checkMapIter == m_optionsMap.end()) {
185                         
186                         ++i;
187                         
188                         if (ovMapIter->second.VariantValue.is_type<unsigned int>()) {
189                             const unsigned int uint32 = (unsigned int)strtoul(val.c_str(), &end_ptr, 10);
190                             unsigned int* varValue = (unsigned int*)ovMapIter->second.pValue;
191                             *varValue = uint32;
192                         } else if (ovMapIter->second.VariantValue.is_type<unsigned char>()) {
193                             const unsigned char uint8 = (unsigned char)strtoul(val.c_str(), &end_ptr, 10);
194                             unsigned char* varValue = (unsigned char*)ovMapIter->second.pValue;
195                             *varValue = uint8;
196                         } else if (ovMapIter->second.VariantValue.is_type<uint64_t>()) {
197                             const uint64_t uint64 = strtoui64(val.c_str(), &end_ptr, 10);
198                             uint64_t* varValue = (uint64_t*)ovMapIter->second.pValue;
199                             *varValue = uint64;
200                         } else if (ovMapIter->second.VariantValue.is_type<double>()) {
201                             const double d = strtod(val.c_str(), &end_ptr);
202                             double* varValue = (double*)ovMapIter->second.pValue;
203                             *varValue = d;
204                         } else if (ovMapIter->second.VariantValue.is_type<float>()) {
205                             const float f = (float)strtod(val.c_str(), &end_ptr);
206                             float* varValue = (float*)ovMapIter->second.pValue;
207                             *varValue = f;
208                         } else if (ovMapIter->second.VariantValue.is_type<string>()) {
209                             string* pStringValue = (string*)ovMapIter->second.pValue;
210                             *pStringValue = val;
211                         } else if (ovMapIter->second.VariantValue.is_type<vector<string> >()) {
212                             vector<string>* pVectorValue = (vector<string>*)ovMapIter->second.pValue;
213                             pVectorValue->push_back(val);
214                         } else {
215                             printf("ERROR: Found an unsupported data type for argument %s when parsing the arguments.\n", argument.c_str());
216                             exit(1);
217                         }
218                     } else {
219                         errorBuilder << ERROR_SPACER << "The argument (" << argument << ") expects a value, but none was found." << endl;
220                         foundError = true;
221                     }
222                 } else {
223                     errorBuilder << ERROR_SPACER << "The argument (" << argument << ") expects a value, but none was found." << endl;
224                     foundError = true;
225                 }
226             }
227         }
228     }
229
230     // check if we missed any required parameters
231     for (ovMapIter = m_optionsMap.begin(); ovMapIter != m_optionsMap.end(); ++ovMapIter) {
232         if (ovMapIter->second.IsRequired && !*ovMapIter->second.pFoundArgument) {
233             errorBuilder << ERROR_SPACER << ovMapIter->second.ValueTypeDescription << " was not specified. Please use the " << ovMapIter->first << " parameter." << endl;
234             foundError = true;
235         }
236     }
237
238     // print the errors if any were found
239     if (foundError) {
240         printf("ERROR: Some problems were encountered when parsing the command line options:\n");
241         printf("%s\n", errorBuilder.str().c_str());
242         printf("For a complete list of command line options, type \"%s help %s\"\n", argv[0], argv[1]);
243         exit(1);
244     }
245 }
246
247 // sets the program info
248 void Options::SetProgramInfo(const string& programName, const string& description, const string& arguments) {
249     m_programName      = programName;
250     m_description      = description;
251     m_exampleArguments = arguments;
252 }
253
254 // return string representations of stdin
255 const string& Options::StandardIn(void)  { return m_stdin; }
256
257 // return string representations of stdout
258 const string& Options::StandardOut(void) { return m_stdout; }