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