1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2013-2015 Imperial College London
5  * Copyright 2013-2015 Andreas Schuh
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifndef MIRTK_Options_H
21 #define MIRTK_Options_H
22 
23 #include "mirtk/Path.h"
24 #include "mirtk/Stream.h"
25 
26 #include "mirtk/Parallel.h"
27 #include "mirtk/Profiling.h"
28 #include "mirtk/Terminal.h"
29 
30 #include "mirtk/CommonExport.h"
31 
32 
33 /*
34    \file  mirtk/Options.h
35    \brief Simple command-line parsing library.
36 
37    The macros defined by this module should reduce the number of lines of code
38    required to parse simple command-line arguments in the main function of a
39    program. Use them for example as follows:
40 
41    \code
42    for (ALL_OPTIONS) {
43      if      (OPTION("-steps" )) NumberOfIntegrationSteps = atoi(ARGUMENT);
44      else if (OPTION("-iters" )) NumberOfBCHSteps         = atoi(ARGUMENT);
45      else if (OPTION("-terms" )) NumberOfBCHTerms         = atoi(ARGUMENT);
46      else if (OPTION("-smooth")) SmoothBCHApproximation   = true;
47      else HANDLE_COMMON_OR_UNKNOWN_OPTION();
48    }
49    \endcode
50 
51    If your program has positional arguments which have to be specified by the
52    user before the optional arguments, use the following code instead:
53 
54    \code
55    REQUIRES_POSARGS(2);
56    const char *input_file  = POSARG(1);
57    const char *output_file = POSARG(2);
58    for (ALL_OPTIONS) {
59      // same as above
60    }
61    \endcode
62 
63    Other uses including options with multiple arguments may look like this:
64    \code
65    REQUIRES_POSARGS(0);
66    DISCARD_PARSED_OPTIONS();
67    int    x  = 1, y  = 1, z  = 1, t  = 1;
68    double dx = 1, dy = 1, dz = 1, dt = 1;
69    for (ALL_OPTIONS) {
70      // -dim <x> <y> <z> <t>
71      if (OPTION("-dim")) {
72        x = atoi(ARGUMENT);
73        y = atoi(ARGUMENT);
74        z = atoi(ARGUMENT);
75        t = atoi(ARGUMENT);
76      } else if (OPTION("-pixdim")) {
77        dx = atof(ARGUMENT);
78        dy = atof(ARGUMENT);
79        dz = atof(ARGUMENT);
80        dt = atof(ARGUMENT);
81      } else HANDLE_COMMON_OPTION();
82    }
83    // do something, then parse remaining options
84    bool foo = false;
85    for (ALL_OPTIONS) {
86      if (OPTION("-foo")) foo = true;
87      else HANDLE_UNKNOWN_OPTION();
88    }
89    \endcode
90 */
91 
92 
93 namespace mirtk {
94 
95 // =============================================================================
96 // Global standard options
97 // =============================================================================
98 
99 /// Verbosity of output messages
100 MIRTK_Common_EXPORT extern int verbose;
101 
102 /// Debug level, e.g., amount of intermediate data to write to disk
103 /// This flag can be combined with verbose after parsing the command options.
104 /// For example, "if (debug) verbose = 100;", or treated separately.
105 MIRTK_Common_EXPORT extern int debug;
106 
107 // =============================================================================
108 // Standard options
109 // =============================================================================
110 
111 /// Check if given option is a standard option
112 bool IsStandardOption(const char *);
113 
114 /// Parse standard option
115 void ParseStandardOption(int &, int &, char *[]);
116 
117 //// Print standard options of any IRTK command
118 void PrintStandardOptions(ostream &);
119 
120 //// Print common options of any IRTK command
121 void PrintCommonOptions(ostream &);
122 
123 // =============================================================================
124 // Command helper macros
125 // =============================================================================
126 
127 // -----------------------------------------------------------------------------
128 /// Print warning and continue
129 #define Warning(msg) \
130   cerr << "Warning: " << msg << endl
131 
132 // -----------------------------------------------------------------------------
133 /// Print error message and exit with error code 1
134 #define FatalError(msg) \
135   do { \
136     cerr << "Error: " << msg << endl; \
137     exit(1); \
138   } while (false)
139 
140 // -----------------------------------------------------------------------------
141 #define EXECNAME                   BaseName(argv[0]).c_str()
142 #define ARGIDX                     _i
143 #define OPTIDX                     ARGIDX
144 #define OPTNAME                    _option
145 #define DISCARD_PARSED_POSARGS()   _discard_parsed_posargs = true
146 #define DISCARD_PARSED_OPTIONS()   _discard_parsed_options = true
147 #define DISCARD_PARSED_ARGUMENTS() _discard_parsed_options = _discard_parsed_posargs = true
148 #define KEEP_PARSED_POSARGS()      _discard_parsed_posargs = false
149 #define KEEP_PARSED_OPTIONS()      _discard_parsed_options = false
150 #define KEEP_PARSED_ARGUMENTS()    _discard_parsed_options = _discard_parsed_posargs = false
151 #define ALL_ARGUMENTS              int ARGIDX = 1; ARGIDX < argc; ARGIDX++
152 #define ALL_POSARGS                int ARGIDX = 1; ARGIDX <= _numposarg; ARGIDX++
153 #define OPTIONAL_POSARGS           int ARGIDX = _posargc + 1; ARGIDX < argc && argv[ARGIDX][0] != '-'; ARGIDX++
154 #define ARGUMENTS_AFTER(pos)       int ARGIDX = (pos)+1; ARGIDX < argc; ARGIDX++
155 #define ALL_OPTIONS                ARGUMENTS_AFTER(_numposarg == -1 ? _posargc : _numposarg)
156 #define POSARG(i)                  _GetPositionalArgument((i), argc, argv)
157 #define NUM_POSARGS                (_numposarg == -1 ? _GetNumberOfPositionalArguments(argc, argv) : _numposarg)
158 #define IS_OPTION                  _IsOption(OPTIDX, argc, argv)
159 #define OPTION(opt)                _IsOption(OPTIDX, argc, argv, opt)
160 #define ARGUMENT                   _GetOptionArgument(OPTIDX, argc, argv)
161 #define HAS_ARGUMENT               _IsArgument(OPTIDX, argc, argv)
162 #define HELP_OPTION                (OPTION("-h") || OPTION("-help") || OPTION("--help"))
163 #define VERSION_OPTION             (OPTION("-version") || OPTION("--version") || OPTION("-revision"))
164 #define STANDARD_OPTION            IsStandardOption    (argv[OPTIDX])
165 #define PARALLEL_OPTION            IsParallelOption    (argv[OPTIDX])
166 #define PROFILING_OPTION           IsProfilingOption   (argv[OPTIDX])
167 #define TERMINAL_OPTION            IsTerminalOption    (argv[OPTIDX])
168 #define PARSE_STANDARD_OPTION()    ParseStandardOption (OPTIDX, argc, argv)
169 #define PARSE_PARALLEL_OPTION()    ParseParallelOption (OPTIDX, argc, argv)
170 #define PARSE_PROFILING_OPTION()   ParseProfilingOption(OPTIDX, argc, argv)
171 #define PARSE_TERMINAL_OPTION()    ParseTerminalOption (OPTIDX, argc, argv)
172 #define PARSE_ARGUMENT(value)      _ParseArgument(OPTNAME, ARGUMENT, value)
173 
174 // -----------------------------------------------------------------------------
175 #define REQUIRES_POSARGS(n)                                                    \
176   do {                                                                         \
177     _posargc = n;                                                              \
178     for (ALL_ARGUMENTS) {                                                      \
179       if      (HELP_OPTION   ) HANDLE_HELP_OPTION();                           \
180       else if (VERSION_OPTION) HANDLE_VERSION_OPTION();                        \
181     }                                                                          \
182     if (argc <= _posargc) {                                                    \
183       PrintHelp(EXECNAME);                                                     \
184       exit(1);                                                                 \
185     }                                                                          \
186     _numposarg = NUM_POSARGS;                                                  \
187     if (_numposarg < _posargc) _numposarg = _posargc;                          \
188   } while (false)
189 
190 // -----------------------------------------------------------------------------
191 #define EXPECTS_POSARGS(n)                                                     \
192   REQUIRES_POSARGS(n);                                                         \
193   do {                                                                         \
194     if (NUM_POSARGS > n) {                                                     \
195       PrintHelp(EXECNAME);                                                     \
196       cout << endl;                                                            \
197       cerr << "Error: Too many positional arguments!" << endl;                 \
198       exit(1);                                                                 \
199     }                                                                          \
200   } while (false)
201 
202 // -----------------------------------------------------------------------------
203 #define HANDLE_HELP_OPTION()                                                   \
204   do { PrintHelp(EXECNAME); exit(0); } while (false)
205 
206 // -----------------------------------------------------------------------------
207 #define HANDLE_VERSION_OPTION()                                                \
208   PARSE_STANDARD_OPTION()
209 
210 // -----------------------------------------------------------------------------
211 #define HANDLE_UNKNOWN_OPTION()                                                \
212   do {                                                                         \
213     PrintHelp(EXECNAME);                                                       \
214     cout << endl;                                                              \
215     cerr << "Error: Unknown option " << argv[OPTIDX] << " (index=" << OPTIDX << ")" << endl; \
216     exit(1);                                                                   \
217   } while (false)
218 
219 // -----------------------------------------------------------------------------
220 #define HANDLE_STANDARD_OPTION()                                               \
221   if      (HELP_OPTION     ) HANDLE_HELP_OPTION();                             \
222   else if (VERSION_OPTION  ) HANDLE_VERSION_OPTION();                          \
223   else if (STANDARD_OPTION ) PARSE_STANDARD_OPTION()
224 
225 // -----------------------------------------------------------------------------
226 #define HANDLE_STANDARD_OR_UNKNOWN_OPTION()                                    \
227   if      (HELP_OPTION     ) HANDLE_HELP_OPTION();                             \
228   else if (VERSION_OPTION  ) HANDLE_VERSION_OPTION();                          \
229   else if (STANDARD_OPTION ) PARSE_STANDARD_OPTION();                          \
230   else                       HANDLE_UNKNOWN_OPTION()
231 
232 // -----------------------------------------------------------------------------
233 #define HANDLE_BOOLEAN_OPTION(name, var)                                       \
234   if (OPTION("-" name)) {                                                      \
235     if (HAS_ARGUMENT) PARSE_ARGUMENT(var);                                     \
236     else var = true;                                                           \
237   } else if (OPTION("-no" name))                                               \
238     var = false
239 
240 // -----------------------------------------------------------------------------
241 #define HANDLE_BOOL_OPTION(name)                                               \
242   HANDLE_BOOLEAN_OPTION(#name, name)
243 
244 // -----------------------------------------------------------------------------
245 #define HANDLE_COMMON_OPTION()                                                 \
246   if      (HELP_OPTION     ) HANDLE_HELP_OPTION();                             \
247   else if (VERSION_OPTION  ) HANDLE_VERSION_OPTION();                          \
248   else if (STANDARD_OPTION ) PARSE_STANDARD_OPTION();                          \
249   else if (PARALLEL_OPTION ) PARSE_PARALLEL_OPTION();                          \
250   else if (PROFILING_OPTION) PARSE_PROFILING_OPTION();                         \
251   else if (TERMINAL_OPTION ) PARSE_TERMINAL_OPTION()
252 
253 // -----------------------------------------------------------------------------
254 #define HANDLE_COMMON_OR_UNKNOWN_OPTION()                                      \
255   if      (HELP_OPTION     ) HANDLE_HELP_OPTION();                             \
256   else if (VERSION_OPTION  ) HANDLE_VERSION_OPTION();                          \
257   else if (STANDARD_OPTION ) PARSE_STANDARD_OPTION();                          \
258   else if (PARALLEL_OPTION ) PARSE_PARALLEL_OPTION();                          \
259   else if (PROFILING_OPTION) PARSE_PROFILING_OPTION();                         \
260   else if (TERMINAL_OPTION ) PARSE_TERMINAL_OPTION();                          \
261   else                       HANDLE_UNKNOWN_OPTION()
262 
263 // -----------------------------------------------------------------------------
264 #define HANDLE_HELP_OR_VERSION()                                               \
265   do {                                                                         \
266     for (ALL_ARGUMENTS) {                                                      \
267       if      (HELP_OPTION   ) HANDLE_HELP_OPTION();                           \
268       else if (VERSION_OPTION) HANDLE_VERSION_OPTION();                        \
269     }                                                                          \
270   } while (false)
271 
272 // =============================================================================
273 // Private global variables/functions used by command-line parsing macros
274 // =============================================================================
275 
276 MIRTK_Common_EXPORT extern int         _posargc;                // Number of required positional arguments
277 MIRTK_Common_EXPORT extern int         _numposarg;              // Number of positional arguments
278 MIRTK_Common_EXPORT extern bool        _discard_parsed_posargs; // Whether to discard or keep parsed positional arguments
279 MIRTK_Common_EXPORT extern bool        _discard_parsed_options; // Whether to discard or keep parsed options
280 MIRTK_Common_EXPORT extern const char *_option;                 // Current option being parsed
281 
282 int _GetNumberOfPositionalArguments(int, char *[]);
283 
284 void  _DiscardArgument      (int &, int &, char *[]);
285 bool  _IsOption             (int &, int &, char *[], const char * = NULL);
286 bool  _IsArgument           (int,   int &, char *[]);
287 char *_GetPositionalArgument(int,   int &, char *[]);
288 char *_GetOptionArgument    (int &, int &, char *[]);
289 
290 // -----------------------------------------------------------------------------
291 template <class T>
_ParseArgument(const char * opt,const char * arg,T & value)292 void _ParseArgument(const char *opt, const char *arg, T &value)
293 {
294   if (!FromString(arg, value)) {
295     FatalError("Invalid " << opt << " argument: " << arg);
296   }
297 }
298 
299 
300 } // namespace mirtk
301 
302 #endif // MIRTK_Options_H
303