File: arg_parse_parse.h

package info (click to toggle)
seqan2 2.3.1%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 222,320 kB
  • ctags: 40,872
  • sloc: cpp: 252,894; ansic: 86,805; python: 6,534; sh: 985; xml: 570; makefile: 236; awk: 51
file content (395 lines) | stat: -rw-r--r-- 15,769 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2016, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================
// Author: Stephan Aiche <stephan.aiche@fu-berlin.de>
// ==========================================================================

#ifndef SEQAN_INCLUDE_SEQAN_ARG_PARSE_ARG_PARSE_PARSE_H_
#define SEQAN_INCLUDE_SEQAN_ARG_PARSE_ARG_PARSE_PARSE_H_

#include <seqan/arg_parse/arg_parse_option.h>
#include <seqan/arg_parse/argument_parser.h>
#include <seqan/arg_parse/arg_parse_ctd_support.h>
#include <seqan/arg_parse/arg_parse_version_check.h>

namespace seqan {

// ----------------------------------------------------------------------------
// Function parse()
// ----------------------------------------------------------------------------

/*!
 * @fn ArgumentParser#parse
 * @headerfile <seqan/arg_parse.h>
 * @brief Parse command line parameters.
 *
 * @signature TResult parse(parser, argc, argv[, outStream[, errStream]]);
 *
 * @param[in,out] parser    The ArgumentParser to use for parsing and for storing parse results.
 * @param[in]     argc      The number of arguments (<tt>int</tt>).
 * @param[in]     argv      The arguments (<tt>const char * argv[]</tt>).
 * @param[in,out] outStream The <tt>std::ostream</tt> to use for output.
 * @param[in,out] errStream The <tt>std::ostream</tt> to use for error output.
 *
 * @return TResult The parse result, of type @link ArgumentParser::ParseResult @endlink.
 *
 * This function must be called before retrieving any options or arguments from the parser.
 */

// Helper class for parsing command line arguments.
//
// Putting things into its a class allows us to structure the parsing in a fine way.

template <typename TChar>
class ArgumentParserHelper_
{
public:
    typedef ArgumentParser::TArgumentMapSize TArgumentPosition;

    // Reference to the ArgumentParser to parse for.
    ArgumentParser & parser;
    // The argc and argv from the main() method.
    int argc;
    TChar ** argv;

    // The parser's state is stored in the following variables.

    // The special argument "--" is ignored as an option and only arguments can follow.  The following flag holds the
    // state that this token was seen.
    bool seenDashDash;
    // The index of the current positional argument.
    TArgumentPosition currentArgument;

    ArgumentParserHelper_(ArgumentParser & parser, int argc, TChar * argv[])
            : parser(parser), argc(argc), argv(argv), seenDashDash(false), currentArgument(0)
    {}

    void reset()
    {
        seenDashDash = false;
        currentArgument = 0;
    }

    // Perform the argument parsing.
    void parseArgs()
    {
        reset();  // reset state

        // Parse binary name from command line if it was not set.
        if (empty(getAppName(parser)))
            _parseAppName(parser, argv[0]);

        for (int argi = 1; argi < argc; ++argi)
        {
            // after "--" ever arg is treated as argument (not as option), e.g. "rm -rf -- --file-name"
            // "-" is a treated as argument as for a filename arguments it represents stdin
            // everything else that begins with "-" is an option

            size_t argLen = strlen(argv[argi]);
            if (seenDashDash || argLen == 0 || ((argv[argi][0] != '-') || (argLen == 1))) //
                // Handle as position argument if we have seen "--" or does not start with dash.
                handleArgument(argv[argi]);
            else if (strcmp(argv[argi], "--") == 0)
                // If this is "--" then ignore the argument itself but set the seen flag for it.
                seenDashDash = true;
            else
                // This is an option.
                handleOption(argi, argv[argi]);
        }
    }

private:

    // Handle the given string as an argument.
    //
    // Throw ParseError in case of too many arguments.
    void handleArgument(char const * argStr)
    {
        // Check whether we have the largest number of allowed arguments already.
        if (parser.argumentList.size() <= currentArgument)
            SEQAN_THROW(ParseError("Too many arguments!"));

        ArgParseArgument & argument = getArgument(parser, currentArgument);
        _assignArgumentValue(argument, argStr);

        if (!isListArgument(argument))
            ++currentArgument;
    }

    // Handle the given string as an option.
    //
    // Throw InvalidOption if there is a formal error (== "-") or invalid option naparser.
    //
    // Throw MissingArgument in case of --key=value option but the option requires multiple values.
    void handleOption(int & argi, std::string arg)
    {
        if (arg == "-")
            SEQAN_THROW(InvalidOption("-"));

        if (arg[1] == '-')
            handleLongOption(argi, arg);
        else
            handleShortOption(argi, arg);
    }

    // Handle the given string as a long option.
    void handleLongOption(int & argi, std::string const & arg)
    {
        // We will store the option name and the value in these variables.
        std::string longOpt = arg;
        std::string val;

        // Split option in case of --key=value format.
        size_t t = arg.find('=');
        if (t != std::string::npos)  // is --key=value option
        {
            val = arg.substr(t + 1);
            longOpt = arg.substr(0, t);
        }
        longOpt.erase(0, 2);

        // Guard against invalid option names.
        if (!hasOption(parser, longOpt))
            SEQAN_THROW(InvalidOption(longOpt));

        // Parse out the values for the option from the command line arguments.
        ArgParseOption & opt = getOption(parser, longOpt);
        if (t != std::string::npos)  // was --key=value option
        {
            // We can only assign one value in this case.  If the option expected more than one value then this is an
            // error.
            if (numberOfAllowedValues(opt) != 1)
                SEQAN_THROW(MissingArgument(longOpt));
            // If everything is fine then we can assign the value to the option.
            _assignArgumentValue(opt, val);
        }
        else if (isFlagOption(opt))
        {
            // Handling flag options is simple.
            _assignArgumentValue(opt, "true");
        }
        else
        {
            // If we reach here, we have a --key value option and might require multiple values.

            // Guard against missing values.
            if (argi + (int)numberOfAllowedValues(opt) >= argc)
                SEQAN_THROW(MissingArgument(longOpt));
            // We have sufficient values, get them from argv and assign them to the option.
            for (int t = 0; t < (int)numberOfAllowedValues(opt); ++t)
                _assignArgumentValue(opt, argv[++argi]);
        }
    }

    // Handle the given string as a short option.
    void handleShortOption(int & argi, std::string const & arg)
    {
        // Parse out the short option.  This is made complicated because the user can concatenate multiple short options
        // into one.  NB: we do not check for and warn about possible ambiguities.
        //
        // We enumerate the possible option names starting from left to right, then sorted descendingly by length.
        // Boolean options can be squished together in this way without further restrictions.  If the option requires
        // additional arguments then it has to be the last in the short options.

        for (unsigned s = 1; s < arg.size(); ++s)
        {
            unsigned e = arg.size();
            for (; s < e; --e)
            {
                if (hasOption(parser, arg.substr(s, e - s)))
                {
                    ArgParseOption & opt = getOption(parser, arg.substr(s, e - s));
                    s = --e;  // advance in squished options;  s > e if at end

                    // Flag options are easy to handle.
                    if (isFlagOption(opt))
                    {
                        _assignArgumentValue(opt, "true");
                        continue;
                    }

                    // Handle option with values.
                    if (e < arg.size() - 1)
                    {
                        std::stringstream what;
                        what << "invalid combination of arguments -- " << arg << std::endl;
                        SEQAN_THROW(ParseError(what.str()));
                    }

                    // Handle the case of too few options.
                    if (argi + (int)numberOfAllowedValues(opt) >= argc)
                        SEQAN_THROW(MissingArgument(opt.shortName));

                    // Assign the required option value.s
                    for (int t = 0; t < (int)numberOfAllowedValues(opt); ++t)
                        _assignArgumentValue(opt, argv[++argi]);
                }
            }

            // No option was found.
            if (s == e)
                SEQAN_THROW(InvalidOption(arg.substr(s)));
        }
    }
};

// Parser driver function.
template <typename TChar>
ArgumentParser::ParseResult parse(ArgumentParser & me,
                                  int argc,
                                  TChar * argv[],
                                  std::ostream & outputStream,
                                  std::ostream & errorStream)
{
    SEQAN_TRY
    {
        // Perform the parsing without any valid value checking on the argument values.
        ArgumentParserHelper_<TChar> parserHelper(me, argc, argv);
        parserHelper.parseArgs();

        // Copy the file extensions from the "--${NAME}-file-ext" options to "--${NAME}".
        for (unsigned i = 0; i < me.optionMap.size(); ++i)
        {
            ArgParseOption & opt = me.optionMap[i];
            std::string longExtName = opt.longName + "-file-ext";
            std::string shortExtName = opt.shortName + "-file-ext";
            if (!opt.longName.empty() && hasOption(me, longExtName))
                opt._fileExtensions = getArgumentValues(getOption(me, longExtName));
            else if (!opt.shortName.empty() && hasOption(me, shortExtName))
                opt._fileExtensions = getArgumentValues(getOption(me, longExtName));
        }
        // Copy the file extensions from the "--arg-${NUM}-file-ext" options to the argument.
        for (unsigned i = 0; i < me.argumentList.size(); ++i)
        {
            ArgParseArgument & arg = me.argumentList[i];
            std::stringstream ss;
            ss << "arg-" << (i + 1) << "-file-ext";
            if (hasOption(me, ss.str()))
                arg._fileExtensions = getArgumentValues(getOption(me, ss.str()));
        }

        // Check all arguments for their values.
        for (unsigned i = 0; i < me.optionMap.size(); ++i)
            _checkValue(me.optionMap[i]);
        for (unsigned i = 0; i < me.argumentList.size(); ++i)
            _checkValue(me.argumentList[i]);
    }
    SEQAN_CATCH(ParseError & ex)
    {
        errorStream << getAppName(me) << ": " << ex.what() << std::endl;
        return ArgumentParser::PARSE_ERROR;
    }

#ifndef SEQAN_DISABLE_VERSION_CHECK
    // do version check if not turned off by the user
    bool check_version = false;
    getOptionValue(check_version, me, "version-check");

    if (check_version)
    {
        VersionCheck app_version(toCString(me._toolDoc._name),
                                 toCString(me._toolDoc._version),
                                 errorStream);
        std::promise<bool> appVersionProm;
        me.appVersionCheckFuture = appVersionProm.get_future();
        app_version(std::move(appVersionProm));
    }
#endif  // !SEQAN_DISABLE_VERSION_CHECK

    // Handle the special options.
    if (hasOption(me, "version") && isSet(me, "version"))
    {
        printVersion(me, outputStream);
        return ArgumentParser::PARSE_VERSION;
    }
    else if (hasOption(me, "copyright") && isSet(me, "copyright"))
    {
        printLongCopyright(me, outputStream);
        return ArgumentParser::PARSE_COPYRIGHT;
    }
    else if (hasOption(me, "write-ctd") && isSet(me, "write-ctd"))
    {
        if (writeCTD(me))
            return ArgumentParser::PARSE_WRITE_CTD;
        else
            return ArgumentParser::PARSE_ERROR;
    }
    else if (isSet(me, "help"))
    {
        printHelp(me, outputStream);
        return ArgumentParser::PARSE_HELP;
    }
    else if (isSet(me, "full-help"))
    {
        printHelp(me, outputStream, "txt", true);
        return ArgumentParser::PARSE_HELP;
    }
    else if (isSet(me, "export-help"))
    {
        std::string format;
        getOptionValue(format, me, "export-help");
        printHelp(me, outputStream, format, true);
        return ArgumentParser::PARSE_EXPORT_HELP;
    }
    else if (argc == 1 && !(_allRequiredSet(me) && _allArgumentsSet(me)))
    {
        // print short help and exit
        printShortHelp(me, errorStream);
        return ArgumentParser::PARSE_HELP;
    }

    // In case that everything is fine, we can now return OK.
    if (_allRequiredSet(me) && _allArgumentsSet(me))
        return ArgumentParser::PARSE_OK;

    // Otherwise, we check which options missed values.
    if (!_allRequiredSet(me))
        for (unsigned o = 0; o < length(me.optionMap); ++o)
            if (!isSet(me.optionMap[o]) && isRequired(me.optionMap[o]))
                errorStream << getAppName(me) << ": Missing value for option: " << getOptionName(me.optionMap[o]) << std::endl;
    // and arguments
    if (!_allArgumentsSet(me))
        errorStream << getAppName(me) << ": Not enough arguments were provided." << std::endl;
    errorStream << "Try '" << getAppName(me) << " --help' for more information.\n";
    return ArgumentParser::PARSE_ERROR;
}

template <typename TChar>
ArgumentParser::ParseResult parse(ArgumentParser & me,
                                  int argc,
                                  TChar * argv[])
{
    return parse(me, argc, argv, std::cout, std::cerr);
}

} // namespace seqan

#endif // SEQAN_INCLUDE_SEQAN_ARG_PARSE_ARG_PARSE_PARSE_H_