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