/************************************************************************
 *
 * opt_trace.cpp - definitions of tracing option handlers
 *
 * $Id: opt_trace.cpp 290026 2005-09-19 00:12:38Z sebor $
 *
 ************************************************************************
 *
 * Copyright (c) 1994-2005 Quovadx,  Inc., acting through its  Rogue Wave
 * Software division. Licensed under the Apache License, Version 2.0 (the
 * "License");  you may  not use this file except  in compliance with the
 * License.    You    may   obtain   a   copy   of    the   License    at
 * http://www.apache.org/licenses/LICENSE-2.0.    Unless   required    by
 * applicable law  or agreed to  in writing,  software  distributed under
 * the License is distributed on an "AS IS" BASIS,  WITHOUT WARRANTIES OR
 * CONDITIONS OF  ANY KIND, either  express or implied.  See  the License
 * for the specific language governing permissions  and limitations under
 * the License.
 *
 **************************************************************************/

// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC

#include "opt_trace.h"

#include <stdio.h>    // for fprintf()
#include <stdlib.h>   // for strtol()
#include <string.h>   // for strchar()

/************************************************************************/

// masked diagnostics (those that shouldn't be issued)
int _rw_diag_mask = 1 << diag_trace;


int
_rw_setopt_trace_mask (int argc, char *argv[])
{
    if (1 == argc && argv && 0 == argv [0]) {
        static const char helpstr[] = {
            "Specifies the severity of diagnostic messages to be issued when\n"
            "active. By default, diagnostics with severity of 3 and and above\n"
            "are issued, all others are suppressed. The severity of an inactive"
            "\ndiagnostic is always zero, regardless of what the severity would"
            "\nbe if it were active.\n\n"
            "The syntax of <arg> is as follows:\n"
            "<arg>   ::= <range> [ , <range> ]\n"
            "<range> ::= [ - ] <digit> | <digit> - [ <digit> ]\n"
            "<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\n"
            "\n"
            "The default names of the diagnostic messages arranged in order\n"
            "of increasing severity are as follows:\n"
            "ITRACE, TRACE, DEBUG, INFO, NOTE, WARNING, ASSERTION, UNUSED,\n"
            "ERROR, and FATAL.\n"
            "The name of each inactive diagnostic is preceded by the name\n"
            "of the severity-0 diagnostic (ITRACE_ by default). The severity\n"
            "of an inactive diagnostic is always zero, regardless of what\n"
            "the severity would be if it were active.\n"
        };

        argv [0] = _RWSTD_CONST_CAST (char*, helpstr);

        return 0;
    }

    char *parg = strchr (argv [0], '=');

    if (0 == parg) {
        _rw_diag_mask = 0;
        return 0;
    }
        
    int diag_set = 0;

    const char* const argbeg = ++parg;

    // the lower bound of a range of severities to be enabled or disabled
    // negative values are not valid and denote an implicit lower bound
    // of 1 (such as in "-3" which is a shorthand for "1-3")
    long first = -1;

    for ( ; '\0' != *parg ; ) {

        // skip any leading whitespace
        for ( ; ' ' == *parg; ++parg);

        if ('-' == *parg) {
            if (first < 0) {
                first = 0;
                ++parg;
            }
            else {
                fprintf (stderr,
                         "invalid character '%c' at position %d: \"%s\"\n",
                         *parg, int (parg - argbeg), argv [0]);
                return 2;
            }
        }

        // parse a numeric argument
        char *end;
        long severity = strtol (parg, &end, 0);

        // skip any trailing whitespace
        for ( ; ' ' == *end; ++end);

        if (end == parg || '-' != *end && ',' != *end && '\0' != *end) {
            fprintf (stderr,
                     "invalid character '%c' at position %d: \"%s\"\n",
                     *end, int (parg - argbeg), argv [0]);
            return 2;
        }

        if (0 <= first) {
            if (severity < 0 || N_DIAG_TYPES < severity) {
                fprintf (stderr,
                         "invalid value %ld at position %d: \"%s\"\n",
                         severity, int (parg - argbeg), argv [0]);
                return 2;
            }

            ++severity;

            if ((',' == *end || '-' == *end) && end [1])
                ++end;
        }
        else if (',' == *end) {
            first = severity++;
            if ('\0' == end [1]) {
                fprintf (stderr,
                         "invalid character '%c' at position %d: \"%s\"\n",
                         *end, int (parg - argbeg), argv [0]);
                return 2;
            }

            ++end;
        }
        else if ('-' == *end) {
            first = severity;
            while (' ' == *++end);
            if ('\0' == *end) {
                severity = N_DIAG_TYPES;
            }
            else if  (',' == *end) {
                severity = N_DIAG_TYPES;
                ++end;
            }
            else
                severity = -1;
        }
        else if ('\0' == *end) {
            first = severity++;
        }
        else {
            fprintf (stderr,
                     "invalid character '%c' at position %d: \"%s\"\n",
                     *end, int (parg - argbeg), argv [0]);
            return 2;
        }

        parg = end;

        if (0 <= first && first < severity) {
            for (int i = first; i != severity; ++i)
                diag_set |= 1 << i;
            severity = -1;
        }
    }

    _rw_diag_mask = ~diag_set;

    return 0;
}

/************************************************************************/

int
_rw_setopt_trace (int argc, char *argv[])
{
    if (1 == argc && argv && 0 == argv [0]) {
        static const char helpstr[] = {
            "Specifies that diagnostic messages of all severities be issued\n"
            "regardless of whether they are active or not. See the --severity\n"
            "option for details.\n"
        };

        argv [0] = _RWSTD_CONST_CAST (char*, helpstr);

        return 0;
    }

    return _rw_setopt_trace_mask (argc, argv);
}
