os/ossrv/stdcpp/tsrc/Stdcpp_test/stdcxx/testengine/src/cmdopt.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
/***************************************************************************
sl@0
     2
 *
sl@0
     3
 * $Id: cmdopt.cpp 348342 2005-11-23 02:03:26Z sebor $
sl@0
     4
 *
sl@0
     5
 ************************************************************************
sl@0
     6
 *
sl@0
     7
 * Copyright (c) 1994-2005 Quovadx,  Inc., acting through its  Rogue Wave
sl@0
     8
 * Software division. Licensed under the Apache License, Version 2.0 (the
sl@0
     9
 * "License");  you may  not use this file except  in compliance with the
sl@0
    10
 * License.    You    may   obtain   a   copy   of    the   License    at
sl@0
    11
 * http://www.apache.org/licenses/LICENSE-2.0.    Unless   required    by
sl@0
    12
 * applicable law  or agreed to  in writing,  software  distributed under
sl@0
    13
 * the License is distributed on an "AS IS" BASIS,  WITHOUT WARRANTIES OR
sl@0
    14
 * CONDITIONS OF  ANY KIND, either  express or implied.  See  the License
sl@0
    15
 * for the specific language governing permissions  and limitations under
sl@0
    16
 * the License.
sl@0
    17
 * 
sl@0
    18
 **************************************************************************/
sl@0
    19
sl@0
    20
// expand _TEST_EXPORT macros
sl@0
    21
#define _RWSTD_TEST_SRC
sl@0
    22
sl@0
    23
#include <cmdopt.h>
sl@0
    24
sl@0
    25
#include <assert.h>   // for assert
sl@0
    26
#include <errno.h>    // for errno
sl@0
    27
#include <stdarg.h>   // for va_arg, ...
sl@0
    28
#include <stdio.h>    // for fprintf
sl@0
    29
#include <stdlib.h>   // for atexit, free, malloc
sl@0
    30
#include <string.h>   // for memcpy, strcpy, strcmp, ...
sl@0
    31
sl@0
    32
#ifdef __ARMCC__
sl@0
    33
#pragma diag_suppress 61
sl@0
    34
#pragma diag_suppress 63
sl@0
    35
#endif
sl@0
    36
sl@0
    37
#ifndef EINVAL
sl@0
    38
#  define EINVAL   22   /* e.g., HP-UX, Linux, Solaris */
sl@0
    39
#endif   // EINVAL
sl@0
    40
sl@0
    41
/**************************************************************************/
sl@0
    42
sl@0
    43
typedef int (optcallback_t)(int, char*[]);
sl@0
    44
sl@0
    45
struct cmdopts_t
sl@0
    46
{
sl@0
    47
    char           loptbuf_ [32];   // buffer for long option name
sl@0
    48
    optcallback_t *callback_;       // function to call to process option
sl@0
    49
sl@0
    50
    // counter to increment for each occurrence of an option
sl@0
    51
    // or to set to the numeric argument (when specified)
sl@0
    52
    int           *pcntr_;
sl@0
    53
sl@0
    54
    char          *lopt_;           // long option name
sl@0
    55
    char           sopt_;           // short option name
sl@0
    56
sl@0
    57
    size_t         maxcalls_;       // how many times option can be invoked
sl@0
    58
    size_t         ncalls_;         // how many times it has been invoked
sl@0
    59
sl@0
    60
    unsigned       arg_ : 1;        // option takes an argument?
sl@0
    61
    unsigned       inv_ : 1;        // callback invocation inverted
sl@0
    62
    unsigned       envseen_ : 1;    // environment option already processed
sl@0
    63
};
sl@0
    64
sl@0
    65
sl@0
    66
// total number of registered options
sl@0
    67
static size_t ncmdopts;
sl@0
    68
sl@0
    69
// number of default (always defined) options
sl@0
    70
static size_t ndefopts;
sl@0
    71
static cmdopts_t cmdoptbuf [32];
sl@0
    72
static cmdopts_t *cmdopts = cmdoptbuf;
sl@0
    73
static size_t optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
sl@0
    74
sl@0
    75
/**************************************************************************/
sl@0
    76
sl@0
    77
static int
sl@0
    78
rw_print_help (int argc, char *argv[])
sl@0
    79
{
sl@0
    80
    if (1 == argc && argv && 0 == argv [0]) {
sl@0
    81
        static const char helpstr[] = {
sl@0
    82
            "Without an argument, prints this help to stdout and exits with\n"
sl@0
    83
            "a status of 0 without further processing.\n"
sl@0
    84
            "With the optional argument prints help on the option with that\n"
sl@0
    85
            "name, if one exists, and exits with a status of 0. If an option\n"
sl@0
    86
            "with the specified name does not exist, prints an error message\n"
sl@0
    87
            "to stderr and exits with a status of 1.\n"
sl@0
    88
            "The leading underscores in the name of an option are optional.\n"
sl@0
    89
        };
sl@0
    90
sl@0
    91
        argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
sl@0
    92
sl@0
    93
        return 0;
sl@0
    94
    }
sl@0
    95
sl@0
    96
    // the option name to get help on, if any
sl@0
    97
    const char* opthelp = 1 < argc ? argv [1] : 0;
sl@0
    98
sl@0
    99
    // remove the optional one or two leading underscores
sl@0
   100
    if (opthelp && '-' == opthelp [0] && opthelp [1])
sl@0
   101
        opthelp += 1 + ('-' == opthelp [1]);
sl@0
   102
sl@0
   103
    if (0 == opthelp)
sl@0
   104
        printf ("OPTIONS\n");
sl@0
   105
sl@0
   106
    // set to a non-zero when the specified option is found
sl@0
   107
    int option_found = 0;
sl@0
   108
sl@0
   109
    for (size_t i = 0; i != ncmdopts; ++i) {
sl@0
   110
sl@0
   111
        // get a pointer to the name of the long option, if any
sl@0
   112
        const char* const lopt =
sl@0
   113
            cmdopts [i].lopt_ ? cmdopts [i].lopt_ : cmdopts [i].loptbuf_;
sl@0
   114
sl@0
   115
        if (opthelp && *opthelp) {
sl@0
   116
sl@0
   117
            if (   cmdopts [i].sopt_ == opthelp [0] && '\0' == opthelp [1]
sl@0
   118
                || *lopt && 0 == strcmp (lopt + 1, opthelp)) {
sl@0
   119
sl@0
   120
                // remember that we found the option whose (short
sl@0
   121
                // or long) name we're to give help on; after printing
sl@0
   122
                // the help text on the option keep looping in case
sl@0
   123
                // there is another option and callback with the same
sl@0
   124
                // name (unlikely but possible)
sl@0
   125
                option_found = 1;
sl@0
   126
            }
sl@0
   127
            else {
sl@0
   128
                // the option doesn't match, continue searching
sl@0
   129
                continue;
sl@0
   130
            }
sl@0
   131
        }
sl@0
   132
sl@0
   133
        printf ("     ");
sl@0
   134
sl@0
   135
        if (cmdopts [i].sopt_) {
sl@0
   136
            printf ("-%c", cmdopts [i].sopt_);
sl@0
   137
sl@0
   138
            if (lopt)
sl@0
   139
                printf (" | ");
sl@0
   140
        }
sl@0
   141
sl@0
   142
        const char *pfx = "";
sl@0
   143
        const char *sfx = pfx;
sl@0
   144
sl@0
   145
        if (lopt) {
sl@0
   146
            printf ("-%s", lopt);
sl@0
   147
            if (   cmdopts [i].arg_
sl@0
   148
                && '=' != lopt [strlen (lopt) - 1]) {
sl@0
   149
                pfx = " [ ";
sl@0
   150
                sfx = " ]";
sl@0
   151
            }
sl@0
   152
        }
sl@0
   153
sl@0
   154
        printf ("%s%s%s", pfx, cmdopts [i].arg_ ? "<arg>" : "", sfx);
sl@0
   155
sl@0
   156
        if (_RWSTD_SIZE_MAX == cmdopts [i].maxcalls_)
sl@0
   157
            printf (" (each occurrence evaluated)\n");
sl@0
   158
        else if (1 < cmdopts [i].maxcalls_)
sl@0
   159
            printf (" (at most %u occurrences evaluated)\n",
sl@0
   160
                    unsigned (cmdopts [i].maxcalls_));
sl@0
   161
        else
sl@0
   162
            printf (" (only the first occurrence evaluated)\n");
sl@0
   163
sl@0
   164
        // invoke callback with the "--help" option
sl@0
   165
        if (cmdopts [i].callback_) {
sl@0
   166
sl@0
   167
            char* help [2] = { 0, 0 };
sl@0
   168
sl@0
   169
            cmdopts [i].callback_ (1, help);
sl@0
   170
sl@0
   171
            for (const char *line = help [0]; line; ) {
sl@0
   172
sl@0
   173
                const char* const nl = strchr (line, '\n');
sl@0
   174
                const int len = nl ? int (nl - line) : int (strlen (line));
sl@0
   175
sl@0
   176
                printf ("       %.*s\n", len, line);
sl@0
   177
sl@0
   178
                line = nl;
sl@0
   179
                if (nl)
sl@0
   180
                    ++line;
sl@0
   181
            }
sl@0
   182
        }
sl@0
   183
    }
sl@0
   184
sl@0
   185
    if (opthelp && !option_found) {
sl@0
   186
        fprintf (stderr, "Unknown option \"%s\".\n", opthelp);
sl@0
   187
        exit (1);
sl@0
   188
    }
sl@0
   189
sl@0
   190
    exit (0);
sl@0
   191
sl@0
   192
    return 0;
sl@0
   193
}
sl@0
   194
sl@0
   195
/**************************************************************************/
sl@0
   196
sl@0
   197
static int
sl@0
   198
rw_set_ignenv (int argc, char *argv[])
sl@0
   199
{
sl@0
   200
    if (1 == argc && argv && 0 == argv [0]) {
sl@0
   201
        static const char helpstr[] = {
sl@0
   202
            "Prevents options specified in the RWSTD_TESTOPTS environment\n"
sl@0
   203
            "variable from taking effect.\n"
sl@0
   204
            "Unless this option is specified, the RWSTD_TESTOPTS environment\n"
sl@0
   205
            "variable is processed as if its value were specified on the \n"
sl@0
   206
            "command line.\n"
sl@0
   207
            "For example, setting the value of the variable to the string\n"
sl@0
   208
            "\"--verbose --no-wchar\" and invoking this program with no\n"
sl@0
   209
            "command line arguments will have the same effect as invoking\n"
sl@0
   210
            "it with the two arguments on the command line.\n"
sl@0
   211
        };
sl@0
   212
sl@0
   213
        argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
sl@0
   214
sl@0
   215
        return 0;
sl@0
   216
    }
sl@0
   217
sl@0
   218
    return 0;
sl@0
   219
}
sl@0
   220
sl@0
   221
extern "C" {
sl@0
   222
sl@0
   223
static void
sl@0
   224
rw_clear_opts ()
sl@0
   225
{
sl@0
   226
    // reset all options, deallocating dynamically allocated storage
sl@0
   227
sl@0
   228
    for (size_t i = 0; i != ncmdopts; ++i) {
sl@0
   229
sl@0
   230
        // free any storage allocated for the option name
sl@0
   231
        free (cmdopts [i].lopt_);
sl@0
   232
    }
sl@0
   233
sl@0
   234
    if (cmdopts != cmdoptbuf) {
sl@0
   235
        // free the storage allocated for all the options
sl@0
   236
        free (cmdopts);
sl@0
   237
    }
sl@0
   238
sl@0
   239
    // reset the options pointer to point at the statically
sl@0
   240
    // allocated buffer and the count back to 0
sl@0
   241
    ncmdopts   = 0;
sl@0
   242
    cmdopts    = cmdoptbuf;
sl@0
   243
    optbufsize = sizeof cmdoptbuf / sizeof *cmdoptbuf;
sl@0
   244
}
sl@0
   245
sl@0
   246
}
sl@0
   247
sl@0
   248
static void
sl@0
   249
rw_set_myopts ()
sl@0
   250
{
sl@0
   251
    static int cleanup_handler_registered;
sl@0
   252
sl@0
   253
    if (0 == cleanup_handler_registered) {
sl@0
   254
        atexit (rw_clear_opts);
sl@0
   255
        cleanup_handler_registered = 1;
sl@0
   256
    }
sl@0
   257
sl@0
   258
    if (0 != ncmdopts)
sl@0
   259
        return;
sl@0
   260
sl@0
   261
    static int recursive;
sl@0
   262
sl@0
   263
    if (recursive)
sl@0
   264
        return;
sl@0
   265
sl@0
   266
    ++recursive;
sl@0
   267
sl@0
   268
    rw_setopts ("|-help: "   // argument optional
sl@0
   269
                "|-ignenv ",
sl@0
   270
                rw_print_help,
sl@0
   271
                rw_set_ignenv);
sl@0
   272
sl@0
   273
    ndefopts = ncmdopts;
sl@0
   274
sl@0
   275
    recursive = 0;
sl@0
   276
}
sl@0
   277
sl@0
   278
/**************************************************************************/
sl@0
   279
sl@0
   280
//////////////////////////////////////////////////////////////////////
sl@0
   281
// syntax of the option description string:
sl@0
   282
//
sl@0
   283
// opts ::= opt [ ':' | '=' | '#' ] [ @N | @* | '!' ] [ opts ]
sl@0
   284
// opt  ::= <sopt> [ '|' <lopt>]
sl@0
   285
//      ::= '|' <lopt>
sl@0
   286
// sopt ::= char
sl@0
   287
// lopt ::= char char*
sl@0
   288
// char ::= A-Z a-z _ 0-9
sl@0
   289
sl@0
   290
_TEST_EXPORT int
sl@0
   291
rw_vsetopts (const char *opts, va_list va)
sl@0
   292
{
sl@0
   293
    if (0 == opts) {
sl@0
   294
sl@0
   295
        rw_clear_opts ();
sl@0
   296
        return 0;
sl@0
   297
    }
sl@0
   298
sl@0
   299
    rw_set_myopts ();
sl@0
   300
sl@0
   301
    const char *next = opts;
sl@0
   302
sl@0
   303
    for ( ; ; ++ncmdopts) {
sl@0
   304
sl@0
   305
        while (' ' == *next)
sl@0
   306
            ++next;
sl@0
   307
sl@0
   308
        if ('\0' == *next) {
sl@0
   309
            break;
sl@0
   310
        }
sl@0
   311
sl@0
   312
        if (ncmdopts == optbufsize) {
sl@0
   313
sl@0
   314
            const size_t newbufsize = 2 * ncmdopts + 1;
sl@0
   315
            
sl@0
   316
            cmdopts_t* const newopts =
sl@0
   317
                (cmdopts_t*)malloc (newbufsize * sizeof (cmdopts_t));
sl@0
   318
sl@0
   319
            if (0 == newopts) {
sl@0
   320
                fprintf (stderr, "%s%d: failed to allocate memory\n",
sl@0
   321
                         __FILE__, __LINE__);
sl@0
   322
                abort ();
sl@0
   323
            }
sl@0
   324
sl@0
   325
            memcpy (newopts, cmdopts, ncmdopts * sizeof (cmdopts_t));
sl@0
   326
sl@0
   327
            if (cmdopts != cmdoptbuf)
sl@0
   328
                free (cmdopts);
sl@0
   329
sl@0
   330
            cmdopts    = newopts;
sl@0
   331
            optbufsize = newbufsize;
sl@0
   332
        }
sl@0
   333
sl@0
   334
        // clear the next option info
sl@0
   335
        memset (cmdopts + ncmdopts, 0, sizeof *cmdopts);
sl@0
   336
sl@0
   337
        if ('|' != *next)
sl@0
   338
            cmdopts [ncmdopts].sopt_ = *next++;
sl@0
   339
sl@0
   340
        if ('|' == *next) {
sl@0
   341
            const char* end = strpbrk (++next, "|@:=*!# ");
sl@0
   342
            if (0 == end)
sl@0
   343
                end = next + strlen (next);
sl@0
   344
sl@0
   345
            // copy the option name up to but not including the delimiter
sl@0
   346
            // (except when the delimiter is the equals sign ('='), which
sl@0
   347
            // becomes the last character of the option name
sl@0
   348
            const size_t optlen = size_t (end - next) + ('=' == *end);
sl@0
   349
sl@0
   350
            char *lopt = 0;
sl@0
   351
sl@0
   352
            if (optlen < sizeof cmdopts [ncmdopts].loptbuf_)
sl@0
   353
                lopt = cmdopts [ncmdopts].loptbuf_;
sl@0
   354
            else {
sl@0
   355
                lopt = (char*)malloc (optlen + 1);
sl@0
   356
                cmdopts [ncmdopts].lopt_ = lopt;
sl@0
   357
            }
sl@0
   358
sl@0
   359
            memcpy (lopt, next, optlen);
sl@0
   360
            lopt [optlen] = '\0';
sl@0
   361
sl@0
   362
            next = end;
sl@0
   363
        }
sl@0
   364
sl@0
   365
        // only the first occurrence of each command line option
sl@0
   366
        // causes an invocation of the callback, all subsequent
sl@0
   367
        // ones will be ignored by default
sl@0
   368
        cmdopts [ncmdopts].maxcalls_ = 1;
sl@0
   369
sl@0
   370
        int arg_is_callback = true;
sl@0
   371
sl@0
   372
        if ('#' == *next) {
sl@0
   373
            // insead of a pointer to a callback, the argument
sl@0
   374
            // is a pointer to an int counter that is to be
sl@0
   375
            // incremented for each occurrence of the option
sl@0
   376
            // during processing; when the option is immediately
sl@0
   377
            // followed by the equals sign ('=') and a numeric
sl@0
   378
            // argument the value of the argument will be stored
sl@0
   379
            arg_is_callback = false;
sl@0
   380
            ++next;
sl@0
   381
            
sl@0
   382
            // an unlimited number of occurrences of the option
sl@0
   383
            // are allowed and will be counted
sl@0
   384
            cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
sl@0
   385
        }
sl@0
   386
        else if (':' == *next || '=' == *next) {
sl@0
   387
            // ':' : argument optional
sl@0
   388
            // '=' : argument required
sl@0
   389
            cmdopts [ncmdopts].arg_ = true;
sl@0
   390
            ++next;
sl@0
   391
        }
sl@0
   392
sl@0
   393
        if ('@' == *next) {
sl@0
   394
sl@0
   395
            ++next;
sl@0
   396
sl@0
   397
            // at most how many occurrences of an option can be processed?
sl@0
   398
            if ('*' == *next) {
sl@0
   399
                // unlimited
sl@0
   400
                cmdopts [ncmdopts].maxcalls_ = _RWSTD_SIZE_MAX;
sl@0
   401
                ++next;
sl@0
   402
            }
sl@0
   403
            else {
sl@0
   404
                // at most this many
sl@0
   405
                char *end;
sl@0
   406
                cmdopts [ncmdopts].maxcalls_ = strtoul (next, &end, 10);
sl@0
   407
                next = end;
sl@0
   408
            }
sl@0
   409
        }
sl@0
   410
        else if ('!' == *next) {
sl@0
   411
            cmdopts [ncmdopts].inv_ = true;
sl@0
   412
            ++next;
sl@0
   413
        }
sl@0
   414
sl@0
   415
        if (arg_is_callback) {
sl@0
   416
            // retrieve the callback and verify it's not null
sl@0
   417
            // (null callback is permitted in the special case when
sl@0
   418
            // the short option is '-', i.e., when setting up or
sl@0
   419
            // resetting an "unknown option" handler)
sl@0
   420
            cmdopts [ncmdopts].callback_ = va_arg (va, optcallback_t*);
sl@0
   421
        }
sl@0
   422
        else {
sl@0
   423
            // retrieve the address of the int counter where to keep
sl@0
   424
            // track of the number of occurrences of the option, or
sl@0
   425
            // where to store the value of the numeric argument of
sl@0
   426
            // the option
sl@0
   427
            cmdopts [ncmdopts].pcntr_ = va_arg (va, int*);
sl@0
   428
        }
sl@0
   429
sl@0
   430
        if (   '-' != cmdopts [ncmdopts].sopt_
sl@0
   431
            && 0 == cmdopts [ncmdopts].callback_
sl@0
   432
            && 0 == cmdopts [ncmdopts].pcntr_) {
sl@0
   433
sl@0
   434
            // get a pointer to the long option name
sl@0
   435
            const char* const lopt = cmdopts [ncmdopts].lopt_
sl@0
   436
                ? cmdopts [ncmdopts].lopt_ : cmdopts [ncmdopts].loptbuf_;
sl@0
   437
sl@0
   438
            if (*lopt)
sl@0
   439
                fprintf (stderr, "null handler for option -%s\n", lopt);
sl@0
   440
            else
sl@0
   441
                fprintf (stderr, "null handler for option -%c\n",
sl@0
   442
                         cmdopts [ncmdopts].sopt_);
sl@0
   443
                
sl@0
   444
            abort ();
sl@0
   445
        }
sl@0
   446
    }
sl@0
   447
sl@0
   448
    return int (ncmdopts - ndefopts);
sl@0
   449
}
sl@0
   450
sl@0
   451
/**************************************************************************/
sl@0
   452
sl@0
   453
_TEST_EXPORT int
sl@0
   454
rw_setopts (const char *opts, ...)
sl@0
   455
{
sl@0
   456
    va_list va;
sl@0
   457
    va_start (va, opts);
sl@0
   458
    const int result = rw_vsetopts (opts, va);
sl@0
   459
    va_end (va);
sl@0
   460
    return result;
sl@0
   461
}
sl@0
   462
sl@0
   463
/**************************************************************************/
sl@0
   464
sl@0
   465
_TEST_EXPORT int
sl@0
   466
rw_runopts (int argc, char *argv[])
sl@0
   467
{
sl@0
   468
    rw_set_myopts ();
sl@0
   469
sl@0
   470
    static int recursive = false;
sl@0
   471
sl@0
   472
    // ignore options set in the environment?
sl@0
   473
    int ignenv = recursive;
sl@0
   474
sl@0
   475
    // return status
sl@0
   476
    int status = 0;
sl@0
   477
sl@0
   478
    // number of options processed
sl@0
   479
    int nopts = 0;
sl@0
   480
sl@0
   481
    // index of registered option whose callback should be invoked
sl@0
   482
    // for command line options that do not match any other
sl@0
   483
    size_t not_found_inx = _RWSTD_SIZE_MAX;
sl@0
   484
sl@0
   485
    // iterate over the command line arguments until a callback
sl@0
   486
    // returns a non-zero value or until all options have been
sl@0
   487
    // successfully processed
sl@0
   488
    for (int i = 0; i < argc && argv [i] && 0 == status; ++i) {
sl@0
   489
sl@0
   490
        if (0 == strcmp ("--ignore-environment", argv [i])) {
sl@0
   491
            // ignore options set in the environment
sl@0
   492
            ignenv = true;
sl@0
   493
            continue;
sl@0
   494
        }
sl@0
   495
sl@0
   496
        if (0 == strcmp ("--", argv [i])) {
sl@0
   497
            // "--" terminates options, everything
sl@0
   498
            // after it is treated as an argument
sl@0
   499
            break;
sl@0
   500
        }
sl@0
   501
sl@0
   502
        // the name of the option without the leading dash
sl@0
   503
        const char* const optname = argv [i] + 1;
sl@0
   504
sl@0
   505
        // look for the first equals sign
sl@0
   506
        const char* const eq = strchr (optname, '=');
sl@0
   507
sl@0
   508
        // compute the length of the option including the equals sign (if any)
sl@0
   509
        const size_t optlen = eq ? size_t (eq - optname + 1) : strlen (optname);
sl@0
   510
sl@0
   511
        int found = false;
sl@0
   512
sl@0
   513
        // look up each command line option (i.e., a string that starts
sl@0
   514
        // with a dash ('-')) and invoke the callback associated with it
sl@0
   515
        for (size_t j = 0; j != ncmdopts; ++j) {
sl@0
   516
sl@0
   517
            if ('-' == cmdopts [j].sopt_)
sl@0
   518
                not_found_inx = j;
sl@0
   519
sl@0
   520
            if ('-' == argv [i][0]) {
sl@0
   521
sl@0
   522
                const size_t cmplen =
sl@0
   523
                    eq && cmdopts [j].pcntr_ ? optlen - 1 : optlen;
sl@0
   524
sl@0
   525
                // get a pointer to the (possibly empty) name
sl@0
   526
                // of the long option
sl@0
   527
                const char* const lopt = cmdopts [j].lopt_ ? 
sl@0
   528
                    cmdopts [j].lopt_ : cmdopts [j].loptbuf_;
sl@0
   529
sl@0
   530
                // try to match the long option first, and only if it
sl@0
   531
                // doesn't match try the short single-character option
sl@0
   532
                if (   cmplen == strlen (lopt)
sl@0
   533
                    && 0 == memcmp (optname, lopt, cmplen)
sl@0
   534
                    || cmdopts [j].sopt_
sl@0
   535
                    && optname [0] == cmdopts [j].sopt_
sl@0
   536
                    && (1 == optlen || cmdopts [j].arg_)) {
sl@0
   537
sl@0
   538
                    // matching option has been found
sl@0
   539
                    found = true;
sl@0
   540
sl@0
   541
                    // ignore the option if invoked recursively (by processing
sl@0
   542
                    // options set in the environment) and the option has
sl@0
   543
                    // already been seen (this prevents duplicate processing
sl@0
   544
                    // of options that are set both on the command line and
sl@0
   545
                    // in the environment and allows option with an argument
sl@0
   546
                    // set on the command line to override those set in the
sl@0
   547
                    // environment)
sl@0
   548
sl@0
   549
                    if (cmdopts [j].ncalls_ && recursive)
sl@0
   550
                        continue;
sl@0
   551
sl@0
   552
                    // if the option has been evaluated the maximum number
sl@0
   553
                    // of times, avoid evaluating it and continue processing
sl@0
   554
                    if (cmdopts [j].maxcalls_ <= cmdopts [j].ncalls_)
sl@0
   555
                        continue;
sl@0
   556
sl@0
   557
                    if (cmdopts [j].callback_) {
sl@0
   558
                        if (!cmdopts [j].inv_) {
sl@0
   559
                            // when the command line argument matched
sl@0
   560
                            // the option,  invoke the callback function
sl@0
   561
                            status = cmdopts [j].callback_ (argc - i, argv + i);
sl@0
   562
                        }
sl@0
   563
                    }
sl@0
   564
                    else if (eq) {
sl@0
   565
                        assert (0 != cmdopts [j].pcntr_);
sl@0
   566
sl@0
   567
                        // obtain the numeric argument
sl@0
   568
                        char *end = 0;
sl@0
   569
                        const long optval = strtol (eq + 1, &end, 0);
sl@0
   570
sl@0
   571
                        if (end && '\0' != *end) {
sl@0
   572
                            fprintf (stderr, "expected numeric argument: %s\n",
sl@0
   573
                                     optname);
sl@0
   574
                            ignenv = true;
sl@0
   575
                            errno  = EINVAL;
sl@0
   576
                            status = 1;
sl@0
   577
                        }
sl@0
   578
sl@0
   579
#if _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
sl@0
   580
sl@0
   581
                        else if (   optval < _RWSTD_INT_MIN
sl@0
   582
                                 || _RWSTD_INT_MAX < optval) {
sl@0
   583
                            fprintf (stderr, "numeric argument %ld out of range"
sl@0
   584
                                     " [%d, %d]: %s\n", optval,
sl@0
   585
                                     _RWSTD_INT_MIN, _RWSTD_INT_MAX, optname);
sl@0
   586
                            ignenv = true;
sl@0
   587
                            errno  = EINVAL;
sl@0
   588
                            status = 1;
sl@0
   589
                        }
sl@0
   590
sl@0
   591
#endif   // _RWSTD_INT_SIZE < _RWSTD_LONG_SIZE
sl@0
   592
sl@0
   593
                        else {
sl@0
   594
                            *cmdopts [j].pcntr_ = optval;
sl@0
   595
                        }
sl@0
   596
                    }
sl@0
   597
                    else {
sl@0
   598
                        assert (0 != cmdopts [j].pcntr_);
sl@0
   599
                        ++*cmdopts [j].pcntr_;
sl@0
   600
                    }
sl@0
   601
sl@0
   602
                    ++cmdopts [j].ncalls_;
sl@0
   603
sl@0
   604
                    if (recursive)
sl@0
   605
                        cmdopts [j].envseen_ = true;
sl@0
   606
sl@0
   607
                    ++nopts;
sl@0
   608
sl@0
   609
                    if (status) {
sl@0
   610
                        // when the status returned from the last callback
sl@0
   611
                        // is non-0 stop further processing (including
sl@0
   612
                        // any options set in the environment) and return
sl@0
   613
                        // status to the caller
sl@0
   614
                        ignenv = true;
sl@0
   615
                        break;
sl@0
   616
                    }
sl@0
   617
                }
sl@0
   618
            }
sl@0
   619
        }
sl@0
   620
sl@0
   621
        if (!found && '-' == argv [i][0]) {
sl@0
   622
sl@0
   623
            // invoke the appropriate error handler for an option
sl@0
   624
            // that was not found
sl@0
   625
            if (_RWSTD_SIZE_MAX != not_found_inx) {
sl@0
   626
sl@0
   627
                // invoke the error handler set up through rw_setopts()
sl@0
   628
                // and let the handler decide whether to go on processing
sl@0
   629
                // other options or whether to abort
sl@0
   630
                status = cmdopts [not_found_inx].callback_ (argc - i, argv + i);
sl@0
   631
                if (status) {
sl@0
   632
                    // no further processing done
sl@0
   633
                    ignenv = true;
sl@0
   634
                    break;
sl@0
   635
                }
sl@0
   636
            }
sl@0
   637
            else {
sl@0
   638
                // print an error message to stderr when no error
sl@0
   639
                // handler has been set up
sl@0
   640
                fprintf (stderr, "unknown option: %s\n", argv [i]);
sl@0
   641
                ignenv = true;
sl@0
   642
                errno  = EINVAL;
sl@0
   643
                status = 1;
sl@0
   644
                break;
sl@0
   645
            }
sl@0
   646
        }
sl@0
   647
    }
sl@0
   648
sl@0
   649
    if (!ignenv) {
sl@0
   650
        // process options from the environment
sl@0
   651
        const char* const envar = getenv ("RWSTD_TESTOPTS");
sl@0
   652
        if (envar) {
sl@0
   653
            recursive = true;
sl@0
   654
            rw_runopts (envar);
sl@0
   655
            recursive = false;
sl@0
   656
        }
sl@0
   657
    }
sl@0
   658
sl@0
   659
    // invoke any inverted callbacks or bump their user-specified counters,
sl@0
   660
    // and reset internal counters indicating if/how many times each option
sl@0
   661
    // has been processed
sl@0
   662
    for (size_t j = 0; j != ncmdopts; ++j) {
sl@0
   663
sl@0
   664
        if (cmdopts [j].inv_ && 0 == cmdopts [j].ncalls_ && 0 == status) {
sl@0
   665
sl@0
   666
            if (cmdopts [j].callback_)
sl@0
   667
                status = cmdopts [j].callback_ (0, 0);
sl@0
   668
            else {
sl@0
   669
                assert (0 != cmdopts [j].pcntr_);
sl@0
   670
                ++*cmdopts [j].pcntr_;
sl@0
   671
            }
sl@0
   672
        }
sl@0
   673
sl@0
   674
        cmdopts [j].ncalls_  = 0;
sl@0
   675
        cmdopts [j].envseen_ = false;
sl@0
   676
    }
sl@0
   677
sl@0
   678
    return status;
sl@0
   679
}
sl@0
   680
sl@0
   681
/**************************************************************************/
sl@0
   682
sl@0
   683
_TEST_EXPORT int
sl@0
   684
rw_runopts (const char *str)
sl@0
   685
{
sl@0
   686
    assert (0 != str);
sl@0
   687
sl@0
   688
    rw_set_myopts ();
sl@0
   689
sl@0
   690
    char buf [80];      // fixed size buffer to copy `str' into
sl@0
   691
    char *pbuf = buf;   // a modifiable copy of `str'
sl@0
   692
sl@0
   693
    size_t len = strlen (str);
sl@0
   694
    if (len >= sizeof buf) {
sl@0
   695
        // allocate if necessary
sl@0
   696
        pbuf = (char*)malloc (len + 1);
sl@0
   697
        if (!pbuf)
sl@0
   698
            return -1;
sl@0
   699
    }
sl@0
   700
sl@0
   701
    // copy `str' to modifiable buffer
sl@0
   702
    memcpy (pbuf, str, len + 1);
sl@0
   703
sl@0
   704
    char *tmp_argv_buf [32] = { 0 };   // fixed size argv buffer
sl@0
   705
    char **argv = tmp_argv_buf;        // array of arguments
sl@0
   706
sl@0
   707
    // initial size of argument array (will grow as necessary)
sl@0
   708
    size_t argv_size = sizeof tmp_argv_buf / sizeof *tmp_argv_buf;
sl@0
   709
    size_t argc = 0;   // number of arguments in array
sl@0
   710
sl@0
   711
    int in_quotes = 0;   // quoted argument being processed
sl@0
   712
sl@0
   713
    for (char *s = pbuf; *s; ++s) {
sl@0
   714
        if ('"' == *s) {
sl@0
   715
            in_quotes = !in_quotes;
sl@0
   716
            continue;
sl@0
   717
        }
sl@0
   718
sl@0
   719
        if (in_quotes)
sl@0
   720
            continue;
sl@0
   721
sl@0
   722
        // split up unquoted space-separated arguments
sl@0
   723
        if (argc == 0 || ' ' == *s) {
sl@0
   724
            if (argc > 0) 
sl@0
   725
                *s = 0;
sl@0
   726
sl@0
   727
            // skip over leading spaces
sl@0
   728
            if (argc > 0 || ' ' == *s)
sl@0
   729
                while (' ' == *++s);
sl@0
   730
sl@0
   731
            if (*s) {
sl@0
   732
                if (argc == argv_size) {
sl@0
   733
                    // grow `argv' as necessary
sl@0
   734
                    char **tmp = (char**)malloc (sizeof *tmp * argv_size * 2);
sl@0
   735
                    if (!tmp) {
sl@0
   736
                        if (argv != tmp_argv_buf)
sl@0
   737
                            free (argv);
sl@0
   738
                        return -1;
sl@0
   739
                    }
sl@0
   740
sl@0
   741
                    // copy existing elementes and zero out any new entries
sl@0
   742
                    memcpy (tmp, argv, sizeof *tmp * argv_size);
sl@0
   743
                    memset (tmp + argv_size, 0, sizeof *tmp * argv_size);
sl@0
   744
sl@0
   745
                    // free existing buffer if necessary
sl@0
   746
                    if (argv != tmp_argv_buf)
sl@0
   747
                        free (argv);
sl@0
   748
sl@0
   749
                    // reassign buffer and increase size
sl@0
   750
                    argv       = tmp;
sl@0
   751
                    argv_size *= 2;
sl@0
   752
                }
sl@0
   753
sl@0
   754
                // add argument to array
sl@0
   755
                argv [argc++] = s;
sl@0
   756
            }
sl@0
   757
        }
sl@0
   758
    }
sl@0
   759
sl@0
   760
    // process `argc' options pointed to by `argv'
sl@0
   761
    const int status = rw_runopts (int (argc), argv);
sl@0
   762
sl@0
   763
    // free buffers if necessary
sl@0
   764
    if (argv != tmp_argv_buf)
sl@0
   765
        free (argv);
sl@0
   766
sl@0
   767
    if (pbuf != buf)
sl@0
   768
        free (pbuf);
sl@0
   769
sl@0
   770
    return status;
sl@0
   771
}