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