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