diff options
Diffstat (limited to 'src/boost/libs/program_options')
58 files changed, 8733 insertions, 0 deletions
diff --git a/src/boost/libs/program_options/Jamfile b/src/boost/libs/program_options/Jamfile new file mode 100644 index 00000000..f0de2e0b --- /dev/null +++ b/src/boost/libs/program_options/Jamfile @@ -0,0 +1,11 @@ +# Boost.ProgramOptions Library Jamfile +# +# Copyright (c) 2018 James E. King III +# +# Use, modification, and distribution are subject to the +# Boost Software License, Version 1.0. (See accompanying file +# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +# please order by name to ease maintenance +build-project example ; +build-project test ; diff --git a/src/boost/libs/program_options/README.md b/src/boost/libs/program_options/README.md new file mode 100644 index 00000000..b2bd595e --- /dev/null +++ b/src/boost/libs/program_options/README.md @@ -0,0 +1,37 @@ +Program Options, part of the collection of [Boost C++ Libraries](http://github.com/boostorg), allows for definition and acquisition of (name, value) pairs from the user via conventional methods such as command line and config file. It is roughly analogous to getopt_long, but for use with C++. + +### License + +Distributed under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt). + +### Properties + +* C++03 +* Requires Linking + +### Build Status +(in progress...) + +|Branch | Travis | Appveyor | codecov.io | Deps | Docs | Tests | +|:-------------: | ------ | -------- | ---------- | ---- | ---- | ----- | +|[`master`](https://github.com/boostorg/program_options/tree/master) | [![Build Status](https://travis-ci.org/boostorg/program_options.svg?branch=master)](https://travis-ci.org/boostorg/program_options) | [![Build status](https://ci.appveyor.com/api/projects/status/e0quisadwh1v7ok5/branch/master?svg=true)](https://ci.appveyor.com/project/vprus/program-options/branch/master) | [![codecov](https://codecov.io/gh/boostorg/program_options/branch/master/graph/badge.svg)](https://codecov.io/gh/boostorg/program_options/branch/master) | [![Deps](https://img.shields.io/badge/deps-master-brightgreen.svg)](https://pdimov.github.io/boostdep-report/master/program_options.html) | [![Documentation](https://img.shields.io/badge/docs-master-brightgreen.svg)](http://www.boost.org/doc/libs/master/doc/html/program_options.html) | [![Enter the Matrix](https://img.shields.io/badge/matrix-master-brightgreen.svg)](http://www.boost.org/development/tests/master/developer/program_options.html) +|[`develop`](https://github.com/boostorg/program_options/tree/develop) | [![Build Status](https://travis-ci.org/boostorg/program_options.svg?branch=develop)](https://travis-ci.org/boostorg/program_options) | [![Build status](https://ci.appveyor.com/api/projects/status/e0quisadwh1v7ok5/branch/develop?svg=true)](https://ci.appveyor.com/project/vprus/program-options/branch/develop) | [![codecov](https://codecov.io/gh/boostorg/program_options/branch/develop/graph/badge.svg)](https://codecov.io/gh/boostorg/program_options/branch/develop) | [![Deps](https://img.shields.io/badge/deps-develop-brightgreen.svg)](https://pdimov.github.io/boostdep-report/develop/program_options.html) | [![Documentation](https://img.shields.io/badge/docs-develop-brightgreen.svg)](http://www.boost.org/doc/libs/develop/doc/html/program_options.html) | [![Enter the Matrix](https://img.shields.io/badge/matrix-develop-brightgreen.svg)](http://www.boost.org/development/tests/develop/developer/program_options.html) + +### Directories + +| Name | Purpose | +| --------- | ------------------------------ | +| `build` | build script for link library | +| `ci` | continuous integration scripts | +| `doc` | documentation | +| `example` | use case examples | +| `include` | headers | +| `src` | source code for link library | +| `test` | unit tests | + +### More information + +* [Ask questions](http://stackoverflow.com/questions/ask?tags=c%2B%2B,boost,boost-program_options): Be sure to read the documentation first to see if it answers your question. +* [Report bugs](https://github.com/boostorg/program_options/issues): Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well. +* [Submit Pull Requests](https://github.com/boostorg/program_options/pulls) against the **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt). Be sure to include tests proving your changes work properly. +* Discussions about the library are held on the [Boost developers mailing list](http://www.boost.org/community/groups.html#main). Be sure to read the [discussion policy](http://www.boost.org/community/policy.html) before posting and add the `[program_options]` tag at the beginning of the subject line. diff --git a/src/boost/libs/program_options/build/Jamfile.v2 b/src/boost/libs/program_options/build/Jamfile.v2 new file mode 100644 index 00000000..852054d0 --- /dev/null +++ b/src/boost/libs/program_options/build/Jamfile.v2 @@ -0,0 +1,16 @@ + +project boost/program_options + : source-location ../src + ; + +SOURCES = + cmdline config_file options_description parsers variables_map + value_semantic positional_options utf8_codecvt_facet + convert winmain split + ; + +boost-lib program_options + : $(SOURCES).cpp + : # See https://svn.boost.org/trac/boost/ticket/5049 + <target-os>hpux,<toolset>gcc:<define>_INCLUDE_STDC__SOURCE_199901 + ;
\ No newline at end of file diff --git a/src/boost/libs/program_options/ci/build.sh b/src/boost/libs/program_options/ci/build.sh new file mode 100755 index 00000000..49dd24ae --- /dev/null +++ b/src/boost/libs/program_options/ci/build.sh @@ -0,0 +1,19 @@ +#! /bin/bash +# +# Copyright 2017 James E. King III +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# Bash script to run in travis to perform a bjam build +# cwd should be $BOOST_ROOT/libs/$SELF before running +# + +set -ex + +# default language level: c++03 +if [[ -z "$CXXSTD" ]]; then + CXXSTD=03 +fi + +$BOOST_ROOT/b2 . toolset=$TOOLSET cxxstd=$CXXSTD $CXXFLAGS $DEFINES $LINKFLAGS $TESTFLAGS $B2_ADDRESS_MODEL $B2_LINK $B2_THREADING $B2_VARIANT -j3 $* diff --git a/src/boost/libs/program_options/ci/codecov.sh b/src/boost/libs/program_options/ci/codecov.sh new file mode 100755 index 00000000..2f7ea10f --- /dev/null +++ b/src/boost/libs/program_options/ci/codecov.sh @@ -0,0 +1,43 @@ +#! /bin/bash +# +# Copyright 2017, 2018 James E. King III +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# Bash script to run in travis to perform codecov.io integration +# + +### +### NOTE: Make sure you grab .codecov.yml +### + +# assumes cwd is the top level directory of the boost project +# assumes an environment variable $SELF is the boost project name + +set -ex + +B2_VARIANT=debug +ci/build.sh cxxflags=-fprofile-arcs cxxflags=-ftest-coverage linkflags=-fprofile-arcs linkflags=-ftest-coverage + +# switch back to the original source code directory +cd $TRAVIS_BUILD_DIR + +# get the version of lcov +lcov --version + +# coverage files are in ../../b2 from this location +lcov --gcov-tool=gcov-7 --rc lcov_branch_coverage=1 --base-directory "$BOOST_ROOT/libs/$SELF" --directory "$BOOST_ROOT" --capture --output-file all.info + +# all.info contains all the coverage info for all projects - limit to ours +lcov --gcov-tool=gcov-7 --rc lcov_branch_coverage=1 --extract all.info "*/boost/$SELF/*" "*/libs/$SELF/src/*" --output-file coverage.info + +# dump a summary on the console - helps us identify problems in pathing +lcov --gcov-tool=gcov-7 --rc lcov_branch_coverage=1 --list coverage.info + +# +# upload to codecov.io +# +curl -s https://codecov.io/bash > .codecov +chmod +x .codecov +./.codecov -f coverage.info -X gcov -x "gcov-7" diff --git a/src/boost/libs/program_options/ci/coverity.sh b/src/boost/libs/program_options/ci/coverity.sh new file mode 100755 index 00000000..0de08a4c --- /dev/null +++ b/src/boost/libs/program_options/ci/coverity.sh @@ -0,0 +1,42 @@ +#! /bin/bash +# +# Copyright 2017 James E. King III +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# Bash script to run in travis to perform a Coverity Scan build +# To skip the coverity integration download (which is huge) if +# you already have it from a previous run, add --skipdownload +# + +# +# Environment Variables +# +# COVERITY_SCAN_NOTIFICATION_EMAIL - email address to notify +# COVERITY_SCAN_TOKEN - the Coverity Scan token (should be secure) +# SELF - the boost libs directory name + +set -ex + +pushd /tmp +if [[ "$1" != "--skipdownload" ]]; then + rm -rf coverity_tool.tgz cov-analysis* + wget https://scan.coverity.com/download/linux64 --post-data "token=$COVERITY_SCAN_TOKEN&project=boostorg/$SELF" -O coverity_tool.tgz + tar xzf coverity_tool.tgz +fi +COVBIN=$(echo $(pwd)/cov-analysis*/bin) +export PATH=$COVBIN:$PATH +popd + +ci/build.sh clean +rm -rf cov-int/ +cov-build --dir cov-int ci/build.sh +tar cJf cov-int.tar.xz cov-int/ +curl --form token="$COVERITY_SCAN_TOKEN" \ + --form email="$COVERITY_SCAN_NOTIFICATION_EMAIL" \ + --form file=@cov-int.tar.xz \ + --form version="$(git describe --tags)" \ + --form description="boostorg/$SELF" \ + https://scan.coverity.com/builds?project="boostorg/$SELF" + diff --git a/src/boost/libs/program_options/ci/cppcheck.sh b/src/boost/libs/program_options/ci/cppcheck.sh new file mode 100755 index 00000000..7734ffcc --- /dev/null +++ b/src/boost/libs/program_options/ci/cppcheck.sh @@ -0,0 +1,38 @@ +#! /bin/bash +# +# Copyright 2018 James E. King III +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# Bash script to run in travis to perform a cppcheck +# cwd should be $BOOST_ROOT before running +# + +set -ex + +# default language level: c++03 +if [[ -z "$CXXSTD" ]]; then + CXXSTD=03 +fi + +# Travis' ubuntu-trusty comes with cppcheck 1.62 which is pretty old +# default cppcheck version: 1.82 +if [[ -z "$CPPCHKVER" ]]; then + CPPCHKVER=1.82 +fi + +pushd ~ +wget https://github.com/danmar/cppcheck/archive/$CPPCHKVER.tar.gz +tar xzf $CPPCHKVER.tar.gz +mkdir cppcheck-build +cd cppcheck-build +cmake ../cppcheck-$CPPCHKVER -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=~/cppcheck +make -j3 install +popd + +~/cppcheck/bin/cppcheck -I. --std=c++$CXXSTD --enable=all --error-exitcode=1 \ + --force --check-config --suppress=*:boost/preprocessor/tuple/size.hpp \ + -UBOOST_USER_CONFIG -UBOOST_COMPILER_CONFIG -UBOOST_STDLIB_CONFIG -UBOOST_PLATFORM_CONFIG \ + libs/$SELF 2>&1 | grep -v 'Cppcheck does not need standard library headers' + diff --git a/src/boost/libs/program_options/ci/mingw.bat b/src/boost/libs/program_options/ci/mingw.bat new file mode 100755 index 00000000..bc189fdb --- /dev/null +++ b/src/boost/libs/program_options/ci/mingw.bat @@ -0,0 +1,50 @@ +:: +:: MinGW Build Script for Appveyor, leveraging the MSYS2 installation +:: Copyright (C) 2018 James E. King III +:: Distributed under the Boost Software License, Version 1.0. +:: (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) +:: + +@ECHO ON +SETLOCAL EnableDelayedExpansion + +:: Set up the toolset +echo using gcc : %FLAVOR% : %ARCH%-w64-mingw32-g++.exe ; > %USERPROFILE%\user-config.jam +SET UPPERFLAVOR=%FLAVOR% +CALL :TOUPPER UPPERFLAVOR + +:: Install packages needed to build boost +:: Optional: comment out ones this library does not need, +:: so people can copy this script to another library. + +FOR %%a IN ("gcc" "icu" "libiconv" "openssl" "xz" "zlib") DO ( + c:\msys64\usr\bin\env MSYSTEM=%UPPERFLAVOR% c:\msys64\usr\bin\bash -l -c ^ + "pacman --sync --needed --noconfirm %FLAVOR%/mingw-w64-%ARCH%-%%a" || EXIT /B +) +c:\msys64\usr\bin\env MSYSTEM=%UPPERFLAVOR% c:\msys64\usr\bin\bash -l -c ^ + "pacman --sync --needed --noconfirm python3" || EXIT /B + +:: +:: Now build things... +:: + +c:\msys64\usr\bin\env MSYSTEM=%UPPERFLAVOR% c:\msys64\usr\bin\bash -l -c ^ + "cd %CD:\=/% && ./bootstrap.sh --with-toolset=gcc" || EXIT /B + +c:\msys64\usr\bin\env MSYSTEM=%UPPERFLAVOR% c:\msys64\usr\bin\bash -l -c ^ + "cd %CD:\=/% && ./b2 libs/%SELF% toolset=gcc-%FLAVOR% cxxstd=%CXXSTD% %CXXFLAGS% %DEFINES% %B2_ADDRESS_MODEL% %B2_LINK% %B2_THREADING% %B2_VARIANT% -j3" || EXIT /B + +EXIT /B 0 + +:: +:: Function to uppercase a variable +:: from: https://stackoverflow.com/questions/34713621/batch-converting-variable-to-uppercase +:: + +:TOUPPER <variable> +@ECHO OFF +FOR %%a IN ("a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I" + "j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R" + "s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z" ) DO ( CALL SET %~1=%%%~1:%%~a%% ) +@ECHO ON +GOTO :EOF
\ No newline at end of file diff --git a/src/boost/libs/program_options/example/Jamfile.v2 b/src/boost/libs/program_options/example/Jamfile.v2 new file mode 100644 index 00000000..9f0b1d8f --- /dev/null +++ b/src/boost/libs/program_options/example/Jamfile.v2 @@ -0,0 +1,21 @@ + +project + : requirements <library>../build//boost_program_options + <hardcode-dll-paths>true + <link>static + ; + +exe first : first.cpp ; +exe options_description : options_description.cpp ; +exe multiple_sources : multiple_sources.cpp ; +exe custom_syntax : custom_syntax.cpp ; + +exe real : real.cpp ; +exe regex : regex.cpp /boost/regex//boost_regex ; + +# The following examples use C++ features beyond C++03. +# It would be possible to make compilation of each conditional on specific config check, +# for now just disable the compilation. +#exe config_file_types : config_file_types.cpp ; +#exe env_options : env_options.cpp ; +#exe options_heirarchy : options_heirarchy.cpp ; diff --git a/src/boost/libs/program_options/example/config_file_types.cpp b/src/boost/libs/program_options/example/config_file_types.cpp new file mode 100644 index 00000000..e466e94a --- /dev/null +++ b/src/boost/libs/program_options/example/config_file_types.cpp @@ -0,0 +1,242 @@ +// Copyright Thomas Kent 2016 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows a config file (in ini format) being parsed by the +// program_options library. It includes a numebr of different value types. + +#include <boost/program_options.hpp> +namespace po = boost::program_options; + +#include <assert.h> +#include <iostream> +#include <sstream> +using namespace std; + +const double FLOAT_SEPERATION = 0.00000000001; +bool check_float(double test, double expected) +{ + double seperation = expected * (1 + FLOAT_SEPERATION) / expected; + if ((test < expected + seperation) && (test > expected - seperation)) + { + return true; + } + return false; +} + +stringstream make_file() +{ + stringstream ss; + ss << "# This file checks parsing of various types of config values\n"; + //FAILS: ss << "; a windows style comment\n"; + + ss << "global_string = global value\n"; + ss << "unregistered_entry = unregistered value\n"; + + ss << "\n[strings]\n"; + ss << "word = word\n"; + ss << "phrase = this is a phrase\n"; + ss << "quoted = \"quotes are in result\"\n"; + + ss << "\n[ints]\n"; + ss << "positive = 41\n"; + ss << "negative = -42\n"; + //FAILS: Lexical cast doesn't support hex, oct, or bin + //ss << "hex = 0x43\n"; + //ss << "oct = 044\n"; + //ss << "bin = 0b101010\n"; + + ss << "\n[floats]\n"; + ss << "positive = 51.1\n"; + ss << "negative = -52.1\n"; + ss << "double = 53.1234567890\n"; + ss << "int = 54\n"; + ss << "int_dot = 55.\n"; + ss << "dot = .56\n"; + ss << "exp_lower = 57.1e5\n"; + ss << "exp_upper = 58.1E5\n"; + ss << "exp_decimal = .591e5\n"; + ss << "exp_negative = 60.1e-5\n"; + ss << "exp_negative_val = -61.1e5\n"; + ss << "exp_negative_negative_val = -62.1e-5\n"; + + ss << "\n[booleans]\n"; + ss << "number_true = 1\n"; + ss << "number_false = 0\n"; + ss << "yn_true = yes\n"; + ss << "yn_false = no\n"; + ss << "tf_true = true\n"; + ss << "tf_false = false\n"; + ss << "onoff_true = on\n"; + ss << "onoff_false = off\n"; + ss << "present_equal_true = \n"; + //FAILS: Must be an = + //ss << "present_no_equal_true\n"; + + ss.seekp(ios_base::beg); + return ss; +} + +po::options_description set_options() +{ + po::options_description opts; + opts.add_options() + ("global_string", po::value<string>()) + + ("strings.word", po::value<string>()) + ("strings.phrase", po::value<string>()) + ("strings.quoted", po::value<string>()) + + ("ints.positive", po::value<int>()) + ("ints.negative", po::value<int>()) + ("ints.hex", po::value<int>()) + ("ints.oct", po::value<int>()) + ("ints.bin", po::value<int>()) + + ("floats.positive", po::value<float>()) + ("floats.negative", po::value<float>()) + ("floats.double", po::value<double>()) + ("floats.int", po::value<float>()) + ("floats.int_dot", po::value<float>()) + ("floats.dot", po::value<float>()) + ("floats.exp_lower", po::value<float>()) + ("floats.exp_upper", po::value<float>()) + ("floats.exp_decimal", po::value<float>()) + ("floats.exp_negative", po::value<float>()) + ("floats.exp_negative_val", po::value<float>()) + ("floats.exp_negative_negative_val", po::value<float>()) + + // Load booleans as value<bool>, so they will require a --option=value on the command line + //("booleans.number_true", po::value<bool>()) + //("booleans.number_false", po::value<bool>()) + //("booleans.yn_true", po::value<bool>()) + //("booleans.yn_false", po::value<bool>()) + //("booleans.tf_true", po::value<bool>()) + //("booleans.tf_false", po::value<bool>()) + //("booleans.onoff_true", po::value<bool>()) + //("booleans.onoff_false", po::value<bool>()) + //("booleans.present_equal_true", po::value<bool>()) + //("booleans.present_no_equal_true", po::value<bool>()) + + // Load booleans as bool_switch, so that a --option will set it true on the command line + // The difference between these two types does not show up when parsing a file + ("booleans.number_true", po::bool_switch()) + ("booleans.number_false", po::bool_switch()) + ("booleans.yn_true", po::bool_switch()) + ("booleans.yn_false", po::bool_switch()) + ("booleans.tf_true", po::bool_switch()) + ("booleans.tf_false", po::bool_switch()) + ("booleans.onoff_true", po::bool_switch()) + ("booleans.onoff_false", po::bool_switch()) + ("booleans.present_equal_true", po::bool_switch()) + ("booleans.present_no_equal_true", po::bool_switch()) + ; + return opts; +} + +vector<string> parse_file(stringstream &file, po::options_description &opts, po::variables_map &vm) +{ + const bool ALLOW_UNREGISTERED = true; + cout << file.str() << endl; + + po::parsed_options parsed = parse_config_file(file, opts, ALLOW_UNREGISTERED); + store(parsed, vm); + vector<string> unregistered = po::collect_unrecognized(parsed.options, po::exclude_positional); + notify(vm); + + return unregistered; +} + +void check_results(po::variables_map &vm, vector<string> unregistered) +{ + // Check that we got the correct values back + string expected_global_string = "global value"; + + string expected_unreg_option = "unregistered_entry"; + string expected_unreg_value = "unregistered value"; + + string expected_strings_word = "word"; + string expected_strings_phrase = "this is a phrase"; + string expected_strings_quoted = "\"quotes are in result\""; + + int expected_int_postitive = 41; + int expected_int_negative = -42; + int expected_int_hex = 0x43; + int expected_int_oct = 044; + int expected_int_bin = 0b101010; + + float expected_float_positive = 51.1f; + float expected_float_negative = -52.1f; + double expected_float_double = 53.1234567890; + float expected_float_int = 54.0f; + float expected_float_int_dot = 55.0f; + float expected_float_dot = .56f; + float expected_float_exp_lower = 57.1e5f; + float expected_float_exp_upper = 58.1E5f; + float expected_float_exp_decimal = .591e5f; + float expected_float_exp_negative = 60.1e-5f; + float expected_float_exp_negative_val = -61.1e5f; + float expected_float_exp_negative_negative_val = -62.1e-5f; + + bool expected_number_true = true; + bool expected_number_false = false; + bool expected_yn_true = true; + bool expected_yn_false = false; + bool expected_tf_true = true; + bool expected_tf_false = false; + bool expected_onoff_true = true; + bool expected_onoff_false = false; + bool expected_present_equal_true = true; + bool expected_present_no_equal_true = true; + + assert(vm["global_string"].as<string>() == expected_global_string); + + assert(unregistered[0] == expected_unreg_option); + assert(unregistered[1] == expected_unreg_value); + + assert(vm["strings.word"].as<string>() == expected_strings_word); + assert(vm["strings.phrase"].as<string>() == expected_strings_phrase); + assert(vm["strings.quoted"].as<string>() == expected_strings_quoted); + + assert(vm["ints.positive"].as<int>() == expected_int_postitive); + assert(vm["ints.negative"].as<int>() == expected_int_negative); + //assert(vm["ints.hex"].as<int>() == expected_int_hex); + //assert(vm["ints.oct"].as<int>() == expected_int_oct); + //assert(vm["ints.bin"].as<int>() == expected_int_bin); + + assert(check_float(vm["floats.positive"].as<float>(), expected_float_positive)); + assert(check_float(vm["floats.negative"].as<float>(), expected_float_negative)); + assert(check_float(vm["floats.double"].as<double>(), expected_float_double)); + assert(check_float(vm["floats.int"].as<float>(), expected_float_int)); + assert(check_float(vm["floats.int_dot"].as<float>(), expected_float_int_dot)); + assert(check_float(vm["floats.dot"].as<float>(), expected_float_dot)); + assert(check_float(vm["floats.exp_lower"].as<float>(), expected_float_exp_lower)); + assert(check_float(vm["floats.exp_upper"].as<float>(), expected_float_exp_upper)); + assert(check_float(vm["floats.exp_decimal"].as<float>(), expected_float_exp_decimal)); + assert(check_float(vm["floats.exp_negative"].as<float>(), expected_float_exp_negative)); + assert(check_float(vm["floats.exp_negative_val"].as<float>(), expected_float_exp_negative_val)); + assert(check_float(vm["floats.exp_negative_negative_val"].as<float>(), expected_float_exp_negative_negative_val)); + + assert(vm["booleans.number_true"].as<bool>() == expected_number_true); + assert(vm["booleans.number_false"].as<bool>() == expected_number_false); + assert(vm["booleans.yn_true"].as<bool>() == expected_yn_true); + assert(vm["booleans.yn_false"].as<bool>() == expected_yn_false); + assert(vm["booleans.tf_true"].as<bool>() == expected_tf_true); + assert(vm["booleans.tf_false"].as<bool>() == expected_tf_false); + assert(vm["booleans.onoff_true"].as<bool>() == expected_onoff_true); + assert(vm["booleans.onoff_false"].as<bool>() == expected_onoff_false); + assert(vm["booleans.present_equal_true"].as<bool>() == expected_present_equal_true); + //assert(vm["booleans.present_no_equal_true"].as<bool>() == expected_present_no_equal_true); +} + +int main(int ac, char* av[]) +{ + auto file = make_file(); + auto opts = set_options(); + po::variables_map vars; + auto unregistered = parse_file(file, opts, vars); + check_results(vars, unregistered); + + return 0; +} diff --git a/src/boost/libs/program_options/example/custom_syntax.cpp b/src/boost/libs/program_options/example/custom_syntax.cpp new file mode 100644 index 00000000..829087d9 --- /dev/null +++ b/src/boost/libs/program_options/example/custom_syntax.cpp @@ -0,0 +1,63 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** This example shows how to support custom options syntax. + + It's possible to install 'custom_parser'. It will be invoked on all command + line tokens and can return name/value pair, or nothing. If it returns + nothing, usual processing will be done. +*/ + + +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/variables_map.hpp> + +using namespace boost::program_options; + +#include <iostream> +using namespace std; + +/* This custom option parse function recognize gcc-style + option "-fbar" / "-fno-bar". +*/ +pair<string, string> reg_foo(const string& s) +{ + if (s.find("-f") == 0) { + if (s.substr(2, 3) == "no-") + return make_pair(s.substr(5), string("false")); + else + return make_pair(s.substr(2), string("true")); + } else { + return make_pair(string(), string()); + } +} + +int main(int ac, char* av[]) +{ + try { + options_description desc("Allowed options"); + desc.add_options() + ("help", "produce a help message") + ("foo", value<string>(), "just an option") + ; + + variables_map vm; + store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo) + .run(), vm); + + if (vm.count("help")) { + cout << desc; + cout << "\nIn addition -ffoo and -fno-foo syntax are recognized.\n"; + } + if (vm.count("foo")) { + cout << "foo value with the value of " + << vm["foo"].as<string>() << "\n"; + } + } + catch(exception& e) { + cout << e.what() << "\n"; + } +} diff --git a/src/boost/libs/program_options/example/env_options.cpp b/src/boost/libs/program_options/example/env_options.cpp new file mode 100644 index 00000000..bab37e25 --- /dev/null +++ b/src/boost/libs/program_options/example/env_options.cpp @@ -0,0 +1,47 @@ +// Copyright Thomas Kent 2016 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/program_options.hpp> +namespace po = boost::program_options; +#include <string> +#include <iostream> + +std::string mapper(std::string env_var) +{ + // ensure the env_var is all caps + std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper); + + if (env_var == "PATH") return "path"; + if (env_var == "EXAMPLE_VERBOSE") return "verbosity"; + return ""; +} + +void get_env_options() +{ + po::options_description config("Configuration"); + config.add_options() + ("path", "the execution path") + ("verbosity", po::value<std::string>()->default_value("INFO"), "set verbosity: DEBUG, INFO, WARN, ERROR, FATAL") + ; + + po::variables_map vm; + store(po::parse_environment(config, boost::function1<std::string, std::string>(mapper)), vm); + notify(vm); + + if (vm.count("path")) + { + std::cout << "First 75 chars of the system path: \n"; + std::cout << vm["path"].as<std::string>().substr(0, 75) << std::endl; + } + + std::cout << "Verbosity: " << vm["verbosity"].as<std::string>() << std::endl; +} + +int main(int ac, char* av[]) +{ + get_env_options(); + + return 0; +} diff --git a/src/boost/libs/program_options/example/first.cpp b/src/boost/libs/program_options/example/first.cpp new file mode 100644 index 00000000..8763fec9 --- /dev/null +++ b/src/boost/libs/program_options/example/first.cpp @@ -0,0 +1,51 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +/* The simplest usage of the library. + */ + +#include <boost/program_options.hpp> +namespace po = boost::program_options; + +#include <iostream> +#include <iterator> +using namespace std; + +int main(int ac, char* av[]) +{ + try { + + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "produce help message") + ("compression", po::value<double>(), "set compression level") + ; + + po::variables_map vm; + po::store(po::parse_command_line(ac, av, desc), vm); + po::notify(vm); + + if (vm.count("help")) { + cout << desc << "\n"; + return 0; + } + + if (vm.count("compression")) { + cout << "Compression level was set to " + << vm["compression"].as<double>() << ".\n"; + } else { + cout << "Compression level was not set.\n"; + } + } + catch(exception& e) { + cerr << "error: " << e.what() << "\n"; + return 1; + } + catch(...) { + cerr << "Exception of unknown type!\n"; + } + + return 0; +} diff --git a/src/boost/libs/program_options/example/multiple_sources.cfg b/src/boost/libs/program_options/example/multiple_sources.cfg new file mode 100644 index 00000000..6966d004 --- /dev/null +++ b/src/boost/libs/program_options/example/multiple_sources.cfg @@ -0,0 +1,5 @@ +# +# Comment out this line to use hard-coded default value of 10 +# +optimization = 1 +include-path = /opt
\ No newline at end of file diff --git a/src/boost/libs/program_options/example/multiple_sources.cpp b/src/boost/libs/program_options/example/multiple_sources.cpp new file mode 100644 index 00000000..22c8235b --- /dev/null +++ b/src/boost/libs/program_options/example/multiple_sources.cpp @@ -0,0 +1,121 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +/* Shows how to use both command line and config file. */ + +#include <boost/program_options.hpp> +namespace po = boost::program_options; + + +#include <iostream> +#include <fstream> +#include <iterator> +using namespace std; + +// A helper function to simplify the main part. +template<class T> +ostream& operator<<(ostream& os, const vector<T>& v) +{ + copy(v.begin(), v.end(), ostream_iterator<T>(os, " ")); + return os; +} + + +int main(int ac, char* av[]) +{ + try { + int opt; + string config_file; + + // Declare a group of options that will be + // allowed only on command line + po::options_description generic("Generic options"); + generic.add_options() + ("version,v", "print version string") + ("help", "produce help message") + ("config,c", po::value<string>(&config_file)->default_value("multiple_sources.cfg"), + "name of a file of a configuration.") + ; + + // Declare a group of options that will be + // allowed both on command line and in + // config file + po::options_description config("Configuration"); + config.add_options() + ("optimization", po::value<int>(&opt)->default_value(10), + "optimization level") + ("include-path,I", + po::value< vector<string> >()->composing(), + "include path") + ; + + // Hidden options, will be allowed both on command line and + // in config file, but will not be shown to the user. + po::options_description hidden("Hidden options"); + hidden.add_options() + ("input-file", po::value< vector<string> >(), "input file") + ; + + + po::options_description cmdline_options; + cmdline_options.add(generic).add(config).add(hidden); + + po::options_description config_file_options; + config_file_options.add(config).add(hidden); + + po::options_description visible("Allowed options"); + visible.add(generic).add(config); + + po::positional_options_description p; + p.add("input-file", -1); + + po::variables_map vm; + store(po::command_line_parser(ac, av). + options(cmdline_options).positional(p).run(), vm); + notify(vm); + + ifstream ifs(config_file.c_str()); + if (!ifs) + { + cout << "can not open config file: " << config_file << "\n"; + return 0; + } + else + { + store(parse_config_file(ifs, config_file_options), vm); + notify(vm); + } + + if (vm.count("help")) { + cout << visible << "\n"; + return 0; + } + + if (vm.count("version")) { + cout << "Multiple sources example, version 1.0\n"; + return 0; + } + + if (vm.count("include-path")) + { + cout << "Include paths are: " + << vm["include-path"].as< vector<string> >() << "\n"; + } + + if (vm.count("input-file")) + { + cout << "Input files are: " + << vm["input-file"].as< vector<string> >() << "\n"; + } + + cout << "Optimization level is " << opt << "\n"; + } + catch(exception& e) + { + cout << e.what() << "\n"; + return 1; + } + return 0; +} diff --git a/src/boost/libs/program_options/example/option_groups.cpp b/src/boost/libs/program_options/example/option_groups.cpp new file mode 100644 index 00000000..63e1fca4 --- /dev/null +++ b/src/boost/libs/program_options/example/option_groups.cpp @@ -0,0 +1,97 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** This example shows how to handle options groups. + + For a test, run: + + option_groups --help + option_groups --num-threads 10 + option_groups --help-module backend + + The first invocation would show to option groups, and will not show the + '--num-threads' options. The second invocation will still get the value of + the hidden '--num-threads' option. Finally, the third invocation will show + the options for the 'backend' module, including the '--num-threads' option. + +*/ + + +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/tokenizer.hpp> +#include <boost/token_functions.hpp> +using namespace boost; +using namespace boost::program_options; + +#include <iostream> +#include <fstream> +#include <exception> +using namespace std; + +int main(int ac, char* av[]) +{ + try { + // Declare three groups of options. + options_description general("General options"); + general.add_options() + ("help", "produce a help message") + ("help-module", value<string>(), + "produce a help for a given module") + ("version", "output the version number") + ; + + options_description gui("GUI options"); + gui.add_options() + ("display", value<string>(), "display to use") + ; + + options_description backend("Backend options"); + backend.add_options() + ("num-threads", value<int>(), "the initial number of threads") + ; + + // Declare an options description instance which will include + // all the options + options_description all("Allowed options"); + all.add(general).add(gui).add(backend); + + // Declare an options description instance which will be shown + // to the user + options_description visible("Allowed options"); + visible.add(general).add(gui); + + + variables_map vm; + store(parse_command_line(ac, av, all), vm); + + if (vm.count("help")) + { + cout << visible; + return 0; + } + if (vm.count("help-module")) { + const string& s = vm["help-module"].as<string>(); + if (s == "gui") { + cout << gui; + } else if (s == "backend") { + cout << backend; + } else { + cout << "Unknown module '" + << s << "' in the --help-module option\n"; + return 1; + } + return 0; + } + if (vm.count("num-threads")) { + cout << "The 'num-threads' options was set to " + << vm["num-threads"].as<int>() << "\n"; + } + } + catch(std::exception& e) { + cout << e.what() << "\n"; + } +} diff --git a/src/boost/libs/program_options/example/options_description.cpp b/src/boost/libs/program_options/example/options_description.cpp new file mode 100644 index 00000000..e9ad2a67 --- /dev/null +++ b/src/boost/libs/program_options/example/options_description.cpp @@ -0,0 +1,86 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/program_options.hpp> + +using namespace boost; +namespace po = boost::program_options; + +#include <iostream> +#include <algorithm> +#include <iterator> +using namespace std; + + +// A helper function to simplify the main part. +template<class T> +ostream& operator<<(ostream& os, const vector<T>& v) +{ + copy(v.begin(), v.end(), ostream_iterator<T>(os, " ")); + return os; +} + +int main(int ac, char* av[]) +{ + try { + int opt; + int portnum; + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "produce help message") + ("optimization", po::value<int>(&opt)->default_value(10), + "optimization level") + ("verbose,v", po::value<int>()->implicit_value(1), + "enable verbosity (optionally specify level)") + ("listen,l", po::value<int>(&portnum)->implicit_value(1001) + ->default_value(0,"no"), + "listen on a port.") + ("include-path,I", po::value< vector<string> >(), + "include path") + ("input-file", po::value< vector<string> >(), "input file") + ; + + po::positional_options_description p; + p.add("input-file", -1); + + po::variables_map vm; + po::store(po::command_line_parser(ac, av). + options(desc).positional(p).run(), vm); + po::notify(vm); + + if (vm.count("help")) { + cout << "Usage: options_description [options]\n"; + cout << desc; + return 0; + } + + if (vm.count("include-path")) + { + cout << "Include paths are: " + << vm["include-path"].as< vector<string> >() << "\n"; + } + + if (vm.count("input-file")) + { + cout << "Input files are: " + << vm["input-file"].as< vector<string> >() << "\n"; + } + + if (vm.count("verbose")) { + cout << "Verbosity enabled. Level is " << vm["verbose"].as<int>() + << "\n"; + } + + cout << "Optimization level is " << opt << "\n"; + + cout << "Listen port is " << portnum << "\n"; + } + catch(std::exception& e) + { + cout << e.what() << "\n"; + return 1; + } + return 0; +} diff --git a/src/boost/libs/program_options/example/options_heirarchy.cpp b/src/boost/libs/program_options/example/options_heirarchy.cpp new file mode 100644 index 00000000..c913b149 --- /dev/null +++ b/src/boost/libs/program_options/example/options_heirarchy.cpp @@ -0,0 +1,690 @@ +// Copyright Thomas Kent 2016 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// +// This is an example of a program that uses multiple facets of the boost +// program_options library. It will go through different types of config +// options in a heirarchal manner: +// 1. Default options are set. +// 2. Command line options are set (they override defaults). +// 3. Environment options are set (they override defaults but not command +// line options). +// 4. Config files specified on the command line are read, if present, in +// the order specified. (these override defaults but not options from the +// other steps). +// 5. Default config file (default.cfg) is read, if present (it overrides +// defaults but not options from the other steps). +// +// See the bottom of this file for full usage examples +// + +#include <boost/program_options.hpp> +namespace po = boost::program_options; +#include <string> +#include <iostream> +#include <map> +#include <stdexcept> +#include <fstream> + +const std::string version("1.0"); + +// Used to exit the program if the help/version option is set +class OptionsExitsProgram : public std::exception +{}; + +struct GuiOpts +{ + unsigned int width; + unsigned int height; +}; + +struct NetworkOpts +{ + std::string address; + unsigned short port; +}; + +class OptionsHeirarchy +{ +public: + // The constructor sets up all the various options that will be parsed + OptionsHeirarchy() + { + SetOptions(); + } + + // Parse options runs through the heirarchy doing all the parsing + void ParseOptions(int argc, char* argv[]) + { + ParseCommandLine(argc, argv); + CheckForHelp(); + CheckForVersion(); + ParseEnvironment(); + ParseConfigFiles(); + ParseDefaultConfigFile(); + } + + // Below is the interface to access the data, once ParseOptions has been run + std::string Path() + { + return results["path"].as<std::string>(); + } + std::string Verbosity() + { + return results["verbosity"].as<std::string>(); + } + std::vector<std::string> IncludePath() + { + if (results.count("include-path")) + { + return results["include-path"].as<std::vector<std::string>>(); + } + return std::vector<std::string>(); + } + std::string MasterFile() + { + if (results.count("master-file")) + { + return results["master-file"].as<std::string>(); + } + return ""; + } + std::vector<std::string> Files() + { + if (results.count("file")) + { + return results["file"].as<std::vector<std::string>>(); + } + return std::vector<std::string>(); + } + bool GUI() + { + if (results["run-gui"].as<bool>()) + { + return true; + } + return false; + } + GuiOpts GuiValues() + { + GuiOpts opts; + opts.width = results["gui.width"].as<unsigned int>(); + opts.height = results["gui.height"].as<unsigned int>(); + return opts; + } + NetworkOpts NetworkValues() + { + NetworkOpts opts; + opts.address = results["network.ip"].as<std::string>(); + opts.port = results["network.port"].as<unsigned short>(); + return opts; + } + +private: + void SetOptions() + { + SetCommandLineOptions(); + SetCommonOptions(); + SetConfigOnlyOptions(); + SetEnvMapping(); + } + void SetCommandLineOptions() + { + command_line_options.add_options() + ("help,h", "display this help message") + ("version,v", "show program version") + ("config,c", po::value<std::vector<std::string>>(), + "config files to parse (always parses default.cfg)") + ; + hidden_command_line_options.add_options() + ("master-file", po::value<std::string>()) + ("file", po::value<std::vector<std::string>>()) + ; + positional_options.add("master-file", 1); + positional_options.add("file", -1); + } + void SetCommonOptions() + { + common_options.add_options() + ("path", po::value<std::string>()->default_value(""), + "the execution path to use (imports from environment if not specified)") + ("verbosity", po::value<std::string>()->default_value("INFO"), + "set verbosity: DEBUG, INFO, WARN, ERROR, FATAL") + ("include-path,I", po::value<std::vector<std::string>>()->composing(), + "paths to search for include files") + ("run-gui", po::bool_switch(), "start the GUI") + ; + } + void SetConfigOnlyOptions() + { + config_only_options.add_options() + ("log-dir", po::value<std::string>()->default_value("log")) + ("gui.height", po::value<unsigned int>()->default_value(100)) + ("gui.width", po::value<unsigned int>()->default_value(100)) + ("network.ip", po::value<std::string>()->default_value("127.0.0.1")) + ("network.port", po::value<unsigned short>()->default_value(12345)) + ; + // Run a parser here (with no command line options) to add these defaults into + // results, this way they will be enabled even if no config files are parsed. + store(po::command_line_parser(0, 0).options(config_only_options).run(), results); + notify(results); + } + void SetEnvMapping() + { + env_to_option["PATH"] = "path"; + env_to_option["EXAMPLE_VERBOSE"] = "verbosity"; + } + + + void ParseCommandLine(int argc, char* argv[]) + { + po::options_description cmd_opts; + cmd_opts.add(command_line_options).add(hidden_command_line_options).add(common_options); + store(po::command_line_parser(argc, argv). + options(cmd_opts).positional(positional_options).run(), results); + notify(results); + } + void CheckForHelp() + { + if (results.count("help")) + { + PrintHelp(); + } + } + void PrintHelp() + { + std::cout << "Program Options Example" << std::endl; + std::cout << "Usage: example [OPTION]... MASTER-FILE [FILE]...\n"; + std::cout << " or example [OPTION] --run-gui\n"; + po::options_description help_opts; + help_opts.add(command_line_options).add(common_options); + std::cout << help_opts << std::endl; + throw OptionsExitsProgram(); + } + void CheckForVersion() + { + if (results.count("version")) + { + PrintVersion(); + } + } + void PrintVersion() + { + std::cout << "Program Options Example " << version << std::endl; + throw OptionsExitsProgram(); + } + void ParseEnvironment() + { + store(po::parse_environment(common_options, + // The next two lines are the crazy syntax to use EnvironmentMapper as + // the lookup function for env->config name conversions + boost::function1<std::string, std::string>( + std::bind1st(std::mem_fun(&OptionsHeirarchy::EnvironmentMapper), this))), + results); + notify(results); + } + std::string EnvironmentMapper(std::string env_var) + { + // ensure the env_var is all caps + std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper); + + auto entry = env_to_option.find(env_var); + if (entry != env_to_option.end()) + { + return entry->second; + } + return ""; + } + void ParseConfigFiles() + { + if (results.count("config")) + { + auto files = results["config"].as<std::vector<std::string>>(); + for (auto file = files.begin(); file != files.end(); file++) + { + LoadAConfigFile(*file); + } + } + } + void LoadAConfigFile(std::string filename) + { + bool ALLOW_UNREGISTERED = true; + + po::options_description config_opts; + config_opts.add(config_only_options).add(common_options); + + std::ifstream cfg_file(filename.c_str()); + if (cfg_file) + { + store(parse_config_file(cfg_file, config_opts, ALLOW_UNREGISTERED), results); + notify(results); + } + } + void ParseDefaultConfigFile() + { + LoadAConfigFile("default.cfg"); + } + + std::map<std::string, std::string> env_to_option; + po::options_description config_only_options; + po::options_description common_options; + po::options_description command_line_options; + po::options_description hidden_command_line_options; + po::positional_options_description positional_options; + po::variables_map results; +}; + + +void get_env_options() +{ +} + +void PrintOptions(OptionsHeirarchy options) +{ + auto path = options.Path(); + if (path.length()) + { + std::cout << "First 75 chars of the system path: \n"; + std::cout << options.Path().substr(0, 75) << std::endl; + } + + std::cout << "Verbosity: " << options.Verbosity() << std::endl; + std::cout << "Include Path:\n"; + auto includePaths = options.IncludePath(); + for (auto path = includePaths.begin(); path != includePaths.end(); path++) + { + std::cout << " " << *path << std::endl; + } + std::cout << "Master-File: " << options.MasterFile() << std::endl; + std::cout << "Additional Files:\n"; + auto files = options.Files(); + for (auto file = files.begin(); file != files.end(); file++) + { + std::cout << " " << *file << std::endl; + } + + std::cout << "GUI Enabled: " << std::boolalpha << options.GUI() << std::endl; + if (options.GUI()) + { + auto gui_values = options.GuiValues(); + std::cout << "GUI Height: " << gui_values.height << std::endl; + std::cout << "GUI Width: " << gui_values.width << std::endl; + } + + auto network_values = options.NetworkValues(); + std::cout << "Network Address: " << network_values.address << std::endl; + std::cout << "Network Port: " << network_values.port << std::endl; +} + +int main(int ac, char* av[]) +{ + OptionsHeirarchy options; + try + { + options.ParseOptions(ac, av); + PrintOptions(options); + } + catch (OptionsExitsProgram){} + + return 0; +} + +/* +Full Usage Examples +=================== + +These were run on windows, so some results may show that environment, but +results should be similar on POSIX platforms. + +Help +---- +To see the help screen, with the available options just pass the --help (or -h) +parameter. The program will then exit. + + > example.exe --help + Program Options Example + Usage: example [OPTION]... MASTER-FILE [FILE]... + or example [OPTION] --run-gui + + -h [ --help ] display this help message + -v [ --version ] show program version + -c [ --config ] arg config files to parse (always parses default.cfg) + + --path arg the execution path to use (imports from + environment if not specified) + --verbosity arg (=INFO) set verbosity: DEBUG, INFO, WARN, ERROR, FATAL + -I [ --include-path ] arg paths to search for include files + --run-gui start the GUI + +Version is similar to help (--version or -v). + + > example.exe -v + Program Options Example 1.0 + +Basics +------ +Running without any options will get the default values (path is set from the +environment): + + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +We can easily override that environment path with a simple option: + + > example.exe --path a/b/c;d/e/f + First 75 chars of the system path: + a/b/c;d/e/f + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +You can use a space or equals sign after long options, also backslashes are +treated literally on windows, on POSIX they need to be escaped. + + > example.exe --path=a\b\c\;d\e\\f + First 75 chars of the system path: + a\b\c\;d\e\\f + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +For short options you can use a space: + + > example.exe -I path/to/includes + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + path\to\includes + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +Or you can put the option immediately after it: + + > example.exe -Ipath/to/includes + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + path\to\includes + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +The include path (--include-path or -I) option allows for multiple paths to be +specified (both on the command line and in config files) and combined into a +vector for use by the program. + + > example.exe --include-path=a/b/c --include-path d/e/f -I g/h/i -Ij/k/l + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + a/b/c + d/e/f + g/h/i + j/k/l + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +There are also the option of flags that do not take parameters and just set a +boolean value to true. In this case, running the gui also causes default values +for the gui to be output to the screen. + + > example.exe --run-gui + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: true + GUI Height: 100 + GUI Width: 100 + Network Address: 127.0.0.1 + Network Port: 12345 + +There are also "positional" options at the end of the command line. The first +one specifies the "master" file the others are additional files. + + > example.exe --path=a-path -I an-include master.cpp additional1.cpp additional2.cpp + First 75 chars of the system path: + a-path + Verbosity: INFO + Include Path: + an-include + Master-File: master.cpp + Additional Files: + additional1.cpp + additional2.cpp + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +Environment Variables +--------------------- +In addition to the PATH environment variable, it also knows how to read the +EXAMPLE_VERBOSE environmental variable and use that to set the verbosity +option/ + + > set EXAMPLE_VERBOSE=DEBUG + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: DEBUG + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +However, if the --verboseity flag is also set, it will override the env +variable. This illustrates an important example, the way program_options works, +is that a parser will not override a value that has previously been set by +another parser. Thus the env parser doesn't override the command line parser. +(We will see this again in config files.) Default values are seperate from this +heirarcy, they only apply if no parser has set the value and it is being read. + + > set EXAMPLE_VERBOSE=DEBUG + > example.exe --verbosity=WARN + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: WARN + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +(You can unset an environmental variable with an empty set command) + + > set EXAMPLE_VERBOSE= + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + + +Config Files +------------ +Config files generally follow the [INI file format] +(https://en.wikipedia.org/wiki/INI_file) with a few exceptions. + +Values can be simply added tp options with an equal sign. Here are two include +paths added via the default config file (default.cfg), you can have optional +spaces around the equal sign. + + # You can use comments in a config file + include-path=first/default/path + include-path = second/default/path + +Results in + + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +Values can also be in sections of the config file. Again, editing default.cfg + + include-path=first/default/path + include-path = second/default/path + + [network] + ip=1.2.3.4 + port=3000 + +Results in + + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 1.2.3.4 + Network Port: 3000 + +This example is also setup to allow multiple config files to be specified on +the command line, which are checked before the default.cfg file is read (but +after the environment and command line parsing). Thus we can set the first.cfg +file to contain the following: + + verbosity=ERROR + + [network] + ip = 5.6.7.8 + +Results in: + + > example.exe --config first.cfg + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: ERROR + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 5.6.7.8 + Network Port: 3000 + +But since the config files are read after the command line, setting the +verbosity there causes the value in the file to be ignored. + + > example.exe --config first.cfg --verbosity=WARN + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: WARN + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 5.6.7.8 + Network Port: 3000 + +The config files are parsed in the order they are received on the command line. +So adding the second.cfg file: + + verbosity=FATAL + run-gui=true + + [gui] + height=720 + width=1280 + +Results in a combination of all three config files: + + > example.exe --config first.cfg --config second.cfg + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: ERROR + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: true + GUI Height: 720 + GUI Width: 1280 + Network Address: 5.6.7.8 + Network Port: 3000 + +Incidently the boolean run-gui option could have been set a number of ways +that all result in the C++ boolean value of true: + + run-gui=true + run-gui=on + run-gui=1 + run-gui=yes + run-gui= + +Since run-gui is an option that was set with the bool_switch type, which +forces its use on the command line without a parameter (i.e. --run-gui instead +of --run-gui=true) it can't be given a "false" option, bool_switch values can +only be turned true. If instead we had a value ("my-switch", po::value<bool>()) +that could be set at the command line --my-switch=true or --my-switch=false, or +any of the other types of boolean keywords true: true, on, 1, yes; +false: false, off, 0, no. In a config file this could look like: + + my-switch=true + my-switch=on + my-switch=1 + my-switch=yes + my-switch= + + my-switch=false + my-switch=off + my-switch=0 + my-switch=no + +*/ diff --git a/src/boost/libs/program_options/example/real.cpp b/src/boost/libs/program_options/example/real.cpp new file mode 100644 index 00000000..1c5d1322 --- /dev/null +++ b/src/boost/libs/program_options/example/real.cpp @@ -0,0 +1,96 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options.hpp> +using namespace boost::program_options; + +#include <iostream> +using namespace std; + +/* Auxiliary functions for checking input for validity. */ + +/* Function used to check that 'opt1' and 'opt2' are not specified + at the same time. */ +void conflicting_options(const variables_map& vm, + const char* opt1, const char* opt2) +{ + if (vm.count(opt1) && !vm[opt1].defaulted() + && vm.count(opt2) && !vm[opt2].defaulted()) + throw logic_error(string("Conflicting options '") + + opt1 + "' and '" + opt2 + "'."); +} + +/* Function used to check that of 'for_what' is specified, then + 'required_option' is specified too. */ +void option_dependency(const variables_map& vm, + const char* for_what, const char* required_option) +{ + if (vm.count(for_what) && !vm[for_what].defaulted()) + if (vm.count(required_option) == 0 || vm[required_option].defaulted()) + throw logic_error(string("Option '") + for_what + + "' requires option '" + required_option + "'."); +} + +int main(int argc, char* argv[]) +{ + try { + string ofile; + string macrofile, libmakfile; + bool t_given = false; + bool b_given = false; + string mainpackage; + string depends = "deps_file"; + string sources = "src_file"; + string root = "."; + + options_description desc("Allowed options"); + desc.add_options() + // First parameter describes option name/short name + // The second is parameter to option + // The third is description + ("help,h", "print usage message") + ("output,o", value(&ofile), "pathname for output") + ("macrofile,m", value(¯ofile), "full pathname of macro.h") + ("two,t", bool_switch(&t_given), "preprocess both header and body") + ("body,b", bool_switch(&b_given), "preprocess body in the header context") + ("libmakfile,l", value(&libmakfile), + "write include makefile for library") + ("mainpackage,p", value(&mainpackage), + "output dependency information") + ("depends,d", value(&depends), + "write dependencies to <pathname>") + ("sources,s", value(&sources), "write source package list to <pathname>") + ("root,r", value(&root), "treat <dirname> as project root directory") + ; + + variables_map vm; + store(parse_command_line(argc, argv, desc), vm); + + if (vm.count("help")) { + cout << desc << "\n"; + return 0; + } + + conflicting_options(vm, "output", "two"); + conflicting_options(vm, "output", "body"); + conflicting_options(vm, "output", "mainpackage"); + conflicting_options(vm, "two", "mainpackage"); + conflicting_options(vm, "body", "mainpackage"); + + conflicting_options(vm, "two", "body"); + conflicting_options(vm, "libmakfile", "mainpackage"); + conflicting_options(vm, "libmakfile", "mainpackage"); + + option_dependency(vm, "depends", "mainpackage"); + option_dependency(vm, "sources", "mainpackage"); + option_dependency(vm, "root", "mainpackage"); + + cout << "two = " << vm["two"].as<bool>() << "\n"; + } + catch(exception& e) { + cerr << e.what() << "\n"; + } +} diff --git a/src/boost/libs/program_options/example/regex.cpp b/src/boost/libs/program_options/example/regex.cpp new file mode 100644 index 00000000..df98f77a --- /dev/null +++ b/src/boost/libs/program_options/example/regex.cpp @@ -0,0 +1,101 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how a user-defined class can be parsed using +// specific mechanism -- not the iostream operations used by default. +// +// A new class 'magic_number' is defined and the 'validate' method is overloaded +// to validate the values of that class using Boost.Regex. +// To test, run +// +// regex -m 123-456 +// regex -m 123-4567 +// +// The first invocation should output: +// +// The magic is "456" +// +// and the second invocation should issue an error message. + +#include <boost/program_options.hpp> +#include <boost/regex.hpp> + +using namespace boost; +using namespace boost::program_options; + +#include <iostream> +using namespace std; + +/* Define a completely non-sensical class. */ +struct magic_number { +public: + magic_number(int n) : n(n) {} + int n; +}; + +/* Overload the 'validate' function for the user-defined class. + It makes sure that value is of form XXX-XXX + where X are digits and converts the second group to an integer. + This has no practical meaning, meant only to show how + regex can be used to validate values. +*/ +void validate(boost::any& v, + const std::vector<std::string>& values, + magic_number*, int) +{ + static regex r("\\d\\d\\d-(\\d\\d\\d)"); + + using namespace boost::program_options; + + // Make sure no previous assignment to 'a' was made. + validators::check_first_occurrence(v); + // Extract the first string from 'values'. If there is more than + // one string, it's an error, and exception will be thrown. + const string& s = validators::get_single_string(values); + + // Do regex match and convert the interesting part to + // int. + smatch match; + if (regex_match(s, match, r)) { + v = any(magic_number(lexical_cast<int>(match[1]))); + } else { + throw validation_error(validation_error::invalid_option_value); + } +} + + +int main(int ac, char* av[]) +{ + try { + options_description desc("Allowed options"); + desc.add_options() + ("help", "produce a help screen") + ("version,v", "print the version number") + ("magic,m", value<magic_number>(), + "magic value (in NNN-NNN format)") + ; + + variables_map vm; + store(parse_command_line(ac, av, desc), vm); + + if (vm.count("help")) { + cout << "Usage: regex [options]\n"; + cout << desc; + return 0; + } + if (vm.count("version")) { + cout << "Version 1.\n"; + return 0; + } + if (vm.count("magic")) { + cout << "The magic is \"" + << vm["magic"].as<magic_number>().n << "\"\n"; + } + } + catch(std::exception& e) + { + cout << e.what() << "\n"; + } +} diff --git a/src/boost/libs/program_options/example/response_file.cpp b/src/boost/libs/program_options/example/response_file.cpp new file mode 100644 index 00000000..ce80f76a --- /dev/null +++ b/src/boost/libs/program_options/example/response_file.cpp @@ -0,0 +1,94 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +/** This example shows how to handle response file. + + For a test, build and run: + response_file -I foo @response_file.rsp + + The expected output is: + Include paths: foo bar biz + + Thanks to Hartmut Kaiser who raised the issue of response files + and discussed the possible approach. +*/ + + +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/tokenizer.hpp> +#include <boost/token_functions.hpp> +using namespace boost; +using namespace boost::program_options; + +#include <iostream> +#include <fstream> +using namespace std; + +// Additional command line parser which interprets '@something' as a +// option "config-file" with the value "something" +pair<string, string> at_option_parser(string const&s) +{ + if ('@' == s[0]) + return std::make_pair(string("response-file"), s.substr(1)); + else + return pair<string, string>(); +} + +int main(int ac, char* av[]) +{ + try { + options_description desc("Allowed options"); + desc.add_options() + ("help", "produce a help message") + ("include-path,I", value< vector<string> >()->composing(), + "include path") + ("magic", value<int>(), "magic value") + ("response-file", value<string>(), + "can be specified with '@name', too") + ; + + variables_map vm; + store(command_line_parser(ac, av).options(desc) + .extra_parser(at_option_parser).run(), vm); + + if (vm.count("help")) { + cout << desc; + } + if (vm.count("response-file")) { + // Load the file and tokenize it + ifstream ifs(vm["response-file"].as<string>().c_str()); + if (!ifs) { + cout << "Could not open the response file\n"; + return 1; + } + // Read the whole file into a string + stringstream ss; + ss << ifs.rdbuf(); + // Split the file content + char_separator<char> sep(" \n\r"); + string sstr = ss.str(); + tokenizer<char_separator<char> > tok(sstr, sep); + vector<string> args; + copy(tok.begin(), tok.end(), back_inserter(args)); + // Parse the file and store the options + store(command_line_parser(args).options(desc).run(), vm); + } + + if (vm.count("include-path")) { + const vector<string>& s = vm["include-path"].as<vector< string> >(); + cout << "Include paths: "; + copy(s.begin(), s.end(), ostream_iterator<string>(cout, " ")); + cout << "\n"; + } + if (vm.count("magic")) { + cout << "Magic value: " << vm["magic"].as<int>() << "\n"; + } + } + catch (std::exception& e) { + cout << e.what() << "\n"; + } +} diff --git a/src/boost/libs/program_options/example/response_file.rsp b/src/boost/libs/program_options/example/response_file.rsp new file mode 100644 index 00000000..d7c67773 --- /dev/null +++ b/src/boost/libs/program_options/example/response_file.rsp @@ -0,0 +1,3 @@ +-I bar +-I biz +--magic 10
\ No newline at end of file diff --git a/src/boost/libs/program_options/index.html b/src/boost/libs/program_options/index.html new file mode 100644 index 00000000..66cb529e --- /dev/null +++ b/src/boost/libs/program_options/index.html @@ -0,0 +1,14 @@ +<html> +<head> +<meta http-equiv="refresh" content="0; URL=../../doc/html/program_options.html"> +</head> +<body> +Automatic redirection failed, please go to +<a href="../../doc/html/program_options.html">../../doc/html/program_options.html</a> + <hr> +<p>© Copyright Beman Dawes, 2001</p> +<p>Distributed under the Boost Software License, Version 1.0. (See accompanying +file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy +at <a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a>)</p> +</body> +</html>
\ No newline at end of file diff --git a/src/boost/libs/program_options/meta/libraries.json b/src/boost/libs/program_options/meta/libraries.json new file mode 100644 index 00000000..5fbf3126 --- /dev/null +++ b/src/boost/libs/program_options/meta/libraries.json @@ -0,0 +1,15 @@ +{ + "key": "program_options", + "name": "Program Options", + "authors": [ + "Vladimir Prus" + ], + "description": "The program_options library allows program developers to obtain program options, that is (name, value) pairs from the user, via conventional methods such as command line and config file.", + "category": [ + "IO", + "Miscellaneous" + ], + "maintainers": [ + "Vladimir Prus <vladimir.prus -at- gmail.com>" + ] +} diff --git a/src/boost/libs/program_options/src/cmdline.cpp b/src/boost/libs/program_options/src/cmdline.cpp new file mode 100644 index 00000000..c2cf1da1 --- /dev/null +++ b/src/boost/libs/program_options/src/cmdline.cpp @@ -0,0 +1,713 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> + +#include <boost/config.hpp> + +#include <boost/program_options/detail/cmdline.hpp> +#include <boost/program_options/errors.hpp> +#include <boost/program_options/value_semantic.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/positional_options.hpp> +#include <boost/throw_exception.hpp> + +#include <boost/bind.hpp> + +#include <string> +#include <utility> +#include <vector> +#include <cassert> +#include <cstring> +#include <cctype> +#include <climits> + +#include <cstdio> + +#include <iostream> + +namespace boost { namespace program_options { + + using namespace std; + using namespace boost::program_options::command_line_style; + + + string + invalid_syntax::get_template(kind_t kind) + { + // Initially, store the message in 'const char*' variable, + // to avoid conversion to string in all cases. + const char* msg; + switch(kind) + { + case empty_adjacent_parameter: + msg = "the argument for option '%canonical_option%' should follow immediately after the equal sign"; + break; + case missing_parameter: + msg = "the required argument for option '%canonical_option%' is missing"; + break; + case unrecognized_line: + msg = "the options configuration file contains an invalid line '%invalid_line%'"; + break; + // none of the following are currently used: + case long_not_allowed: + msg = "the unabbreviated option '%canonical_option%' is not valid"; + break; + case long_adjacent_not_allowed: + msg = "the unabbreviated option '%canonical_option%' does not take any arguments"; + break; + case short_adjacent_not_allowed: + msg = "the abbreviated option '%canonical_option%' does not take any arguments"; + break; + case extra_parameter: + msg = "option '%canonical_option%' does not take any arguments"; + break; + default: + msg = "unknown command line syntax error for '%s'"; + } + return msg; + } + + +}} + + +namespace boost { namespace program_options { namespace detail { + + // vc6 needs this, but borland chokes when this is added. +#if BOOST_WORKAROUND(_MSC_VER, < 1300) + using namespace std; + using namespace program_options; +#endif + + + cmdline::cmdline(const vector<string>& args) + { + init(args); + } + + cmdline::cmdline(int argc, const char*const * argv) + { +#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS) + vector<string> args; + copy(argv+1, argv+argc+!argc, inserter(args, args.end())); + init(args); +#else + init(vector<string>(argv+1, argv+argc+!argc)); +#endif + } + + void + cmdline::init(const vector<string>& args) + { + this->m_args = args; + m_style = command_line_style::default_style; + m_desc = 0; + m_positional = 0; + m_allow_unregistered = false; + } + + void + cmdline::style(int style) + { + if (style == 0) + style = default_style; + + check_style(style); + this->m_style = style_t(style); + } + + void + cmdline::allow_unregistered() + { + this->m_allow_unregistered = true; + } + + void + cmdline::check_style(int style) const + { + bool allow_some_long = + (style & allow_long) || (style & allow_long_disguise); + + const char* error = 0; + if (allow_some_long && + !(style & long_allow_adjacent) && !(style & long_allow_next)) + error = "boost::program_options misconfiguration: " + "choose one or other of 'command_line_style::long_allow_next' " + "(whitespace separated arguments) or " + "'command_line_style::long_allow_adjacent' ('=' separated arguments) for " + "long options."; + + if (!error && (style & allow_short) && + !(style & short_allow_adjacent) && !(style & short_allow_next)) + error = "boost::program_options misconfiguration: " + "choose one or other of 'command_line_style::short_allow_next' " + "(whitespace separated arguments) or " + "'command_line_style::short_allow_adjacent' ('=' separated arguments) for " + "short options."; + + if (!error && (style & allow_short) && + !(style & allow_dash_for_short) && !(style & allow_slash_for_short)) + error = "boost::program_options misconfiguration: " + "choose one or other of 'command_line_style::allow_slash_for_short' " + "(slashes) or 'command_line_style::allow_dash_for_short' (dashes) for " + "short options."; + + if (error) + boost::throw_exception(invalid_command_line_style(error)); + + // Need to check that if guessing and long disguise are enabled + // -f will mean the same as -foo + } + + bool + cmdline::is_style_active(style_t style) const + { + return ((m_style & style) ? true : false); + } + + void + cmdline::set_options_description(const options_description& desc) + { + m_desc = &desc; + } + + void + cmdline::set_positional_options( + const positional_options_description& positional) + { + m_positional = &positional; + } + + int + cmdline::get_canonical_option_prefix() + { + if (m_style & allow_long) + return allow_long; + + if (m_style & allow_long_disguise) + return allow_long_disguise; + + if ((m_style & allow_short) && (m_style & allow_dash_for_short)) + return allow_dash_for_short; + + if ((m_style & allow_short) && (m_style & allow_slash_for_short)) + return allow_slash_for_short; + + return 0; + } + + vector<option> + cmdline::run() + { + // The parsing is done by having a set of 'style parsers' + // and trying then in order. Each parser is passed a vector + // of unparsed tokens and can consume some of them (by + // removing elements on front) and return a vector of options. + // + // We try each style parser in turn, untill some input + // is consumed. The returned vector of option may contain the + // result of just syntactic parsing of token, say --foo will + // be parsed as option with name 'foo', and the style parser + // is not required to care if that option is defined, and how + // many tokens the value may take. + // So, after vector is returned, we validate them. + assert(m_desc); + + vector<style_parser> style_parsers; + + if (m_style_parser) + style_parsers.push_back(m_style_parser); + + if (m_additional_parser) + style_parsers.push_back( + boost::bind(&cmdline::handle_additional_parser, this, _1)); + + if (m_style & allow_long) + style_parsers.push_back( + boost::bind(&cmdline::parse_long_option, this, _1)); + + if ((m_style & allow_long_disguise)) + style_parsers.push_back( + boost::bind(&cmdline::parse_disguised_long_option, this, _1)); + + if ((m_style & allow_short) && (m_style & allow_dash_for_short)) + style_parsers.push_back( + boost::bind(&cmdline::parse_short_option, this, _1)); + + if ((m_style & allow_short) && (m_style & allow_slash_for_short)) + style_parsers.push_back(boost::bind(&cmdline::parse_dos_option, this, _1)); + + style_parsers.push_back(boost::bind(&cmdline::parse_terminator, this, _1)); + + vector<option> result; + vector<string>& args = m_args; + while(!args.empty()) + { + bool ok = false; + for(unsigned i = 0; i < style_parsers.size(); ++i) + { + unsigned current_size = static_cast<unsigned>(args.size()); + vector<option> next = style_parsers[i](args); + + // Check that option names + // are valid, and that all values are in place. + if (!next.empty()) + { + vector<string> e; + for(unsigned k = 0; k < next.size()-1; ++k) { + finish_option(next[k], e, style_parsers); + } + // For the last option, pass the unparsed tokens + // so that they can be added to next.back()'s values + // if appropriate. + finish_option(next.back(), args, style_parsers); + for (unsigned j = 0; j < next.size(); ++j) + result.push_back(next[j]); + } + + if (args.size() != current_size) { + ok = true; + break; + } + } + + if (!ok) { + option opt; + opt.value.push_back(args[0]); + opt.original_tokens.push_back(args[0]); + result.push_back(opt); + args.erase(args.begin()); + } + } + + /* If an key option is followed by a positional option, + can can consume more tokens (e.g. it's multitoken option), + give those tokens to it. */ + vector<option> result2; + for (unsigned i = 0; i < result.size(); ++i) + { + result2.push_back(result[i]); + option& opt = result2.back(); + + if (opt.string_key.empty()) + continue; + + const option_description* xd; + try + { + xd = m_desc->find_nothrow(opt.string_key, + is_style_active(allow_guessing), + is_style_active(long_case_insensitive), + is_style_active(short_case_insensitive)); + } + catch(error_with_option_name& e) + { + // add context and rethrow + e.add_context(opt.string_key, opt.original_tokens[0], get_canonical_option_prefix()); + throw; + } + + if (!xd) + continue; + + unsigned min_tokens = xd->semantic()->min_tokens(); + unsigned max_tokens = xd->semantic()->max_tokens(); + if (min_tokens < max_tokens && opt.value.size() < max_tokens) + { + // This option may grab some more tokens. + // We only allow to grab tokens that are not already + // recognized as key options. + + int can_take_more = max_tokens - static_cast<int>(opt.value.size()); + unsigned j = i+1; + for (; can_take_more && j < result.size(); --can_take_more, ++j) + { + option& opt2 = result[j]; + if (!opt2.string_key.empty()) + break; + + if (opt2.position_key == INT_MAX) + { + // We use INT_MAX to mark positional options that + // were found after the '--' terminator and therefore + // should stay positional forever. + break; + } + + assert(opt2.value.size() == 1); + + opt.value.push_back(opt2.value[0]); + + assert(opt2.original_tokens.size() == 1); + + opt.original_tokens.push_back(opt2.original_tokens[0]); + } + i = j-1; + } + } + result.swap(result2); + + + // Assign position keys to positional options. + int position_key = 0; + for(unsigned i = 0; i < result.size(); ++i) { + if (result[i].string_key.empty()) + result[i].position_key = position_key++; + } + + if (m_positional) + { + unsigned position = 0; + for (unsigned i = 0; i < result.size(); ++i) { + option& opt = result[i]; + if (opt.position_key != -1) { + if (position >= m_positional->max_total_count()) + { + boost::throw_exception(too_many_positional_options_error()); + } + opt.string_key = m_positional->name_for_position(position); + ++position; + } + } + } + + // set case sensitive flag + for (unsigned i = 0; i < result.size(); ++i) { + if (result[i].string_key.size() > 2 || + (result[i].string_key.size() > 1 && result[i].string_key[0] != '-')) + { + // it is a long option + result[i].case_insensitive = is_style_active(long_case_insensitive); + } + else + { + // it is a short option + result[i].case_insensitive = is_style_active(short_case_insensitive); + } + } + + return result; + } + + void + cmdline::finish_option(option& opt, + vector<string>& other_tokens, + const vector<style_parser>& style_parsers) + { + if (opt.string_key.empty()) + return; + + // + // Be defensive: + // will have no original token if option created by handle_additional_parser() + std::string original_token_for_exceptions = opt.string_key; + if (opt.original_tokens.size()) + original_token_for_exceptions = opt.original_tokens[0]; + + try + { + // First check that the option is valid, and get its description. + const option_description* xd = m_desc->find_nothrow(opt.string_key, + is_style_active(allow_guessing), + is_style_active(long_case_insensitive), + is_style_active(short_case_insensitive)); + + if (!xd) + { + if (m_allow_unregistered) { + opt.unregistered = true; + return; + } else { + boost::throw_exception(unknown_option()); + } + } + const option_description& d = *xd; + + // Canonize the name + opt.string_key = d.key(opt.string_key); + + // We check that the min/max number of tokens for the option + // agrees with the number of tokens we have. The 'adjacent_value' + // (the value in --foo=1) counts as a separate token, and if present + // must be consumed. The following tokens on the command line may be + // left unconsumed. + unsigned min_tokens = d.semantic()->min_tokens(); + unsigned max_tokens = d.semantic()->max_tokens(); + + unsigned present_tokens = static_cast<unsigned>(opt.value.size() + other_tokens.size()); + + if (present_tokens >= min_tokens) + { + if (!opt.value.empty() && max_tokens == 0) + { + boost::throw_exception( + invalid_command_line_syntax(invalid_command_line_syntax::extra_parameter)); + } + + // Grab min_tokens values from other_tokens, but only if those tokens + // are not recognized as options themselves. + if (opt.value.size() <= min_tokens) + { + min_tokens -= static_cast<unsigned>(opt.value.size()); + } + else + { + min_tokens = 0; + } + + // Everything's OK, move the values to the result. + for(;!other_tokens.empty() && min_tokens--; ) + { + // check if extra parameter looks like a known option + // we use style parsers to check if it is syntactically an option, + // additionally we check if an option_description exists + vector<option> followed_option; + vector<string> next_token(1, other_tokens[0]); + for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i) + { + followed_option = style_parsers[i](next_token); + } + if (!followed_option.empty()) + { + original_token_for_exceptions = other_tokens[0]; + const option_description* od = m_desc->find_nothrow(other_tokens[0], + is_style_active(allow_guessing), + is_style_active(long_case_insensitive), + is_style_active(short_case_insensitive)); + if (od) + boost::throw_exception( + invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter)); + } + opt.value.push_back(other_tokens[0]); + opt.original_tokens.push_back(other_tokens[0]); + other_tokens.erase(other_tokens.begin()); + } + } + else + { + boost::throw_exception( + invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter)); + + } + } + // use only original token for unknown_option / ambiguous_option since by definition + // they are unrecognised / unparsable + catch(error_with_option_name& e) + { + // add context and rethrow + e.add_context(opt.string_key, original_token_for_exceptions, get_canonical_option_prefix()); + throw; + } + + } + + vector<option> + cmdline::parse_long_option(vector<string>& args) + { + vector<option> result; + const string& tok = args[0]; + if (tok.size() >= 3 && tok[0] == '-' && tok[1] == '-') + { + string name, adjacent; + + string::size_type p = tok.find('='); + if (p != tok.npos) + { + name = tok.substr(2, p-2); + adjacent = tok.substr(p+1); + if (adjacent.empty()) + boost::throw_exception( invalid_command_line_syntax( + invalid_command_line_syntax::empty_adjacent_parameter, + name, + name, + get_canonical_option_prefix()) ); + } + else + { + name = tok.substr(2); + } + option opt; + opt.string_key = name; + if (!adjacent.empty()) + opt.value.push_back(adjacent); + opt.original_tokens.push_back(tok); + result.push_back(opt); + args.erase(args.begin()); + } + return result; + } + + + vector<option> + cmdline::parse_short_option(vector<string>& args) + { + const string& tok = args[0]; + if (tok.size() >= 2 && tok[0] == '-' && tok[1] != '-') + { + vector<option> result; + + string name = tok.substr(0,2); + string adjacent = tok.substr(2); + + // Short options can be 'grouped', so that + // "-d -a" becomes "-da". Loop, processing one + // option at a time. We exit the loop when either + // we've processed all the token, or when the remainder + // of token is considered to be value, not further grouped + // option. + for(;;) { + const option_description* d; + try + { + + d = m_desc->find_nothrow(name, false, false, + is_style_active(short_case_insensitive)); + } + catch(error_with_option_name& e) + { + // add context and rethrow + e.add_context(name, name, get_canonical_option_prefix()); + throw; + } + + + // FIXME: check for 'allow_sticky'. + if (d && (m_style & allow_sticky) && + d->semantic()->max_tokens() == 0 && !adjacent.empty()) { + // 'adjacent' is in fact further option. + option opt; + opt.string_key = name; + result.push_back(opt); + + if (adjacent.empty()) + { + args.erase(args.begin()); + break; + } + + name = string("-") + adjacent[0]; + adjacent.erase(adjacent.begin()); + } else { + + option opt; + opt.string_key = name; + opt.original_tokens.push_back(tok); + if (!adjacent.empty()) + opt.value.push_back(adjacent); + result.push_back(opt); + args.erase(args.begin()); + break; + } + } + return result; + } + return vector<option>(); + } + + vector<option> + cmdline::parse_dos_option(vector<string>& args) + { + vector<option> result; + const string& tok = args[0]; + if (tok.size() >= 2 && tok[0] == '/') + { + string name = "-" + tok.substr(1,1); + string adjacent = tok.substr(2); + + option opt; + opt.string_key = name; + if (!adjacent.empty()) + opt.value.push_back(adjacent); + opt.original_tokens.push_back(tok); + result.push_back(opt); + args.erase(args.begin()); + } + return result; + } + + vector<option> + cmdline::parse_disguised_long_option(vector<string>& args) + { + const string& tok = args[0]; + if (tok.size() >= 2 && + ((tok[0] == '-' && tok[1] != '-') || + ((m_style & allow_slash_for_short) && tok[0] == '/'))) + { + try + { + if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1), + is_style_active(allow_guessing), + is_style_active(long_case_insensitive), + is_style_active(short_case_insensitive))) + { + args[0].insert(0, "-"); + if (args[0][1] == '/') + args[0][1] = '-'; + return parse_long_option(args); + } + } + catch(error_with_option_name& e) + { + // add context and rethrow + e.add_context(tok, tok, get_canonical_option_prefix()); + throw; + } + } + return vector<option>(); + } + + vector<option> + cmdline::parse_terminator(vector<string>& args) + { + vector<option> result; + const string& tok = args[0]; + if (tok == "--") + { + for(unsigned i = 1; i < args.size(); ++i) + { + option opt; + opt.value.push_back(args[i]); + opt.original_tokens.push_back(args[i]); + opt.position_key = INT_MAX; + result.push_back(opt); + } + args.clear(); + } + return result; + } + + vector<option> + cmdline::handle_additional_parser(vector<string>& args) + { + vector<option> result; + pair<string, string> r = m_additional_parser(args[0]); + if (!r.first.empty()) { + option next; + next.string_key = r.first; + if (!r.second.empty()) + next.value.push_back(r.second); + result.push_back(next); + args.erase(args.begin()); + } + return result; + } + + void + cmdline::set_additional_parser(additional_parser p) + { + m_additional_parser = p; + } + + void + cmdline::extra_style_parser(style_parser s) + { + m_style_parser = s; + } + + + +}}} diff --git a/src/boost/libs/program_options/src/config_file.cpp b/src/boost/libs/program_options/src/config_file.cpp new file mode 100644 index 00000000..f2a57b4b --- /dev/null +++ b/src/boost/libs/program_options/src/config_file.cpp @@ -0,0 +1,198 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> + +#include <boost/program_options/detail/config_file.hpp> +#include <boost/program_options/errors.hpp> +#include <boost/program_options/detail/convert.hpp> +#include <boost/throw_exception.hpp> + +#include <iostream> +#include <fstream> +#include <cassert> + +namespace boost { namespace program_options { namespace detail { + + using namespace std; + + common_config_file_iterator::common_config_file_iterator( + const std::set<std::string>& allowed_options, + bool allow_unregistered) + : allowed_options(allowed_options), + m_allow_unregistered(allow_unregistered) + { + for(std::set<std::string>::const_iterator i = allowed_options.begin(); + i != allowed_options.end(); + ++i) + { + add_option(i->c_str()); + } + } + + void + common_config_file_iterator::add_option(const char* name) + { + string s(name); + assert(!s.empty()); + if (*s.rbegin() == '*') { + s.resize(s.size()-1); + bool bad_prefixes(false); + // If 's' is a prefix of one of allowed suffix, then + // lower_bound will return that element. + // If some element is prefix of 's', then lower_bound will + // return the next element. + set<string>::iterator i = allowed_prefixes.lower_bound(s); + if (i != allowed_prefixes.end()) { + if (i->find(s) == 0) + bad_prefixes = true; + } + if (i != allowed_prefixes.begin()) { + --i; + if (s.find(*i) == 0) + bad_prefixes = true; + } + if (bad_prefixes) + boost::throw_exception(error("options '" + string(name) + "' and '" + + *i + "*' will both match the same " + "arguments from the configuration file")); + allowed_prefixes.insert(s); + } + } + + namespace { + string trim_ws(const string& s) + { + string::size_type n, n2; + n = s.find_first_not_of(" \t\r\n"); + if (n == string::npos) + return string(); + else { + n2 = s.find_last_not_of(" \t\r\n"); + return s.substr(n, n2-n+1); + } + } + } + + + void common_config_file_iterator::get() + { + string s; + string::size_type n; + bool found = false; + + while(this->getline(s)) { + + // strip '#' comments and whitespace + if ((n = s.find('#')) != string::npos) + s = s.substr(0, n); + s = trim_ws(s); + + if (!s.empty()) { + // Handle section name + if (*s.begin() == '[' && *s.rbegin() == ']') { + m_prefix = s.substr(1, s.size()-2); + if (*m_prefix.rbegin() != '.') + m_prefix += '.'; + } + else if ((n = s.find('=')) != string::npos) { + + string name = m_prefix + trim_ws(s.substr(0, n)); + string value = trim_ws(s.substr(n+1)); + + bool registered = allowed_option(name); + if (!registered && !m_allow_unregistered) + boost::throw_exception(unknown_option(name)); + + found = true; + this->value().string_key = name; + this->value().value.clear(); + this->value().value.push_back(value); + this->value().unregistered = !registered; + this->value().original_tokens.clear(); + this->value().original_tokens.push_back(name); + this->value().original_tokens.push_back(value); + break; + + } else { + boost::throw_exception(invalid_config_file_syntax(s, invalid_syntax::unrecognized_line)); + } + } + } + if (!found) + found_eof(); + } + + + bool + common_config_file_iterator::allowed_option(const std::string& s) const + { + set<string>::const_iterator i = allowed_options.find(s); + if (i != allowed_options.end()) + return true; + // If s is "pa" where "p" is allowed prefix then + // lower_bound should find the element after "p". + // This depends on 'allowed_prefixes' invariant. + i = allowed_prefixes.lower_bound(s); + if (i != allowed_prefixes.begin() && s.find(*--i) == 0) + return true; + return false; + } + +#if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303)) || \ + (defined(__sgi) && BOOST_WORKAROUND(_COMPILER_VERSION, BOOST_TESTED_AT(741))) + template<> + bool + basic_config_file_iterator<wchar_t>::getline(std::string& s) + { + std::wstring ws; + // On Comeau, using two-argument version causes + // call to some internal function with std::wstring, and '\n' + // (not L'\n') and compile can't resolve that call. + + if (std::getline(*is, ws, L'\n')) { + s = to_utf8(ws); + return true; + } else { + return false; + } + } +#endif + +}}} + +#if 0 +using boost::program_options::config_file; + +#include <sstream> +#include <cassert> + +int main() +{ + try { + stringstream s( + "a = 1\n" + "b = 2\n"); + + config_file cf(s); + cf.add_option("a"); + cf.add_option("b"); + + assert(++cf); + assert(cf.name() == "a"); + assert(cf.value() == "1"); + assert(++cf); + assert(cf.name() == "b"); + assert(cf.value() == "2"); + assert(!++cf); + } + catch(exception& e) + { + cout << e.what() << "\n"; + } +} +#endif diff --git a/src/boost/libs/program_options/src/convert.cpp b/src/boost/libs/program_options/src/convert.cpp new file mode 100644 index 00000000..9be759e2 --- /dev/null +++ b/src/boost/libs/program_options/src/convert.cpp @@ -0,0 +1,161 @@ +// Copyright Vladimir Prus 2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <fstream> +#include <locale.h> +#include <locale> +#include <iostream> +#include <string> +#include <locale> +#include <stdexcept> + +#include <boost/config.hpp> + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> +#include <boost/program_options/detail/convert.hpp> +#include <boost/program_options/detail/utf8_codecvt_facet.hpp> +#include <boost/throw_exception.hpp> + +#include <boost/bind.hpp> + +using namespace std; + +namespace boost { namespace detail { + + /* Internal function to actually perform conversion. + The logic in from_8_bit and to_8_bit function is exactly + the same, except that one calls 'in' method of codecvt and another + calls the 'out' method, and that syntax difference makes straightforward + template implementation impossible. + + This functions takes a 'fun' argument, which should have the same + parameters and return type and the in/out methods. The actual converting + function will pass functional objects created with boost::bind. + Experiments show that the performance loss is less than 10%. + */ + template<class ToChar, class FromChar, class Fun> + std::basic_string<ToChar> + convert(const std::basic_string<FromChar>& s, Fun fun) + + { + std::basic_string<ToChar> result; + + std::mbstate_t state = std::mbstate_t(); + + const FromChar* from = s.data(); + const FromChar* from_end = s.data() + s.size(); + // The interface of cvt is not really iterator-like, and it's + // not possible the tell the required output size without the conversion. + // All we can is convert data by pieces. + while(from != from_end) { + + // std::basic_string does not provide non-const pointers to the data, + // so converting directly into string is not possible. + ToChar buffer[32]; + + ToChar* to_next = buffer; + // Need variable because boost::bind doesn't work with rvalues. + ToChar* to_end = buffer + 32; + std::codecvt_base::result r = + fun(state, from, from_end, from, buffer, to_end, to_next); + + if (r == std::codecvt_base::error) + boost::throw_exception( + std::logic_error("character conversion failed")); + // 'partial' is not an error, it just means not all source + // characters were converted. However, we need to check that at + // least one new target character was produced. If not, it means + // the source data is incomplete, and since we don't have extra + // data to add to source, it's error. + if (to_next == buffer) + boost::throw_exception( + std::logic_error("character conversion failed")); + + // Add converted characters + result.append(buffer, to_next); + } + + return result; + } +}} + +namespace boost { + +#ifndef BOOST_NO_STD_WSTRING + BOOST_PROGRAM_OPTIONS_DECL std::wstring + from_8_bit(const std::string& s, + const std::codecvt<wchar_t, char, std::mbstate_t>& cvt) + { + return detail::convert<wchar_t>( + s, + boost::bind(&std::codecvt<wchar_t, char, mbstate_t>::in, + &cvt, + _1, _2, _3, _4, _5, _6, _7)); + } + + BOOST_PROGRAM_OPTIONS_DECL std::string + to_8_bit(const std::wstring& s, + const std::codecvt<wchar_t, char, std::mbstate_t>& cvt) + { + return detail::convert<char>( + s, + boost::bind(&codecvt<wchar_t, char, mbstate_t>::out, + &cvt, + _1, _2, _3, _4, _5, _6, _7)); + } + + + namespace { + boost::program_options::detail::utf8_codecvt_facet + utf8_facet; + } + + BOOST_PROGRAM_OPTIONS_DECL std::wstring + from_utf8(const std::string& s) + { + return from_8_bit(s, utf8_facet); + } + + BOOST_PROGRAM_OPTIONS_DECL std::string + to_utf8(const std::wstring& s) + { + return to_8_bit(s, utf8_facet); + } + + BOOST_PROGRAM_OPTIONS_DECL std::wstring + from_local_8_bit(const std::string& s) + { + typedef codecvt<wchar_t, char, mbstate_t> facet_type; + return from_8_bit(s, + BOOST_USE_FACET(facet_type, locale())); + } + + BOOST_PROGRAM_OPTIONS_DECL std::string + to_local_8_bit(const std::wstring& s) + { + typedef codecvt<wchar_t, char, mbstate_t> facet_type; + return to_8_bit(s, + BOOST_USE_FACET(facet_type, locale())); + } +#endif + + namespace program_options + { + BOOST_PROGRAM_OPTIONS_DECL std::string to_internal(const std::string& s) + { + return s; + } + +#ifndef BOOST_NO_STD_WSTRING + BOOST_PROGRAM_OPTIONS_DECL std::string to_internal(const std::wstring& s) + { + return to_utf8(s); + } +#endif + } + + +} diff --git a/src/boost/libs/program_options/src/options_description.cpp b/src/boost/libs/program_options/src/options_description.cpp new file mode 100644 index 00000000..dc0eae87 --- /dev/null +++ b/src/boost/libs/program_options/src/options_description.cpp @@ -0,0 +1,699 @@ +// Copyright Vladimir Prus 2002-2004. +// Copyright Bertolt Mildner 2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> +#include <boost/program_options/options_description.hpp> +// FIXME: this is only to get multiple_occurrences class +// should move that to a separate headers. +#include <boost/program_options/parsers.hpp> + + +#include <boost/lexical_cast.hpp> +#include <boost/tokenizer.hpp> +#include <boost/detail/workaround.hpp> +#include <boost/throw_exception.hpp> + +#include <cassert> +#include <climits> +#include <cstring> +#include <cstdarg> +#include <sstream> +#include <iterator> +using namespace std; + +namespace boost { namespace program_options { + + namespace { + + template< class charT > + std::basic_string< charT > tolower_(const std::basic_string< charT >& str) + { + std::basic_string< charT > result; + for (typename std::basic_string< charT >::size_type i = 0; i < str.size(); ++i) + { + result.append(1, static_cast< charT >(std::tolower(str[i]))); + } + return result; + } + + } // unnamed namespace + + + option_description::option_description() + { + } + + option_description:: + option_description(const char* names, + const value_semantic* s) + : m_value_semantic(s) + { + this->set_names(names); + } + + + option_description:: + option_description(const char* names, + const value_semantic* s, + const char* description) + : m_description(description), m_value_semantic(s) + { + this->set_names(names); + } + + option_description::~option_description() + { + } + + option_description::match_result + option_description::match(const std::string& option, + bool approx, + bool long_ignore_case, + bool short_ignore_case) const + { + match_result result = no_match; + std::string local_option = (long_ignore_case ? tolower_(option) : option); + + for(std::vector<std::string>::const_iterator it(m_long_names.begin()); it != m_long_names.end(); it++) + { + std::string local_long_name((long_ignore_case ? tolower_(*it) : *it)); + + if (!local_long_name.empty()) { + + + if ((result == no_match) && (*local_long_name.rbegin() == '*')) + { + // The name ends with '*'. Any specified name with the given + // prefix is OK. + if (local_option.find(local_long_name.substr(0, local_long_name.length()-1)) + == 0) + result = approximate_match; + } + + if (local_long_name == local_option) + { + result = full_match; + break; + } + else if (approx) + { + if (local_long_name.find(local_option) == 0) + { + result = approximate_match; + } + } + } + + } + + if (result != full_match) + { + std::string local_short_name(short_ignore_case ? tolower_(m_short_name) : m_short_name); + + if (local_short_name == local_option) + { + result = full_match; + } + } + + return result; + } + + const std::string& + option_description::key(const std::string& option) const + { + // We make the arbitrary choise of using the first long + // name as the key, regardless of anything else + if (!m_long_names.empty()) { + const std::string& first_long_name = *m_long_names.begin(); + if (first_long_name.find('*') != string::npos) + // The '*' character means we're long_name + // matches only part of the input. So, returning + // long name will remove some of the information, + // and we have to return the option as specified + // in the source. + return option; + else + return first_long_name; + } + else + return m_short_name; + } + + std::string + option_description::canonical_display_name(int prefix_style) const + { + // We prefer the first long name over any others + if (!m_long_names.empty()) + { + if (prefix_style == command_line_style::allow_long) + return "--" + *m_long_names.begin(); + if (prefix_style == command_line_style::allow_long_disguise) + return "-" + *m_long_names.begin(); + } + // sanity check: m_short_name[0] should be '-' or '/' + if (m_short_name.length() == 2) + { + if (prefix_style == command_line_style::allow_slash_for_short) + return string("/") + m_short_name[1]; + if (prefix_style == command_line_style::allow_dash_for_short) + return string("-") + m_short_name[1]; + } + if (!m_long_names.empty()) + return *m_long_names.begin(); + else + return m_short_name; + } + + + const std::string& + option_description::long_name() const + { + static std::string empty_string(""); + return m_long_names.empty() ? empty_string : *m_long_names.begin(); + } + + const std::pair<const std::string*, std::size_t> + option_description::long_names() const + { + // reinterpret_cast is to please msvc 10. + return (m_long_names.empty()) + ? std::pair<const std::string*, size_t>(reinterpret_cast<const std::string*>(0), 0 ) + : std::pair<const std::string*, size_t>( &(*m_long_names.begin()), m_long_names.size()); + } + + option_description& + option_description::set_names(const char* _names) + { + m_long_names.clear(); + std::istringstream iss(_names); + std::string name; + + while(std::getline(iss, name, ',')) { + m_long_names.push_back(name); + } + assert(!m_long_names.empty() && "No option names were specified"); + + bool try_interpreting_last_name_as_a_switch = m_long_names.size() > 1; + if (try_interpreting_last_name_as_a_switch) { + const std::string& last_name = *m_long_names.rbegin(); + if (last_name.length() == 1) { + m_short_name = '-' + last_name; + m_long_names.pop_back(); + // The following caters to the (valid) input of ",c" for some + // character c, where the caller only wants this option to have + // a short name. + if (m_long_names.size() == 1 && (*m_long_names.begin()).empty()) { + m_long_names.clear(); + } + } + } + // We could theoretically also ensure no remaining long names + // are empty, or that none of them have length 1 + return *this; + } + + const std::string& + option_description::description() const + { + return m_description; + } + + shared_ptr<const value_semantic> + option_description::semantic() const + { + return m_value_semantic; + } + + std::string + option_description::format_name() const + { + if (!m_short_name.empty()) + { + return m_long_names.empty() + ? m_short_name + : string(m_short_name).append(" [ --"). + append(*m_long_names.begin()).append(" ]"); + } + return string("--").append(*m_long_names.begin()); + } + + std::string + option_description::format_parameter() const + { + if (m_value_semantic->max_tokens() != 0) + return m_value_semantic->name(); + else + return ""; + } + + options_description_easy_init:: + options_description_easy_init(options_description* owner) + : owner(owner) + {} + + options_description_easy_init& + options_description_easy_init:: + operator()(const char* name, + const char* description) + { + // Create untypes semantic which accepts zero tokens: i.e. + // no value can be specified on command line. + // FIXME: does not look exception-safe + shared_ptr<option_description> d( + new option_description(name, new untyped_value(true), description)); + + owner->add(d); + return *this; + } + + options_description_easy_init& + options_description_easy_init:: + operator()(const char* name, + const value_semantic* s) + { + shared_ptr<option_description> d(new option_description(name, s)); + owner->add(d); + return *this; + } + + options_description_easy_init& + options_description_easy_init:: + operator()(const char* name, + const value_semantic* s, + const char* description) + { + shared_ptr<option_description> d(new option_description(name, s, description)); + + owner->add(d); + return *this; + } + + const unsigned options_description::m_default_line_length = 80; + + options_description::options_description(unsigned line_length, + unsigned min_description_length) + : m_line_length(line_length) + , m_min_description_length(min_description_length) + { + // we require a space between the option and description parts, so add 1. + assert(m_min_description_length < m_line_length - 1); + } + + options_description::options_description(const std::string& caption, + unsigned line_length, + unsigned min_description_length) + : m_caption(caption) + , m_line_length(line_length) + , m_min_description_length(min_description_length) + { + // we require a space between the option and description parts, so add 1. + assert(m_min_description_length < m_line_length - 1); + } + + void + options_description::add(shared_ptr<option_description> desc) + { + m_options.push_back(desc); + belong_to_group.push_back(false); + } + + options_description& + options_description::add(const options_description& desc) + { + shared_ptr<options_description> d(new options_description(desc)); + groups.push_back(d); + + for (size_t i = 0; i < desc.m_options.size(); ++i) { + add(desc.m_options[i]); + belong_to_group.back() = true; + } + + return *this; + } + + options_description_easy_init + options_description::add_options() + { + return options_description_easy_init(this); + } + + const option_description& + options_description::find(const std::string& name, + bool approx, + bool long_ignore_case, + bool short_ignore_case) const + { + const option_description* d = find_nothrow(name, approx, + long_ignore_case, short_ignore_case); + if (!d) + boost::throw_exception(unknown_option()); + return *d; + } + + const std::vector< shared_ptr<option_description> >& + options_description::options() const + { + return m_options; + } + + const option_description* + options_description::find_nothrow(const std::string& name, + bool approx, + bool long_ignore_case, + bool short_ignore_case) const + { + shared_ptr<option_description> found; + bool had_full_match = false; + vector<string> approximate_matches; + vector<string> full_matches; + + // We use linear search because matching specified option + // name with the declared option name need to take care about + // case sensitivity and trailing '*' and so we can't use simple map. + for(unsigned i = 0; i < m_options.size(); ++i) + { + option_description::match_result r = + m_options[i]->match(name, approx, long_ignore_case, short_ignore_case); + + if (r == option_description::no_match) + continue; + + if (r == option_description::full_match) + { + full_matches.push_back(m_options[i]->key(name)); + found = m_options[i]; + had_full_match = true; + } + else + { + // FIXME: the use of 'key' here might not + // be the best approach. + approximate_matches.push_back(m_options[i]->key(name)); + if (!had_full_match) + found = m_options[i]; + } + } + if (full_matches.size() > 1) + boost::throw_exception(ambiguous_option(full_matches)); + + // If we have a full match, and an approximate match, + // ignore approximate match instead of reporting error. + // Say, if we have options "all" and "all-chroots", then + // "--all" on the command line should select the first one, + // without ambiguity. + if (full_matches.empty() && approximate_matches.size() > 1) + boost::throw_exception(ambiguous_option(approximate_matches)); + + return found.get(); + } + + BOOST_PROGRAM_OPTIONS_DECL + std::ostream& operator<<(std::ostream& os, const options_description& desc) + { + desc.print(os); + return os; + } + + namespace { + + /* Given a string 'par', that contains no newline characters + outputs it to 'os' with wordwrapping, that is, as several + line. + + Each output line starts with 'indent' space characters, + following by characters from 'par'. The total length of + line is no longer than 'line_length'. + + */ + void format_paragraph(std::ostream& os, + std::string par, + unsigned indent, + unsigned line_length) + { + // Through reminder of this function, 'line_length' will + // be the length available for characters, not including + // indent. + assert(indent < line_length); + line_length -= indent; + + // index of tab (if present) is used as additional indent relative + // to first_column_width if paragrapth is spanned over multiple + // lines if tab is not on first line it is ignored + string::size_type par_indent = par.find('\t'); + + if (par_indent == string::npos) + { + par_indent = 0; + } + else + { + // only one tab per paragraph allowed + if (count(par.begin(), par.end(), '\t') > 1) + { + boost::throw_exception(program_options::error( + "Only one tab per paragraph is allowed in the options description")); + } + + // erase tab from string + par.erase(par_indent, 1); + + // this assert may fail due to user error or + // environment conditions! + assert(par_indent < line_length); + + // ignore tab if not on first line + if (par_indent >= line_length) + { + par_indent = 0; + } + } + + if (par.size() < line_length) + { + os << par; + } + else + { + string::const_iterator line_begin = par.begin(); + const string::const_iterator par_end = par.end(); + + bool first_line = true; // of current paragraph! + + while (line_begin < par_end) // paragraph lines + { + if (!first_line) + { + // If line starts with space, but second character + // is not space, remove the leading space. + // We don't remove double spaces because those + // might be intentianal. + if ((*line_begin == ' ') && + ((line_begin + 1 < par_end) && + (*(line_begin + 1) != ' '))) + { + line_begin += 1; // line_begin != line_end + } + } + + // Take care to never increment the iterator past + // the end, since MSVC 8.0 (brokenly), assumes that + // doing that, even if no access happens, is a bug. + unsigned remaining = static_cast<unsigned>(std::distance(line_begin, par_end)); + string::const_iterator line_end = line_begin + + ((remaining < line_length) ? remaining : line_length); + + // prevent chopped words + // Is line_end between two non-space characters? + if ((*(line_end - 1) != ' ') && + ((line_end < par_end) && (*line_end != ' '))) + { + // find last ' ' in the second half of the current paragraph line + string::const_iterator last_space = + find(reverse_iterator<string::const_iterator>(line_end), + reverse_iterator<string::const_iterator>(line_begin), + ' ') + .base(); + + if (last_space != line_begin) + { + // is last_space within the second half ot the + // current line + if (static_cast<unsigned>(std::distance(last_space, line_end)) < + (line_length / 2)) + { + line_end = last_space; + } + } + } // prevent chopped words + + // write line to stream + copy(line_begin, line_end, ostream_iterator<char>(os)); + + if (first_line) + { + indent += static_cast<unsigned>(par_indent); + line_length -= static_cast<unsigned>(par_indent); // there's less to work with now + first_line = false; + } + + // more lines to follow? + if (line_end != par_end) + { + os << '\n'; + + for(unsigned pad = indent; pad > 0; --pad) + { + os.put(' '); + } + } + + // next line starts after of this line + line_begin = line_end; + } // paragraph lines + } + } + + void format_description(std::ostream& os, + const std::string& desc, + unsigned first_column_width, + unsigned line_length) + { + // we need to use one char less per line to work correctly if actual + // console has longer lines + assert(line_length > 1); + if (line_length > 1) + { + --line_length; + } + + // line_length must be larger than first_column_width + // this assert may fail due to user error or environment conditions! + assert(line_length > first_column_width); + + // Note: can't use 'tokenizer' as name of typedef -- borland + // will consider uses of 'tokenizer' below as uses of + // boost::tokenizer, not typedef. + typedef boost::tokenizer<boost::char_separator<char> > tok; + + tok paragraphs( + desc, + char_separator<char>("\n", "", boost::keep_empty_tokens)); + + tok::const_iterator par_iter = paragraphs.begin(); + const tok::const_iterator par_end = paragraphs.end(); + + while (par_iter != par_end) // paragraphs + { + format_paragraph(os, *par_iter, first_column_width, + line_length); + + ++par_iter; + + // prepair next line if any + if (par_iter != par_end) + { + os << '\n'; + + for(unsigned pad = first_column_width; pad > 0; --pad) + { + os.put(' '); + } + } + } // paragraphs + } + + void format_one(std::ostream& os, const option_description& opt, + unsigned first_column_width, unsigned line_length) + { + stringstream ss; + ss << " " << opt.format_name() << ' ' << opt.format_parameter(); + + // Don't use ss.rdbuf() since g++ 2.96 is buggy on it. + os << ss.str(); + + if (!opt.description().empty()) + { + if (ss.str().size() >= first_column_width) + { + os.put('\n'); // first column is too long, lets put description in new line + for (unsigned pad = first_column_width; pad > 0; --pad) + { + os.put(' '); + } + } else { + for(unsigned pad = first_column_width - static_cast<unsigned>(ss.str().size()); pad > 0; --pad) + { + os.put(' '); + } + } + + format_description(os, opt.description(), + first_column_width, line_length); + } + } + } + + unsigned + options_description::get_option_column_width() const + { + /* Find the maximum width of the option column */ + unsigned width(23); + unsigned i; // vc6 has broken for loop scoping + for (i = 0; i < m_options.size(); ++i) + { + const option_description& opt = *m_options[i]; + stringstream ss; + ss << " " << opt.format_name() << ' ' << opt.format_parameter(); + width = (max)(width, static_cast<unsigned>(ss.str().size())); + } + + /* Get width of groups as well*/ + for (unsigned j = 0; j < groups.size(); ++j) + width = max(width, groups[j]->get_option_column_width()); + + /* this is the column were description should start, if first + column is longer, we go to a new line */ + const unsigned start_of_description_column = m_line_length - m_min_description_length; + + width = (min)(width, start_of_description_column-1); + + /* add an additional space to improve readability */ + ++width; + return width; + } + + void + options_description::print(std::ostream& os, unsigned width) const + { + if (!m_caption.empty()) + os << m_caption << ":\n"; + + if (!width) + width = get_option_column_width(); + + /* The options formatting style is stolen from Subversion. */ + for (unsigned i = 0; i < m_options.size(); ++i) + { + if (belong_to_group[i]) + continue; + + const option_description& opt = *m_options[i]; + + format_one(os, opt, width, m_line_length); + + os << "\n"; + } + + for (unsigned j = 0; j < groups.size(); ++j) { + os << "\n"; + groups[j]->print(os, width); + } + } + +}} diff --git a/src/boost/libs/program_options/src/parsers.cpp b/src/boost/libs/program_options/src/parsers.cpp new file mode 100644 index 00000000..3e435eea --- /dev/null +++ b/src/boost/libs/program_options/src/parsers.cpp @@ -0,0 +1,258 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/config.hpp> + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/positional_options.hpp> +#include <boost/program_options/detail/cmdline.hpp> +#include <boost/program_options/detail/config_file.hpp> +#include <boost/program_options/environment_iterator.hpp> +#include <boost/program_options/detail/convert.hpp> + +#include <boost/bind.hpp> +#include <boost/throw_exception.hpp> + +#include <cctype> +#include <fstream> + +#if !defined(__GNUC__) || __GNUC__ < 3 +#include <iostream> +#else +#include <istream> +#endif + +#ifdef _WIN32 +#include <stdlib.h> +#else +#include <unistd.h> +#endif + +// The 'environ' should be declared in some cases. E.g. Linux man page says: +// (This variable must be declared in the user program, but is declared in +// the header file unistd.h in case the header files came from libc4 or libc5, +// and in case they came from glibc and _GNU_SOURCE was defined.) +// To be safe, declare it here. + +// It appears that on Mac OS X the 'environ' variable is not +// available to dynamically linked libraries. +// See: http://article.gmane.org/gmane.comp.lib.boost.devel/103843 +// See: http://lists.gnu.org/archive/html/bug-guile/2004-01/msg00013.html +#if defined(__APPLE__) && defined(__DYNAMIC__) +// The proper include for this is crt_externs.h, however it's not +// available on iOS. The right replacement is not known. See +// https://svn.boost.org/trac/boost/ticket/5053 +extern "C" { extern char ***_NSGetEnviron(void); } +#define environ (*_NSGetEnviron()) +#else +#if defined(__MWERKS__) +#include <crtl.h> +#else +#if !defined(_WIN32) || defined(__COMO_VERSION__) +extern char** environ; +#endif +#endif +#endif + +using namespace std; + +namespace boost { namespace program_options { + +#ifndef BOOST_NO_STD_WSTRING + namespace { + woption woption_from_option(const option& opt) + { + woption result; + result.string_key = opt.string_key; + result.position_key = opt.position_key; + result.unregistered = opt.unregistered; + + std::transform(opt.value.begin(), opt.value.end(), + back_inserter(result.value), + boost::bind(from_utf8, _1)); + + std::transform(opt.original_tokens.begin(), + opt.original_tokens.end(), + back_inserter(result.original_tokens), + boost::bind(from_utf8, _1)); + return result; + } + } + + basic_parsed_options<wchar_t> + ::basic_parsed_options(const parsed_options& po) + : description(po.description), + utf8_encoded_options(po), + m_options_prefix(po.m_options_prefix) + { + for (unsigned i = 0; i < po.options.size(); ++i) + options.push_back(woption_from_option(po.options[i])); + } +#endif + + template<class charT> + basic_parsed_options<charT> + parse_config_file(std::basic_istream<charT>& is, + const options_description& desc, + bool allow_unregistered) + { + set<string> allowed_options; + + const vector<shared_ptr<option_description> >& options = desc.options(); + for (unsigned i = 0; i < options.size(); ++i) + { + const option_description& d = *options[i]; + + if (d.long_name().empty()) + boost::throw_exception( + error("abbreviated option names are not permitted in options configuration files")); + + allowed_options.insert(d.long_name()); + } + + // Parser return char strings + parsed_options result(&desc); + copy(detail::basic_config_file_iterator<charT>( + is, allowed_options, allow_unregistered), + detail::basic_config_file_iterator<charT>(), + back_inserter(result.options)); + // Convert char strings into desired type. + return basic_parsed_options<charT>(result); + } + + template + BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char> + parse_config_file(std::basic_istream<char>& is, + const options_description& desc, + bool allow_unregistered); + +#ifndef BOOST_NO_STD_WSTRING + template + BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t> + parse_config_file(std::basic_istream<wchar_t>& is, + const options_description& desc, + bool allow_unregistered); +#endif + + template<class charT> + basic_parsed_options<charT> + parse_config_file(const char* filename, + const options_description& desc, + bool allow_unregistered) + { + // Parser return char strings + std::basic_ifstream< charT > strm(filename); + if (!strm) + { + boost::throw_exception(reading_file(filename)); + } + + basic_parsed_options<charT> result + = parse_config_file(strm, desc, allow_unregistered); + + if (strm.bad()) + { + boost::throw_exception(reading_file(filename)); + } + + return result; + } + + template + BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char> + parse_config_file(const char* filename, + const options_description& desc, + bool allow_unregistered); + +#ifndef BOOST_NO_STD_WSTRING + template + BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t> + parse_config_file(const char* filename, + const options_description& desc, + bool allow_unregistered); +#endif + + +// This versio, which accepts any options without validation, is disabled, +// in the hope that nobody will need it and we cant drop it altogether. +// Besides, probably the right way to handle all options is the '*' name. +#if 0 + BOOST_PROGRAM_OPTIONS_DECL parsed_options + parse_config_file(std::istream& is) + { + detail::config_file_iterator cf(is, false); + parsed_options result(0); + copy(cf, detail::config_file_iterator(), + back_inserter(result.options)); + return result; + } +#endif + + BOOST_PROGRAM_OPTIONS_DECL parsed_options + parse_environment(const options_description& desc, + const function1<std::string, std::string>& name_mapper) + { + parsed_options result(&desc); + + for(environment_iterator i(environ), e; i != e; ++i) { + string option_name = name_mapper(i->first); + + if (!option_name.empty()) { + option n; + n.string_key = option_name; + n.value.push_back(i->second); + result.options.push_back(n); + } + } + + return result; + } + + namespace detail { + class prefix_name_mapper { + public: + prefix_name_mapper(const std::string& prefix) + : prefix(prefix) + {} + + std::string operator()(const std::string& s) + { + string result; + if (s.find(prefix) == 0) { + for(string::size_type n = prefix.size(); n < s.size(); ++n) + { + // Intel-Win-7.1 does not understand + // push_back on string. + result += static_cast<char>(tolower(s[n])); + } + } + return result; + } + private: + std::string prefix; + }; + } + + BOOST_PROGRAM_OPTIONS_DECL parsed_options + parse_environment(const options_description& desc, + const std::string& prefix) + { + return parse_environment(desc, detail::prefix_name_mapper(prefix)); + } + + BOOST_PROGRAM_OPTIONS_DECL parsed_options + parse_environment(const options_description& desc, const char* prefix) + { + return parse_environment(desc, string(prefix)); + } + + + + +}} diff --git a/src/boost/libs/program_options/src/positional_options.cpp b/src/boost/libs/program_options/src/positional_options.cpp new file mode 100644 index 00000000..72dc0d6b --- /dev/null +++ b/src/boost/libs/program_options/src/positional_options.cpp @@ -0,0 +1,53 @@ +// Copyright Vladimir Prus 2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> + +#include <boost/program_options/positional_options.hpp> + +#include <boost/limits.hpp> + +#include <cassert> + +namespace boost { namespace program_options { + + positional_options_description::positional_options_description() + {} + + positional_options_description& + positional_options_description::add(const char* name, int max_count) + { + assert(max_count != -1 || m_trailing.empty()); + + if (max_count == -1) + m_trailing = name; + else { + m_names.resize(m_names.size() + max_count, name); + } + return *this; + } + + unsigned + positional_options_description::max_total_count() const + { + return m_trailing.empty() ? + static_cast<unsigned>(m_names.size()) : (std::numeric_limits<unsigned>::max)(); + } + + const std::string& + positional_options_description::name_for_position(unsigned position) const + { + assert(position < max_total_count()); + + if (position < m_names.size()) + return m_names[position]; + else + return m_trailing; + } + + +}} + diff --git a/src/boost/libs/program_options/src/split.cpp b/src/boost/libs/program_options/src/split.cpp new file mode 100644 index 00000000..96da068b --- /dev/null +++ b/src/boost/libs/program_options/src/split.cpp @@ -0,0 +1,62 @@ +// Copyright Sascha Ochsenknecht 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_PROGRAM_OPTIONS_SOURCE + +#include <boost/program_options/parsers.hpp> +#include <boost/tokenizer.hpp> + +#include <string> +#include <vector> + +namespace boost { namespace program_options { namespace detail { + + template< class charT > + std::vector<std::basic_string<charT> > + split_unix( + const std::basic_string<charT>& cmdline, + const std::basic_string<charT>& seperator, + const std::basic_string<charT>& quote, + const std::basic_string<charT>& escape) + { + typedef boost::tokenizer< boost::escaped_list_separator<charT>, + typename std::basic_string<charT>::const_iterator, + std::basic_string<charT> > tokenizerT; + + tokenizerT tok(cmdline.begin(), cmdline.end(), + boost::escaped_list_separator< charT >(escape, seperator, quote)); + + std::vector< std::basic_string<charT> > result; + for (typename tokenizerT::iterator cur_token(tok.begin()), end_token(tok.end()); cur_token != end_token; ++cur_token) { + if (!cur_token->empty()) + result.push_back(*cur_token); + } + return result; + } + +}}} // namespace + +namespace boost { namespace program_options { + + // Take a command line string and splits in into tokens, according + // to the given collection of seperators chars. + BOOST_PROGRAM_OPTIONS_DECL std::vector<std::string> + split_unix(const std::string& cmdline, const std::string& seperator, + const std::string& quote, const std::string& escape) + { + return detail::split_unix< char >(cmdline, seperator, quote, escape); + } + +#ifndef BOOST_NO_STD_WSTRING + BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring> + split_unix(const std::wstring& cmdline, const std::wstring& seperator, + const std::wstring& quote, const std::wstring& escape) + { + return detail::split_unix< wchar_t >(cmdline, seperator, quote, escape); + } +#endif + +}} // namespace + diff --git a/src/boost/libs/program_options/src/utf8_codecvt_facet.cpp b/src/boost/libs/program_options/src/utf8_codecvt_facet.cpp new file mode 100644 index 00000000..2e4c532c --- /dev/null +++ b/src/boost/libs/program_options/src/utf8_codecvt_facet.cpp @@ -0,0 +1,21 @@ +// Copyright Vladimir Prus 2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> + +#define BOOST_UTF8_BEGIN_NAMESPACE \ + namespace boost { namespace program_options { namespace detail { + +#define BOOST_UTF8_END_NAMESPACE }}} +#define BOOST_UTF8_DECL BOOST_PROGRAM_OPTIONS_DECL + +#include <boost/detail/utf8_codecvt_facet.ipp> + + +#undef BOOST_UTF8_BEGIN_NAMESPACE +#undef BOOST_UTF8_END_NAMESPACE +#undef BOOST_UTF8_DECL + diff --git a/src/boost/libs/program_options/src/value_semantic.cpp b/src/boost/libs/program_options/src/value_semantic.cpp new file mode 100644 index 00000000..a7366d43 --- /dev/null +++ b/src/boost/libs/program_options/src/value_semantic.cpp @@ -0,0 +1,428 @@ +// Copyright Vladimir Prus 2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> +#include <boost/program_options/value_semantic.hpp> +#include <boost/program_options/detail/convert.hpp> +#include <boost/program_options/detail/cmdline.hpp> +#include <set> + +#include <cctype> + +namespace boost { namespace program_options { + + using namespace std; + + +#ifndef BOOST_NO_STD_WSTRING + namespace + { + std::string convert_value(const std::wstring& s) + { + try { + return to_local_8_bit(s); + } + catch(const std::exception&) { + return "<unrepresentable unicode string>"; + } + } + } +#endif + + void + value_semantic_codecvt_helper<char>:: + parse(boost::any& value_store, + const std::vector<std::string>& new_tokens, + bool utf8) const + { + if (utf8) { +#ifndef BOOST_NO_STD_WSTRING + // Need to convert to local encoding. + std::vector<string> local_tokens; + for (unsigned i = 0; i < new_tokens.size(); ++i) { + std::wstring w = from_utf8(new_tokens[i]); + local_tokens.push_back(to_local_8_bit(w)); + } + xparse(value_store, local_tokens); +#else + boost::throw_exception( + std::runtime_error("UTF-8 conversion not supported.")); +#endif + } else { + // Already in local encoding, pass unmodified + xparse(value_store, new_tokens); + } + } + +#ifndef BOOST_NO_STD_WSTRING + void + value_semantic_codecvt_helper<wchar_t>:: + parse(boost::any& value_store, + const std::vector<std::string>& new_tokens, + bool utf8) const + { + std::vector<wstring> tokens; + if (utf8) { + // Convert from utf8 + for (unsigned i = 0; i < new_tokens.size(); ++i) { + tokens.push_back(from_utf8(new_tokens[i])); + } + + } else { + // Convert from local encoding + for (unsigned i = 0; i < new_tokens.size(); ++i) { + tokens.push_back(from_local_8_bit(new_tokens[i])); + } + } + + xparse(value_store, tokens); + } +#endif + + BOOST_PROGRAM_OPTIONS_DECL std::string arg("arg"); + + std::string + untyped_value::name() const + { + return arg; + } + + unsigned + untyped_value::min_tokens() const + { + if (m_zero_tokens) + return 0; + else + return 1; + } + + unsigned + untyped_value::max_tokens() const + { + if (m_zero_tokens) + return 0; + else + return 1; + } + + + void + untyped_value::xparse(boost::any& value_store, + const std::vector<std::string>& new_tokens) const + { + if (!value_store.empty()) + boost::throw_exception( + multiple_occurrences()); + if (new_tokens.size() > 1) + boost::throw_exception(multiple_values()); + value_store = new_tokens.empty() ? std::string("") : new_tokens.front(); + } + + BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>* + bool_switch() + { + return bool_switch(0); + } + + BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>* + bool_switch(bool* v) + { + typed_value<bool>* r = new typed_value<bool>(v); + r->default_value(0); + r->zero_tokens(); + + return r; + } + + /* Validates bool value. + Any of "1", "true", "yes", "on" will be converted to "1".<br> + Any of "0", "false", "no", "off" will be converted to "0".<br> + Case is ignored. The 'xs' vector can either be empty, in which + case the value is 'true', or can contain explicit value. + */ + BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<string>& xs, + bool*, int) + { + check_first_occurrence(v); + string s(get_single_string(xs, true)); + + for (size_t i = 0; i < s.size(); ++i) + s[i] = char(tolower(s[i])); + + if (s.empty() || s == "on" || s == "yes" || s == "1" || s == "true") + v = any(true); + else if (s == "off" || s == "no" || s == "0" || s == "false") + v = any(false); + else + boost::throw_exception(invalid_bool_value(s)); + } + + // This is blatant copy-paste. However, templating this will cause a problem, + // since wstring can't be constructed/compared with char*. We'd need to + // create auxiliary 'widen' routine to convert from char* into + // needed string type, and that's more work. +#if !defined(BOOST_NO_STD_WSTRING) + BOOST_PROGRAM_OPTIONS_DECL + void validate(any& v, const vector<wstring>& xs, bool*, int) + { + check_first_occurrence(v); + wstring s(get_single_string(xs, true)); + + for (size_t i = 0; i < s.size(); ++i) + s[i] = wchar_t(tolower(s[i])); + + if (s.empty() || s == L"on" || s == L"yes" || s == L"1" || s == L"true") + v = any(true); + else if (s == L"off" || s == L"no" || s == L"0" || s == L"false") + v = any(false); + else + boost::throw_exception(invalid_bool_value(convert_value(s))); + } +#endif + BOOST_PROGRAM_OPTIONS_DECL + void validate(any& v, const vector<string>& xs, std::string*, int) + { + check_first_occurrence(v); + v = any(get_single_string(xs)); + } + +#if !defined(BOOST_NO_STD_WSTRING) + BOOST_PROGRAM_OPTIONS_DECL + void validate(any& v, const vector<wstring>& xs, std::string*, int) + { + check_first_occurrence(v); + v = any(get_single_string(xs)); + } +#endif + + namespace validators { + + BOOST_PROGRAM_OPTIONS_DECL + void check_first_occurrence(const boost::any& value) + { + if (!value.empty()) + boost::throw_exception( + multiple_occurrences()); + } + } + + + invalid_option_value:: + invalid_option_value(const std::string& bad_value) + : validation_error(validation_error::invalid_option_value) + { + set_substitute("value", bad_value); + } + +#ifndef BOOST_NO_STD_WSTRING + invalid_option_value:: + invalid_option_value(const std::wstring& bad_value) + : validation_error(validation_error::invalid_option_value) + { + set_substitute("value", convert_value(bad_value)); + } +#endif + + + + invalid_bool_value:: + invalid_bool_value(const std::string& bad_value) + : validation_error(validation_error::invalid_bool_value) + { + set_substitute("value", bad_value); + } + + + + + + + error_with_option_name::error_with_option_name( const std::string& template_, + const std::string& option_name, + const std::string& original_token, + int option_style) : + error(template_), + m_option_style(option_style), + m_error_template(template_) + { + // parameter | placeholder | value + // --------- | ----------- | ----- + set_substitute_default("canonical_option", "option '%canonical_option%'", "option"); + set_substitute_default("value", "argument ('%value%')", "argument"); + set_substitute_default("prefix", "%prefix%", ""); + m_substitutions["option"] = option_name; + m_substitutions["original_token"] = original_token; + } + + + const char* error_with_option_name::what() const throw() + { + // will substitute tokens each time what is run() + substitute_placeholders(m_error_template); + + return m_message.c_str(); + } + + void error_with_option_name::replace_token(const string& from, const string& to) const + { + for (;;) + { + std::size_t pos = m_message.find(from.c_str(), 0, from.length()); + // not found: all replaced + if (pos == std::string::npos) + return; + m_message.replace(pos, from.length(), to); + } + } + + string error_with_option_name::get_canonical_option_prefix() const + { + switch (m_option_style) + { + case command_line_style::allow_dash_for_short: + return "-"; + case command_line_style::allow_slash_for_short: + return "/"; + case command_line_style::allow_long_disguise: + return "-"; + case command_line_style::allow_long: + return "--"; + case 0: + return ""; + } + throw std::logic_error("error_with_option_name::m_option_style can only be " + "one of [0, allow_dash_for_short, allow_slash_for_short, " + "allow_long_disguise or allow_long]"); + } + + + string error_with_option_name::get_canonical_option_name() const + { + if (!m_substitutions.find("option")->second.length()) + return m_substitutions.find("original_token")->second; + + string original_token = strip_prefixes(m_substitutions.find("original_token")->second); + string option_name = strip_prefixes(m_substitutions.find("option")->second); + + // For long options, use option name + if (m_option_style == command_line_style::allow_long || + m_option_style == command_line_style::allow_long_disguise) + return get_canonical_option_prefix() + option_name; + + // For short options use first letter of original_token + if (m_option_style && original_token.length()) + return get_canonical_option_prefix() + original_token[0]; + + // no prefix + return option_name; + } + + + void error_with_option_name::substitute_placeholders(const string& error_template) const + { + m_message = error_template; + std::map<std::string, std::string> substitutions(m_substitutions); + substitutions["canonical_option"] = get_canonical_option_name(); + substitutions["prefix"] = get_canonical_option_prefix(); + + + // + // replace placeholder with defaults if values are missing + // + for (map<string, string_pair>::const_iterator iter = m_substitution_defaults.begin(); + iter != m_substitution_defaults.end(); ++iter) + { + // missing parameter: use default + if (substitutions.count(iter->first) == 0 || + substitutions[iter->first].length() == 0) + replace_token(iter->second.first, iter->second.second); + } + + + // + // replace placeholder with values + // placeholder are denoted by surrounding '%' + // + for (map<string, string>::iterator iter = substitutions.begin(); + iter != substitutions.end(); ++iter) + replace_token('%' + iter->first + '%', iter->second); + } + + + void ambiguous_option::substitute_placeholders(const string& original_error_template) const + { + // For short forms, all alternatives must be identical, by + // definition, to the specified option, so we don't need to + // display alternatives + if (m_option_style == command_line_style::allow_dash_for_short || + m_option_style == command_line_style::allow_slash_for_short) + { + error_with_option_name::substitute_placeholders(original_error_template); + return; + } + + + string error_template = original_error_template; + // remove duplicates using std::set + std::set<std::string> alternatives_set (m_alternatives.begin(), m_alternatives.end()); + std::vector<std::string> alternatives_vec (alternatives_set.begin(), alternatives_set.end()); + + error_template += " and matches "; + // Being very cautious: should be > 1 alternative! + if (alternatives_vec.size() > 1) + { + for (unsigned i = 0; i < alternatives_vec.size() - 1; ++i) + error_template += "'%prefix%" + alternatives_vec[i] + "', "; + error_template += "and "; + } + + // there is a programming error if multiple options have the same name... + if (m_alternatives.size() > 1 && alternatives_vec.size() == 1) + error_template += "different versions of "; + + error_template += "'%prefix%" + alternatives_vec.back() + "'"; + + + // use inherited logic + error_with_option_name::substitute_placeholders(error_template); + } + + + + + + + string + validation_error::get_template(kind_t kind) + { + // Initially, store the message in 'const char*' variable, + // to avoid conversion to std::string in all cases. + const char* msg; + switch(kind) + { + case invalid_bool_value: + msg = "the argument ('%value%') for option '%canonical_option%' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'"; + break; + case invalid_option_value: + msg = "the argument ('%value%') for option '%canonical_option%' is invalid"; + break; + case multiple_values_not_allowed: + msg = "option '%canonical_option%' only takes a single argument"; + break; + case at_least_one_value_required: + msg = "option '%canonical_option%' requires at least one argument"; + break; + // currently unused + case invalid_option: + msg = "option '%canonical_option%' is not valid"; + break; + default: + msg = "unknown error"; + } + return msg; + } + +}} diff --git a/src/boost/libs/program_options/src/variables_map.cpp b/src/boost/libs/program_options/src/variables_map.cpp new file mode 100644 index 00000000..bc85f7e3 --- /dev/null +++ b/src/boost/libs/program_options/src/variables_map.cpp @@ -0,0 +1,248 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/config.hpp> +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/value_semantic.hpp> +#include <boost/program_options/variables_map.hpp> + +#include <cassert> + +namespace boost { namespace program_options { + + using namespace std; + + // First, performs semantic actions for 'oa'. + // Then, stores in 'm' all options that are defined in 'desc'. + BOOST_PROGRAM_OPTIONS_DECL + void store(const parsed_options& options, variables_map& xm, + bool utf8) + { + // TODO: what if we have different definition + // for the same option name during different calls + // 'store'. + assert(options.description); + const options_description& desc = *options.description; + + // We need to access map's operator[], not the overriden version + // variables_map. Ehmm.. messy. + std::map<std::string, variable_value>& m = xm; + + std::set<std::string> new_final; + + // Declared once, to please Intel in VC++ mode; + unsigned i; + + // Declared here so can be used to provide context for exceptions + string option_name; + string original_token; + +#ifndef BOOST_NO_EXCEPTIONS + try +#endif + { + + // First, convert/store all given options + for (i = 0; i < options.options.size(); ++i) { + + option_name = options.options[i].string_key; + // Skip positional options without name + if (option_name.empty()) + continue; + + // Ignore unregistered option. The 'unregistered' + // field can be true only if user has explicitly asked + // to allow unregistered options. We can't store them + // to variables map (lacking any information about paring), + // so just ignore them. + if (options.options[i].unregistered) + continue; + + // If option has final value, skip this assignment + if (xm.m_final.count(option_name)) + continue; + + original_token = options.options[i].original_tokens.size() ? + options.options[i].original_tokens[0] : ""; + const option_description& d = desc.find(option_name, false, + false, false); + + variable_value& v = m[option_name]; + if (v.defaulted()) { + // Explicit assignment here erases defaulted value + v = variable_value(); + } + + d.semantic()->parse(v.value(), options.options[i].value, utf8); + + v.m_value_semantic = d.semantic(); + + // The option is not composing, and the value is explicitly + // provided. Ignore values of this option for subsequent + // calls to 'store'. We store this to a temporary set, + // so that several assignment inside *this* 'store' call + // are allowed. + if (!d.semantic()->is_composing()) + new_final.insert(option_name); + } + } +#ifndef BOOST_NO_EXCEPTIONS + catch(error_with_option_name& e) + { + // add context and rethrow + e.add_context(option_name, original_token, options.m_options_prefix); + throw; + } +#endif + xm.m_final.insert(new_final.begin(), new_final.end()); + + + + // Second, apply default values and store required options. + const vector<shared_ptr<option_description> >& all = desc.options(); + for(i = 0; i < all.size(); ++i) + { + const option_description& d = *all[i]; + string key = d.key(""); + // FIXME: this logic relies on knowledge of option_description + // internals. + // The 'key' is empty if options description contains '*'. + // In that + // case, default value makes no sense at all. + if (key.empty()) + { + continue; + } + if (m.count(key) == 0) { + + boost::any def; + if (d.semantic()->apply_default(def)) { + m[key] = variable_value(def, true); + m[key].m_value_semantic = d.semantic(); + } + } + + // add empty value if this is an required option + if (d.semantic()->is_required()) { + + // For option names specified in multiple ways, e.g. on the command line, + // config file etc, the following precedence rules apply: + // "--" > ("-" or "/") > "" + // Precedence is set conveniently by a single call to length() + string canonical_name = d.canonical_display_name(options.m_options_prefix); + if (canonical_name.length() > xm.m_required[key].length()) + xm.m_required[key] = canonical_name; + } + } + } + + BOOST_PROGRAM_OPTIONS_DECL + void store(const wparsed_options& options, variables_map& m) + { + store(options.utf8_encoded_options, m, true); + } + + BOOST_PROGRAM_OPTIONS_DECL + void notify(variables_map& vm) + { + vm.notify(); + } + + abstract_variables_map::abstract_variables_map() + : m_next(0) + {} + + abstract_variables_map:: + abstract_variables_map(const abstract_variables_map* next) + : m_next(next) + {} + + const variable_value& + abstract_variables_map::operator[](const std::string& name) const + { + const variable_value& v = get(name); + if (v.empty() && m_next) + return (*m_next)[name]; + else if (v.defaulted() && m_next) { + const variable_value& v2 = (*m_next)[name]; + if (!v2.empty() && !v2.defaulted()) + return v2; + else return v; + } else { + return v; + } + } + + void + abstract_variables_map::next(abstract_variables_map* next) + { + m_next = next; + } + + variables_map::variables_map() + {} + + variables_map::variables_map(const abstract_variables_map* next) + : abstract_variables_map(next) + {} + + void variables_map::clear() + { + std::map<std::string, variable_value>::clear(); + m_final.clear(); + m_required.clear(); + } + + const variable_value& + variables_map::get(const std::string& name) const + { + static variable_value empty; + const_iterator i = this->find(name); + if (i == this->end()) + return empty; + else + return i->second; + } + + void + variables_map::notify() + { + // This checks if all required options occur + for (map<string, string>::const_iterator r = m_required.begin(); + r != m_required.end(); + ++r) + { + const string& opt = r->first; + const string& display_opt = r->second; + map<string, variable_value>::const_iterator iter = find(opt); + if (iter == end() || iter->second.empty()) + { + boost::throw_exception(required_option(display_opt)); + + } + } + + // Lastly, run notify actions. + for (map<string, variable_value>::iterator k = begin(); + k != end(); + ++k) + { + /* Users might wish to use variables_map to store their own values + that are not parsed, and therefore will not have value_semantics + defined. Do not crash on such values. In multi-module programs, + one module might add custom values, and the 'notify' function + will be called after that, so we check that value_sematics is + not NULL. See: + https://svn.boost.org/trac/boost/ticket/2782 + */ + if (k->second.m_value_semantic) + k->second.m_value_semantic->notify(k->second.value()); + } + } + +}} diff --git a/src/boost/libs/program_options/src/winmain.cpp b/src/boost/libs/program_options/src/winmain.cpp new file mode 100644 index 00000000..6220043f --- /dev/null +++ b/src/boost/libs/program_options/src/winmain.cpp @@ -0,0 +1,102 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_PROGRAM_OPTIONS_SOURCE +#include <boost/program_options/parsers.hpp> +#include <cctype> + +using std::size_t; + +#ifdef _WIN32 +namespace boost { namespace program_options { + + // Take a command line string and splits in into tokens, according + // to the rules windows command line processor uses. + // + // The rules are pretty funny, see + // http://article.gmane.org/gmane.comp.lib.boost.user/3005 + // http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp + BOOST_PROGRAM_OPTIONS_DECL + std::vector<std::string> split_winmain(const std::string& input) + { + std::vector<std::string> result; + + std::string::const_iterator i = input.begin(), e = input.end(); + for(;i != e; ++i) + if (!isspace((unsigned char)*i)) + break; + + if (i != e) { + + std::string current; + bool inside_quoted = false; + bool empty_quote = false; + int backslash_count = 0; + + for(; i != e; ++i) { + if (*i == '"') { + // '"' preceded by even number (n) of backslashes generates + // n/2 backslashes and is a quoted block delimiter + if (backslash_count % 2 == 0) { + current.append(backslash_count / 2, '\\'); + empty_quote = inside_quoted && current.empty(); + inside_quoted = !inside_quoted; + // '"' preceded by odd number (n) of backslashes generates + // (n-1)/2 backslashes and is literal quote. + } else { + current.append(backslash_count / 2, '\\'); + current += '"'; + } + backslash_count = 0; + } else if (*i == '\\') { + ++backslash_count; + } else { + // Not quote or backslash. All accumulated backslashes should be + // added + if (backslash_count) { + current.append(backslash_count, '\\'); + backslash_count = 0; + } + if (isspace((unsigned char)*i) && !inside_quoted) { + // Space outside quoted section terminate the current argument + result.push_back(current); + current.resize(0); + empty_quote = false; + for(;i != e && isspace((unsigned char)*i); ++i) + ; + --i; + } else { + current += *i; + } + } + } + + // If we have trailing backslashes, add them + if (backslash_count) + current.append(backslash_count, '\\'); + + // If we have non-empty 'current' or we're still in quoted + // section (even if 'current' is empty), add the last token. + if (!current.empty() || inside_quoted || empty_quote) + result.push_back(current); + } + return result; + } + +#ifndef BOOST_NO_STD_WSTRING + BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring> + split_winmain(const std::wstring& cmdline) + { + std::vector<std::wstring> result; + std::vector<std::string> aux = split_winmain(to_internal(cmdline)); + for (size_t i = 0, e = aux.size(); i < e; ++i) + result.push_back(from_utf8(aux[i])); + return result; + } +#endif + +}} +#endif + diff --git a/src/boost/libs/program_options/test/Jamfile.v2 b/src/boost/libs/program_options/test/Jamfile.v2 new file mode 100644 index 00000000..a45ed8e6 --- /dev/null +++ b/src/boost/libs/program_options/test/Jamfile.v2 @@ -0,0 +1,44 @@ +import testing ; + +project + : requirements + <library>../build//boost_program_options + <link>static + <variant>debug + +# <define>_GLIBCXX_CONCEPT_CHECKS +# <define>_GLIBCXX_DEBUG + ; + +rule po-test ( source : input-file ? ) +{ + return + [ run $(source) : : $(input-file) ] + [ run $(source) : : $(input-file) + : <link>shared <define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 + : $(source:B)_dll ] + ; +} + +test-suite program_options : + + [ po-test options_description_test.cpp ] + [ po-test parsers_test.cpp : config_test.cfg ] + [ po-test variable_map_test.cpp ] + [ po-test cmdline_test.cpp ] + [ po-test positional_options_test.cpp ] + [ po-test unicode_test.cpp ] + [ po-test winmain.cpp ] + [ po-test exception_test.cpp ] + [ po-test split_test.cpp ] + [ po-test unrecognized_test.cpp ] + [ po-test required_test.cpp : required_test.cfg ] + [ po-test exception_txt_test.cpp ] + [ po-test optional_test.cpp ] + [ run options_description_test.cpp : : : <rtti>off <define>BOOST_NO_RTTI <define>BOOST_NO_TYPEID : options_description_no_rtti_test ] + ; + +exe test_convert : test_convert.cpp ; + +# `quick` target (for CI) +run quick.cpp : --path=initial ; diff --git a/src/boost/libs/program_options/test/cmdline_test.cpp b/src/boost/libs/program_options/test/cmdline_test.cpp new file mode 100644 index 00000000..1fc0af83 --- /dev/null +++ b/src/boost/libs/program_options/test/cmdline_test.cpp @@ -0,0 +1,656 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/program_options/cmdline.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/detail/cmdline.hpp> +using namespace boost::program_options; +using boost::program_options::detail::cmdline; + +#include <iostream> +#include <sstream> +#include <vector> +#include <cassert> +using namespace std; + +#include "minitest.hpp" + +/* To facilitate testing, declare a number of error codes. Otherwise, + we'd have to specify the type of exception that should be thrown. +*/ + +const int s_success = 0; +const int s_unknown_option = 1; +const int s_ambiguous_option = 2; +const int s_long_not_allowed = 3; +const int s_long_adjacent_not_allowed = 4; +const int s_short_adjacent_not_allowed = 5; +const int s_empty_adjacent_parameter = 6; +const int s_missing_parameter = 7; +const int s_extra_parameter = 8; +const int s_unrecognized_line = 9; + +int translate_syntax_error_kind(invalid_command_line_syntax::kind_t k) +{ + invalid_command_line_syntax::kind_t table[] = { + invalid_command_line_syntax::long_not_allowed, + invalid_command_line_syntax::long_adjacent_not_allowed, + invalid_command_line_syntax::short_adjacent_not_allowed, + invalid_command_line_syntax::empty_adjacent_parameter, + invalid_command_line_syntax::missing_parameter, + invalid_command_line_syntax::extra_parameter, + invalid_command_line_syntax::unrecognized_line + }; + invalid_command_line_syntax::kind_t *b, *e, *i; + b = table; + e = table + sizeof(table)/sizeof(table[0]); + i = std::find(b, e, k); + assert(i != e); + return std::distance(b, i) + 3; +} + +struct test_case { + const char* input; + int expected_status; + const char* expected_result; +}; + + +/* Parses the syntax description in 'syntax' and initialized + 'cmd' accordingly' + The "boost::program_options" in parameter type is needed because CW9 + has std::detail and it causes an ambiguity. +*/ +void apply_syntax(options_description& desc, + const char* syntax) +{ + + string s; + stringstream ss; + ss << syntax; + while(ss >> s) { + value_semantic* v = 0; + + if (*(s.end()-1) == '=') { + v = value<string>(); + s.resize(s.size()-1); + } else if (*(s.end()-1) == '?') { + v = value<string>()->implicit_value("default"); + s.resize(s.size()-1); + } else if (*(s.end()-1) == '*') { + v = value<vector<string> >()->multitoken(); + s.resize(s.size()-1); + } else if (*(s.end()-1) == '+') { + v = value<vector<string> >()->multitoken(); + s.resize(s.size()-1); + } + if (v) { + desc.add_options() + (s.c_str(), v, ""); + } else { + desc.add_options() + (s.c_str(), ""); + } + } +} + +void test_cmdline(const char* syntax, + command_line_style::style_t style, + const test_case* cases) +{ + for (int i = 0; cases[i].input; ++i) { + // Parse input + vector<string> xinput; + { + string s; + stringstream ss; + ss << cases[i].input; + while (ss >> s) { + xinput.push_back(s); + } + } + options_description desc; + apply_syntax(desc, syntax); + + cmdline cmd(xinput); + cmd.style(style); + cmd.set_options_description(desc); + + + string result; + int status = 0; + + try { + vector<option> options = cmd.run(); + + for(unsigned j = 0; j < options.size(); ++j) + { + option opt = options[j]; + + if (opt.position_key != -1) { + if (!result.empty()) + result += " "; + result += opt.value[0]; + } else { + if (!result.empty()) + result += " "; + result += opt.string_key + ":"; + for (size_t k = 0; k < opt.value.size(); ++k) { + if (k != 0) + result += "-"; + result += opt.value[k]; + } + } + } + } + catch(unknown_option&) { + status = s_unknown_option; + } + catch(ambiguous_option&) { + status = s_ambiguous_option; + } + catch(invalid_command_line_syntax& e) { + status = translate_syntax_error_kind(e.kind()); + } + BOOST_CHECK_EQUAL(status, cases[i].expected_status); + BOOST_CHECK_EQUAL(result, cases[i].expected_result); + } +} + +void test_long_options() +{ + using namespace command_line_style; + cmdline::style_t style = cmdline::style_t( + allow_long | long_allow_adjacent); + + test_case test_cases1[] = { + // Test that long options are recognized and everything else + // is treated like arguments + {"--foo foo -123 /asd", s_success, "foo: foo -123 /asd"}, + + // Unknown option + {"--unk", s_unknown_option, ""}, + + // Test that abbreviated names do not work + {"--fo", s_unknown_option, ""}, + + // Test for disallowed parameter + {"--foo=13", s_extra_parameter, ""}, + + // Test option with required parameter + {"--bar=", s_empty_adjacent_parameter, ""}, + {"--bar", s_missing_parameter, ""}, + + {"--bar=123", s_success, "bar:123"}, + {0, 0, 0} + }; + test_cmdline("foo bar=", style, test_cases1); + + + style = cmdline::style_t( + allow_long | long_allow_next); + + test_case test_cases2[] = { + {"--bar 10", s_success, "bar:10"}, + {"--bar", s_missing_parameter, ""}, + // Since --bar accepts a parameter, --foo is + // considered a value, even though it looks like + // an option. + {"--bar --foo", s_success, "bar:--foo"}, + {0, 0, 0} + }; + test_cmdline("foo bar=", style, test_cases2); + style = cmdline::style_t( + allow_long | long_allow_adjacent + | long_allow_next); + + test_case test_cases3[] = { + {"--bar=10", s_success, "bar:10"}, + {"--bar 11", s_success, "bar:11"}, + {0, 0, 0} + }; + test_cmdline("foo bar=", style, test_cases3); + + style = cmdline::style_t( + allow_long | long_allow_adjacent + | long_allow_next | case_insensitive); + + // Test case insensitive style. + // Note that option names are normalized to lower case. + test_case test_cases4[] = { + {"--foo", s_success, "foo:"}, + {"--Foo", s_success, "foo:"}, + {"--bar=Ab", s_success, "bar:Ab"}, + {"--Bar=ab", s_success, "bar:ab"}, + {"--giz", s_success, "Giz:"}, + {0, 0, 0} + }; + test_cmdline("foo bar= baz? Giz", style, test_cases4); +} + +void test_short_options() +{ + using namespace command_line_style; + cmdline::style_t style; + + style = cmdline::style_t( + allow_short | allow_dash_for_short + | short_allow_adjacent); + + test_case test_cases1[] = { + {"-d d /bar", s_success, "-d: d /bar"}, + // This is treated as error when long options are disabled + {"--foo", s_success, "--foo"}, + {"-d13", s_extra_parameter, ""}, + {"-f14", s_success, "-f:14"}, + {"-g -f1", s_success, "-g: -f:1"}, + {"-f", s_missing_parameter, ""}, + {0, 0, 0} + }; + test_cmdline(",d ,f= ,g", style, test_cases1); + + style = cmdline::style_t( + allow_short | allow_dash_for_short + | short_allow_next); + + test_case test_cases2[] = { + {"-f 13", s_success, "-f:13"}, + {"-f -13", s_success, "-f:-13"}, + {"-f", s_missing_parameter, ""}, + {"-f /foo", s_success, "-f:/foo"}, + {"-f -d", s_missing_parameter, ""}, + {0, 0, 0} + }; + test_cmdline(",d ,f=", style, test_cases2); + + style = cmdline::style_t( + allow_short | short_allow_next + | allow_dash_for_short | short_allow_adjacent); + + test_case test_cases3[] = { + {"-f10", s_success, "-f:10"}, + {"-f 10", s_success, "-f:10"}, + {"-f -d", s_missing_parameter, ""}, + {0, 0, 0} + }; + test_cmdline(",d ,f=", style, test_cases3); + + style = cmdline::style_t( + allow_short | short_allow_next + | allow_dash_for_short + | short_allow_adjacent | allow_sticky); + + test_case test_cases4[] = { + {"-de", s_success, "-d: -e:"}, + {"-df10", s_success, "-d: -f:10"}, + // FIXME: review + //{"-d12", s_extra_parameter, ""}, + {"-f12", s_success, "-f:12"}, + {"-fe", s_success, "-f:e"}, + {0, 0, 0} + }; + test_cmdline(",d ,f= ,e", style, test_cases4); + +} + + +void test_dos_options() +{ + using namespace command_line_style; + cmdline::style_t style; + + style = cmdline::style_t( + allow_short + | allow_slash_for_short | short_allow_adjacent); + + test_case test_cases1[] = { + {"/d d -bar", s_success, "-d: d -bar"}, + {"--foo", s_success, "--foo"}, + {"/d13", s_extra_parameter, ""}, + {"/f14", s_success, "-f:14"}, + {"/f", s_missing_parameter, ""}, + {0, 0, 0} + }; + test_cmdline(",d ,f=", style, test_cases1); + + style = cmdline::style_t( + allow_short + | allow_slash_for_short | short_allow_next + | short_allow_adjacent | allow_sticky); + + test_case test_cases2[] = { + {"/de", s_extra_parameter, ""}, + {"/fe", s_success, "-f:e"}, + {0, 0, 0} + }; + test_cmdline(",d ,f= ,e", style, test_cases2); + +} + + +void test_disguised_long() +{ + using namespace command_line_style; + cmdline::style_t style; + + style = cmdline::style_t( + allow_short | short_allow_adjacent + | allow_dash_for_short + | short_allow_next | allow_long_disguise + | long_allow_adjacent); + + test_case test_cases1[] = { + {"-foo -f", s_success, "foo: foo:"}, + {"-goo=x -gy", s_success, "goo:x goo:y"}, + {"-bee=x -by", s_success, "bee:x bee:y"}, + {0, 0, 0} + }; + test_cmdline("foo,f goo,g= bee,b?", style, test_cases1); + + style = cmdline::style_t(style | allow_slash_for_short); + test_case test_cases2[] = { + {"/foo -f", s_success, "foo: foo:"}, + {"/goo=x", s_success, "goo:x"}, + {0, 0, 0} + }; + test_cmdline("foo,f goo,g= bee,b?", style, test_cases2); +} + +void test_guessing() +{ + using namespace command_line_style; + cmdline::style_t style; + + style = cmdline::style_t( + allow_short | short_allow_adjacent + | allow_dash_for_short + | allow_long | long_allow_adjacent + | allow_guessing | allow_long_disguise); + + test_case test_cases1[] = { + {"--opt1", s_success, "opt123:"}, + {"--opt", s_ambiguous_option, ""}, + {"--f=1", s_success, "foo:1"}, + {"-far", s_success, "foo:ar"}, + {0, 0, 0} + }; + test_cmdline("opt123 opt56 foo,f=", style, test_cases1); + + test_case test_cases2[] = { + {"--fname file --fname2 file2", s_success, "fname: file fname2: file2"}, + {"--fnam file --fnam file2", s_ambiguous_option, ""}, + {"--fnam file --fname2 file2", s_ambiguous_option, ""}, + {"--fname2 file2 --fnam file", s_ambiguous_option, ""}, + {0, 0, 0} + }; + test_cmdline("fname fname2", style, test_cases2); +} + +void test_arguments() +{ + using namespace command_line_style; + cmdline::style_t style; + + style = cmdline::style_t( + allow_short | allow_long + | allow_dash_for_short + | short_allow_adjacent | long_allow_adjacent); + + test_case test_cases1[] = { + {"-f file -gx file2", s_success, "-f: file -g:x file2"}, + {"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"}, + {0, 0, 0} + }; + test_cmdline(",f ,g= ,e", style, test_cases1); + + // "--" should stop options regardless of whether long options are + // allowed or not. + + style = cmdline::style_t( + allow_short | short_allow_adjacent + | allow_dash_for_short); + + test_case test_cases2[] = { + {"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"}, + {0, 0, 0} + }; + test_cmdline(",f ,g= ,e", style, test_cases2); +} + +void test_prefix() +{ + using namespace command_line_style; + cmdline::style_t style; + + style = cmdline::style_t( + allow_short | allow_long + | allow_dash_for_short + | short_allow_adjacent | long_allow_adjacent + ); + + test_case test_cases1[] = { + {"--foo.bar=12", s_success, "foo.bar:12"}, + {0, 0, 0} + }; + + test_cmdline("foo*=", style, test_cases1); +} + + +pair<string, string> at_option_parser(string const&s) +{ + if ('@' == s[0]) + return std::make_pair(string("response-file"), s.substr(1)); + else + return pair<string, string>(); +} + +pair<string, string> at_option_parser_broken(string const&s) +{ + if ('@' == s[0]) + return std::make_pair(string("some garbage"), s.substr(1)); + else + return pair<string, string>(); +} + + + +void test_additional_parser() +{ + options_description desc; + desc.add_options() + ("response-file", value<string>(), "response file") + ("foo", value<int>(), "foo") + ("bar,baz", value<int>(), "bar") + ; + + vector<string> input; + input.push_back("@config"); + input.push_back("--foo=1"); + input.push_back("--baz=11"); + + cmdline cmd(input); + cmd.set_options_description(desc); + cmd.set_additional_parser(at_option_parser); + + vector<option> result = cmd.run(); + + BOOST_REQUIRE(result.size() == 3); + BOOST_CHECK_EQUAL(result[0].string_key, "response-file"); + BOOST_CHECK_EQUAL(result[0].value[0], "config"); + BOOST_CHECK_EQUAL(result[1].string_key, "foo"); + BOOST_CHECK_EQUAL(result[1].value[0], "1"); + BOOST_CHECK_EQUAL(result[2].string_key, "bar"); + BOOST_CHECK_EQUAL(result[2].value[0], "11"); + + // Test that invalid options returned by additional style + // parser are detected. + cmdline cmd2(input); + cmd2.set_options_description(desc); + cmd2.set_additional_parser(at_option_parser_broken); + + BOOST_CHECK_THROW(cmd2.run(), unknown_option); + +} + +vector<option> at_option_parser2(vector<string>& args) +{ + vector<option> result; + if ('@' == args[0][0]) { + // Simulate reading the response file. + result.push_back(option("foo", vector<string>(1, "1"))); + result.push_back(option("bar", vector<string>(1, "1"))); + args.erase(args.begin()); + } + return result; +} + + +void test_style_parser() +{ + options_description desc; + desc.add_options() + ("foo", value<int>(), "foo") + ("bar", value<int>(), "bar") + ; + + vector<string> input; + input.push_back("@config"); + + cmdline cmd(input); + cmd.set_options_description(desc); + cmd.extra_style_parser(at_option_parser2); + + vector<option> result = cmd.run(); + + BOOST_REQUIRE(result.size() == 2); + BOOST_CHECK_EQUAL(result[0].string_key, "foo"); + BOOST_CHECK_EQUAL(result[0].value[0], "1"); + BOOST_CHECK_EQUAL(result[1].string_key, "bar"); + BOOST_CHECK_EQUAL(result[1].value[0], "1"); +} + +void test_unregistered() +{ + // Check unregisted option when no options are registed at all. + options_description desc; + + vector<string> input; + input.push_back("--foo=1"); + input.push_back("--bar"); + input.push_back("1"); + input.push_back("-b"); + input.push_back("-biz"); + + cmdline cmd(input); + cmd.set_options_description(desc); + cmd.allow_unregistered(); + + vector<option> result = cmd.run(); + BOOST_REQUIRE(result.size() == 5); + // --foo=1 + BOOST_CHECK_EQUAL(result[0].string_key, "foo"); + BOOST_CHECK_EQUAL(result[0].unregistered, true); + BOOST_CHECK_EQUAL(result[0].value[0], "1"); + // --bar + BOOST_CHECK_EQUAL(result[1].string_key, "bar"); + BOOST_CHECK_EQUAL(result[1].unregistered, true); + BOOST_CHECK(result[1].value.empty()); + // '1' is considered a positional option, not a value to + // --bar + BOOST_CHECK(result[2].string_key.empty()); + BOOST_CHECK(result[2].position_key == 0); + BOOST_CHECK_EQUAL(result[2].unregistered, false); + BOOST_CHECK_EQUAL(result[2].value[0], "1"); + // -b + BOOST_CHECK_EQUAL(result[3].string_key, "-b"); + BOOST_CHECK_EQUAL(result[3].unregistered, true); + BOOST_CHECK(result[3].value.empty()); + // -biz + BOOST_CHECK_EQUAL(result[4].string_key, "-b"); + BOOST_CHECK_EQUAL(result[4].unregistered, true); + BOOST_CHECK_EQUAL(result[4].value[0], "iz"); + + // Check sticky short options together with unregisted options. + + desc.add_options() + ("help,h", "") + ("magic,m", value<string>(), "") + ; + + input.clear(); + input.push_back("-hc"); + input.push_back("-mc"); + + + cmdline cmd2(input); + cmd2.set_options_description(desc); + cmd2.allow_unregistered(); + + result = cmd2.run(); + + BOOST_REQUIRE(result.size() == 3); + BOOST_CHECK_EQUAL(result[0].string_key, "help"); + BOOST_CHECK_EQUAL(result[0].unregistered, false); + BOOST_CHECK(result[0].value.empty()); + BOOST_CHECK_EQUAL(result[1].string_key, "-c"); + BOOST_CHECK_EQUAL(result[1].unregistered, true); + BOOST_CHECK(result[1].value.empty()); + BOOST_CHECK_EQUAL(result[2].string_key, "magic"); + BOOST_CHECK_EQUAL(result[2].unregistered, false); + BOOST_CHECK_EQUAL(result[2].value[0], "c"); + + // CONSIDER: + // There's a corner case: + // -foo + // when 'allow_long_disguise' is set. Should this be considered + // disguised long option 'foo' or short option '-f' with value 'oo'? + // It's not clear yet, so I'm leaving the decision till later. +} + +void test_implicit_value() +{ + using namespace command_line_style; + cmdline::style_t style; + + style = cmdline::style_t( + allow_long | long_allow_adjacent + ); + + test_case test_cases1[] = { + // 'bar' does not even look like option, so is consumed + {"--foo bar", s_success, "foo:bar"}, + // '--bar' looks like option, and such option exists, so we don't consume this token + {"--foo --bar", s_success, "foo: bar:"}, + // '--biz' looks like option, but does not match any existing one. + // Presently this results in parse error, since + // (1) in cmdline.cpp:finish_option, we only consume following tokens if they are + // requires + // (2) in cmdline.cpp:run, we let options consume following positional options + // For --biz, an exception is thrown between 1 and 2. + // We might want to fix that in future. + {"--foo --biz", s_unknown_option, ""}, + {0, 0, 0} + }; + + test_cmdline("foo? bar?", style, test_cases1); +} + +int main(int /*ac*/, char** /*av*/) +{ + test_long_options(); + test_short_options(); + test_dos_options(); + test_disguised_long(); + test_guessing(); + test_arguments(); + test_prefix(); + test_additional_parser(); + test_style_parser(); + test_unregistered(); + test_implicit_value(); + + return 0; +} diff --git a/src/boost/libs/program_options/test/config_test.cfg b/src/boost/libs/program_options/test/config_test.cfg new file mode 100644 index 00000000..1530f7c8 --- /dev/null +++ b/src/boost/libs/program_options/test/config_test.cfg @@ -0,0 +1,9 @@ +gv1 = 0#asd +empty_value = +plug3 = 7 +b = true + +[m1] +v1 = 1 +v2 = 2 +v3 = 3 diff --git a/src/boost/libs/program_options/test/exception_test.cpp b/src/boost/libs/program_options/test/exception_test.cpp new file mode 100644 index 00000000..438f68db --- /dev/null +++ b/src/boost/libs/program_options/test/exception_test.cpp @@ -0,0 +1,264 @@ +// Copyright Sascha Ochsenknecht 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/program_options/cmdline.hpp> +using namespace boost::program_options; + +#include <iostream> +#include <sstream> +#include <vector> +#include <cassert> +using namespace std; + +#include "minitest.hpp" + + +void test_ambiguous() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>()->multitoken(), "the config file") + ("output,c", value<string>(), "the output file") + ("output,o", value<string>(), "the output file") + ; + + const char* cmdline[] = {"program", "-c", "file", "-o", "anotherfile"}; + + variables_map vm; + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + } + catch (ambiguous_option& e) + { + BOOST_CHECK_EQUAL(e.alternatives().size(), 2); + BOOST_CHECK_EQUAL(e.get_option_name(), "-c"); + BOOST_CHECK_EQUAL(e.alternatives()[0], "cfgfile"); + BOOST_CHECK_EQUAL(e.alternatives()[1], "output"); + } +} + + +void test_ambiguous_long() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>()->multitoken(), "the config file") + ("output,c", value<string>(), "the output file") + ("output,o", value<string>(), "the output file") + ; + + const char* cmdline[] = {"program", "--cfgfile", "file", "--output", "anotherfile"}; + + variables_map vm; + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + } + catch (ambiguous_option& e) + { + BOOST_CHECK_EQUAL(e.alternatives().size(), 2); + BOOST_CHECK_EQUAL(e.get_option_name(), "--output"); + BOOST_CHECK_EQUAL(e.alternatives()[0], "output"); + BOOST_CHECK_EQUAL(e.alternatives()[1], "output"); + } +} + +void test_ambiguous_multiple_long_names() +{ + options_description desc; + desc.add_options() + ("cfgfile,foo,c", value<string>()->multitoken(), "the config file") + ("output,foo,o", value<string>(), "the output file") + ; + + const char* cmdline[] = {"program", "--foo", "file"}; + + variables_map vm; + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + } + catch (ambiguous_option& e) + { + BOOST_CHECK_EQUAL(e.alternatives().size(), 2); + BOOST_CHECK_EQUAL(e.get_option_name(), "--foo"); + BOOST_CHECK_EQUAL(e.alternatives()[0], "cfgfile"); + BOOST_CHECK_EQUAL(e.alternatives()[1], "output"); + } +} + + + +void test_unknown_option() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>(), "the configfile") + ; + + const char* cmdline[] = {"program", "-c", "file", "-f", "anotherfile"}; + + variables_map vm; + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + } + catch (unknown_option& e) + { + BOOST_CHECK_EQUAL(e.get_option_name(), "-f"); + BOOST_CHECK_EQUAL(string(e.what()), "unrecognised option '-f'"); + } +} + + + +void test_multiple_values() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>()->multitoken(), "the config file") + ("output,o", value<string>(), "the output file") + ; + + const char* cmdline[] = { "program", "-o", "fritz", "hugo", "--cfgfile", "file", "c", "-o", "text.out" }; + + variables_map vm; + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + notify(vm); + } + catch (validation_error& e) + { + // TODO: this is currently validation_error, shouldn't it be multiple_values ??? + // + // multiple_values is thrown only at one place untyped_value::xparse(), + // but I think this can never be reached + // because: untyped_value always has one value and this is filtered before reach specific + // validation and parsing + // + BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile"); + BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' only takes a single argument"); + } +} + + +void test_multiple_occurrences() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>(), "the configfile") + ; + + const char* cmdline[] = {"program", "--cfgfile", "file", "-c", "anotherfile"}; + + variables_map vm; + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + notify(vm); + } + catch (multiple_occurrences& e) + { + BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile"); + BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' cannot be specified more than once"); + } +} + +void test_multiple_occurrences_with_different_names() +{ + options_description desc; + desc.add_options() + ("cfgfile,config-file,c", value<string>(), "the configfile") + ; + + const char* cmdline[] = {"program", "--config-file", "file", "--cfgfile", "anotherfile"}; + + variables_map vm; + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + notify(vm); + } + catch (multiple_occurrences& e) + { + BOOST_CHECK( (e.get_option_name() == "--cfgfile") || (e.get_option_name() == "--config-file")); + BOOST_CHECK( + (string(e.what()) == "option '--cfgfile' cannot be specified more than once") || + (string(e.what()) == "option '--config-file' cannot be specified more than once") + ); + } +} + + +void test_multiple_occurrences_with_non_key_names() +{ + options_description desc; + desc.add_options() + ("cfgfile,config-file,c", value<string>(), "the configfile") + ; + + const char* cmdline[] = {"program", "--config-file", "file", "-c", "anotherfile"}; + + variables_map vm; + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + notify(vm); + } + catch (multiple_occurrences& e) + { + BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile"); + BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' cannot be specified more than once"); + } +} + + +void test_missing_value() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>()->multitoken(), "the config file") + ("output,o", value<string>(), "the output file") + ; + // missing value for option '-c' + const char* cmdline[] = { "program", "-c", "-c", "output.txt"}; + + variables_map vm; + + try { + store(parse_command_line(sizeof(cmdline)/sizeof(const char*), + const_cast<char**>(cmdline), desc), vm); + notify(vm); + } + catch (invalid_command_line_syntax& e) + { + BOOST_CHECK_EQUAL(e.kind(), invalid_syntax::missing_parameter); + BOOST_CHECK_EQUAL(e.tokens(), "--cfgfile"); + } +} + + + +int main(int /*ac*/, char** /*av*/) +{ + test_ambiguous(); + test_ambiguous_long(); + test_ambiguous_multiple_long_names(); + test_unknown_option(); + test_multiple_values(); + test_multiple_occurrences(); + test_multiple_occurrences_with_different_names(); + test_multiple_occurrences_with_non_key_names(); + test_missing_value(); + + return 0; +} + diff --git a/src/boost/libs/program_options/test/exception_txt_test.cpp b/src/boost/libs/program_options/test/exception_txt_test.cpp new file mode 100644 index 00000000..43e0014b --- /dev/null +++ b/src/boost/libs/program_options/test/exception_txt_test.cpp @@ -0,0 +1,693 @@ +// Copyright Leo Goodstadt 2012 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/program_options/cmdline.hpp> +using namespace boost::program_options; + +#include <iostream> +#include <sstream> +#include <vector> +#include <cassert> +using namespace std; + +#include "minitest.hpp" + + + +// +// like BOOST_CHECK_EQUAL but with more descriptive error message +// +#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \ + description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);} + + +// Uncomment for Debugging, removes asserts so we can see more failures! +//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n"; + + +//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 +// +// Uncomment to print out the complete set of diagnostic messages for the different test cases +/* +#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \ + description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \ + else {std::cout << description<< "\t" << b << "\n";} +*/ + +//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 + + +// +// test exception for each specified command line style, e.g. short dash or config file +// +template<typename EXCEPTION> +void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin) +{ + if (exception_msg.length() == 0) + return; + variables_map vm; + unsigned argc = argv.size(); + + + try { + if (style == -1) + store(parse_config_file(is, desc), vm); + else + store(parse_command_line(argv.size(), &argv[0], desc, style), vm); + notify(vm); + } + catch (EXCEPTION& e) + { + //cerr << "Correct:\n\t" << e.what() << "\n"; + CHECK_EQUAL(test_description, e.what(), exception_msg); + return; + } + catch (std::exception& e) + { + // concatenate argv without boost::algorithm::join + string argv_txt; + for (unsigned ii = 0; ii < argc - 1; ++ii) + argv_txt += argv[ii] + string(" "); + if (argc) + argv_txt += argv[argc - 1]; + + BOOST_ERROR("\n<<" + test_description + + string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() + + "\"\n argv =\"" + argv_txt + + "\"\n Expected text=\"" + exception_msg + "\"\n"); + return; + } + BOOST_ERROR(test_description + ": No exception thrown. "); +} + + + + +// +// test exception messages for all command line styles (unix/long/short/slash/config file) +// +// try each command line style in turn +const int unix_style = command_line_style::unix_style; +const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky; +const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent; +const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing; + + + +template<typename EXCEPTION> +void test_exception_message(const vector<vector<const char*> >& argv, + options_description& desc, + const string& error_description, + const char* expected_message_template[5]) +{ + string expected_message; + + // unix + expected_message = expected_message_template[0]; + test_each_exception_message<EXCEPTION>(error_description + " -- unix", + argv[0], desc, unix_style, expected_message); + + // long dash only + expected_message = expected_message_template[1]; + test_each_exception_message<EXCEPTION>(error_description + " -- long_dash", + argv[1], desc, long_dash, expected_message); + + + // short dash only + expected_message = expected_message_template[2]; + test_each_exception_message<EXCEPTION>(error_description + " -- short_dash", + argv[2], desc, short_dash, expected_message); + + // short slash only + expected_message = expected_message_template[3]; + test_each_exception_message<EXCEPTION>(error_description + " -- short_slash", + argv[3], desc, short_slash, expected_message); + + // config file only + expected_message = expected_message_template[4]; + if (expected_message.length()) + { + istringstream istrm(argv[4][0]); + test_each_exception_message<EXCEPTION>(error_description + " -- config_file", + argv[4], desc, -1, expected_message, istrm); + } + +} + +#define VEC_STR_PUSH_BACK(vec, c_array) \ + vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*))); + +//________________________________________________________________________________________ +// +// invalid_option_value +// +//________________________________________________________________________________________ +void test_invalid_option_value_exception_msg() +{ + options_description desc; + desc.add_options() + ("int-option,d", value< int >(), "An option taking an integer") + ; + + vector<vector<const char*> > argv; + const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { "int-option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4); + + const char* expected_msg[5] = { + "the argument ('A_STRING') for option '--int-option' is invalid", + "the argument ('A_STRING') for option '--int-option' is invalid", + "the argument ('A_STRING') for option '-d' is invalid", + "the argument ('A_STRING') for option '/d' is invalid", + "the argument ('A_STRING') for option 'int-option' is invalid", + }; + + + test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value", + expected_msg); + + +} + +//________________________________________________________________________________________ +// +// missing_value +// +//________________________________________________________________________________________ +void test_missing_value_exception_msg() +{ + options_description desc; + desc.add_options() + ("cfgfile,e", value<string>(), "the config file") + ("output,o", value<string>(), "the output file") + ; + vector<vector<const char*> > argv; + const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4); + + const char* expected_msg[5] = { + "the required argument for option '--cfgfile' is missing", + "the required argument for option '--cfgfile' is missing", + "the required argument for option '-e' is missing", + "", // Ignore probable bug in cmdline::finish_option + //"the required argument for option '/e' is missing", + "", + }; + test_exception_message<invalid_command_line_syntax>(argv, desc, + "invalid_syntax::missing_parameter", + expected_msg); +} + +//________________________________________________________________________________________ +// +// ambiguous_option +// +//________________________________________________________________________________________ +void test_ambiguous_option_exception_msg() +{ + options_description desc; + desc.add_options() + ("cfgfile1,c", value<string>(), "the config file") + ("cfgfile2,o", value<string>(), "the config file") + ("good,g", "good option") + ("output,c", value<string>(), "the output file") + ("output", value<string>(), "the output file") + ; + + vector<vector<const char*> > argv; + const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4); + const char* expected_msg[5] = { + "option '-c' is ambiguous and matches '--cfgfile1', and '--output'", + "option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'", + "option '-c' is ambiguous", + "option '/c' is ambiguous", + "option 'output' is ambiguous and matches different versions of 'output'", + }; + test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option", + expected_msg); +} + +//________________________________________________________________________________________ +// +// multiple_occurrences +// +//________________________________________________________________________________________ +void test_multiple_occurrences_exception_msg() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>(), "the configfile") + ; + + vector<vector<const char*> > argv; + const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4); + const char* expected_msg[5] = { + "option '--cfgfile' cannot be specified more than once", + "option '--cfgfile' cannot be specified more than once", + "option '-c' cannot be specified more than once", + "option '/c' cannot be specified more than once", + "option 'cfgfile' cannot be specified more than once", + }; + test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences", + expected_msg); +} + +//________________________________________________________________________________________ +// +// unknown_option +// +//________________________________________________________________________________________ +void test_unknown_option_exception_msg() +{ + options_description desc; + desc.add_options() + ("good,g", "good option") + ; + + vector<vector<const char*> > argv; + const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4); + const char* expected_msg[5] = { + "unrecognised option '-ggc'", + "unrecognised option '--cfgfile'", + "unrecognised option '-ggc'", + "unrecognised option '/c'", + "unrecognised option 'cfgfile'", + }; + test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg); +} + +//________________________________________________________________________________________ +// +// validation_error::invalid_bool_value +// +//________________________________________________________________________________________ +void test_invalid_bool_value_exception_msg() +{ + options_description desc; + desc.add_options() + ("bool_option,b", value< bool>(), "bool_option") + ; + + + vector<vector<const char*> > argv; + const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4); + const char* expected_msg[5] = { + "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'", + "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'", + "the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'", + "the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'", + "the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'", + }; + test_exception_message<validation_error>(argv, + desc, + "validation_error::invalid_bool_value", + expected_msg); +} + + + + +//________________________________________________________________________________________ +// +// validation_error::multiple_values_not_allowed +// +//________________________________________________________________________________________ +// +// Strange exception: sole purpose seems to be catching multitoken() associated with a scalar +// validation_error::multiple_values_not_allowed seems thus to be a programmer error +// +// +void test_multiple_values_not_allowed_exception_msg() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>()->multitoken(), "the config file") + ("good,g", "good option") + ("output,o", value<string>(), "the output file") + ; + + vector<vector<const char*> > argv; + const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4); + const char* expected_msg[5] = { + "option '--cfgfile' only takes a single argument", + "option '--cfgfile' only takes a single argument", + "option '-c' only takes a single argument", + "option '/c' only takes a single argument", + "", + }; + test_exception_message<validation_error>(argv, + desc, + "validation_error::multiple_values_not_allowed", + expected_msg); +} + +//________________________________________________________________________________________ +// +// validation_error::at_least_one_value_required +// +//________________________________________________________________________________________ +// +// Strange exception: sole purpose seems to be catching zero_tokens() associated with a scalar +// validation_error::multiple_values_not_allowed seems thus to be a programmer error +// +// +void test_at_least_one_value_required_exception_msg() +{ + + + options_description desc; + desc.add_options() + ("cfgfile,c", value<int>()->zero_tokens(), "the config file") + ("other,o", value<string>(), "other") + ; + + vector<vector<const char*> > argv; + const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = { "program", "--cfg", "--o", "name" } ; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4); + const char* expected_msg[5] = { + "option '--cfgfile' requires at least one argument", + "option '--cfgfile' requires at least one argument", + "option '-c' requires at least one argument", + "option '/c' requires at least one argument", + "", + }; + test_exception_message<validation_error>(argv, + desc, + "validation_error::at_least_one_value_required", + expected_msg); +} + + +//________________________________________________________________________________________ +// +// required_option +// +//________________________________________________________________________________________ +void test_required_option_exception_msg() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", value<string>()->required(), "the config file") + ("good,g", "good option") + ("output,o", value<string>()->required(), "the output file") + ; + + vector<vector<const char*> > argv; + const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0); + const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1); + const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2); + const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3); + const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4); + const char* expected_msg[5] = { + "the option '--cfgfile' is required but missing", + "the option '--cfgfile' is required but missing", + "the option '-c' is required but missing", + "the option '/c' is required but missing", + "the option 'cfgfile' is required but missing", + }; + test_exception_message<required_option>(argv, + desc, + "required_option", + expected_msg); +} + + + + + + + + + + + + + + + + +/** + * Check if this is the expected exception with the right message is being thrown inside + * func +*/ +template <typename EXCEPTION, typename FUNC> +void test_exception(const string& test_name, const string& exception_txt, FUNC func) +{ + + try { + options_description desc; + variables_map vm; + func(desc, vm); + } + catch (EXCEPTION& e) + { + CHECK_EQUAL(test_name, e.what(), exception_txt); + return; + } + catch (std::exception& e) + { + BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() + + "\nExpected text:\n" + exception_txt + "\n\n"); + return; + } + BOOST_ERROR(test_name + ": No exception thrown. "); +} + + + +//________________________________________________________________________________________ +// +// check_reading_file +// +//________________________________________________________________________________________ +void check_reading_file(options_description& desc, variables_map& vm) +{ + desc.add_options() + ("output,o", value<string>(), "the output file"); + + const char* file_name = "no_such_file"; + store(parse_config_file<char>(file_name, desc, true), vm); + +} + + +//________________________________________________________________________________________ +// +// config_file_wildcard +// +//________________________________________________________________________________________ +void config_file_wildcard(options_description& desc, variables_map& vm) +{ + desc.add_options() + ("outpu*", value<string>(), "the output file1") + ("outp*", value<string>(), "the output file2") + ; + istringstream is("output1=whichone\noutput2=whichone\n"); + store(parse_config_file(is, desc), vm); +} + +//________________________________________________________________________________________ +// +// invalid_syntax::unrecognized_line +// +//________________________________________________________________________________________ +void unrecognized_line(options_description& desc, variables_map& vm) +{ + istringstream is("funny wierd line\n"); + store(parse_config_file(is, desc), vm); +} + +//________________________________________________________________________________________ +// +// abbreviated_options_in_config_file +// +//________________________________________________________________________________________ +void abbreviated_options_in_config_file(options_description& desc, variables_map& vm) +{ + desc.add_options()(",o", value<string>(), "the output file"); + istringstream is("o=output.txt\n"); + store(parse_config_file(is, desc), vm); +} + + +//________________________________________________________________________________________ +// +// too_many_positional_options +// +//________________________________________________________________________________________ +void too_many_positional_options(options_description& desc, variables_map& vm) +{ + const char* argv[] = {"program", "1", "2", "3"}; + positional_options_description positional_args; + positional_args.add("two_positional_arguments", 2); + store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm); +} + + +//________________________________________________________________________________________ +// +// invalid_command_line_style +// +//________________________________________________________________________________________ + +void test_invalid_command_line_style_exception_msg() +{ + string test_name = "invalid_command_line_style"; + using namespace command_line_style; + options_description desc; + desc.add_options()("output,o", value<string>(), "the output file"); + + vector<int> invalid_styles; + invalid_styles.push_back(allow_short | short_allow_adjacent); + invalid_styles.push_back(allow_short | allow_dash_for_short); + invalid_styles.push_back(allow_long); + vector<string> invalid_diagnostics; + invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one " + "or other of 'command_line_style::allow_slash_for_short' " + "(slashes) or 'command_line_style::allow_dash_for_short' " + "(dashes) for short options."); + invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one " + "or other of 'command_line_style::short_allow_next' " + "(whitespace separated arguments) or " + "'command_line_style::short_allow_adjacent' ('=' " + "separated arguments) for short options."); + invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one " + "or other of 'command_line_style::long_allow_next' " + "(whitespace separated arguments) or " + "'command_line_style::long_allow_adjacent' ('=' " + "separated arguments) for long options."); + + + const char* argv[] = {"program"}; + variables_map vm; + for (unsigned ii = 0; ii < 3; ++ii) + { + bool exception_thrown = false; + try + { + store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm); + } + catch (invalid_command_line_style& e) + { + string error_msg("arguments are not allowed for unabbreviated option names"); + CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]); + exception_thrown = true; + } + catch (std::exception& e) + { + BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() + + "\nExpected text:\n" + invalid_diagnostics[ii] + "\n"); + exception_thrown = true; + } + if (!exception_thrown) + { + BOOST_ERROR(test_name << ": No exception thrown. "); + } + } +} + +void test_empty_value_inner(options_description &opts, variables_map& vm) { + positional_options_description popts; + opts.add_options()("foo", value<uint32_t>()->value_name("<time>")->required()); + popts.add("foo", 1); + vector<string> tokens(1, ""); + parsed_options parsed = command_line_parser(tokens) + .style(command_line_style::default_style & ~command_line_style::allow_guessing) + .options(opts) + .positional(popts) + .run(); + store(parsed, vm); +} + +void test_empty_value() { + // Test that passing empty token for an option that requires integer does not result + // in out-of-range error in error reporting code. + test_exception<invalid_option_value>( + "test_empty_value", + "the argument for option '--foo' is invalid", + test_empty_value_inner); +} + +int main(int /*ac*/, char** /*av*/) +{ + test_ambiguous_option_exception_msg(); + test_unknown_option_exception_msg(); + test_multiple_occurrences_exception_msg(); + test_missing_value_exception_msg(); + test_invalid_option_value_exception_msg(); + test_invalid_bool_value_exception_msg(); + test_multiple_values_not_allowed_exception_msg(); + test_required_option_exception_msg(); + test_at_least_one_value_required_exception_msg(); + test_empty_value(); + + string test_name; + string expected_message; + + + // check_reading_file + test_name = "check_reading_file"; + expected_message = "can not read options configuration file 'no_such_file'"; + test_exception<reading_file>(test_name, expected_message, check_reading_file); + + // config_file_wildcard + test_name = "config_file_wildcard"; + expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file"; + test_exception<error>(test_name, expected_message, config_file_wildcard); + + // unrecognized_line + test_name = "unrecognized_line"; + expected_message = "the options configuration file contains an invalid line 'funny wierd line'"; + test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line); + + + // abbreviated_options_in_config_file + test_name = "abbreviated_options_in_config_file"; + expected_message = "abbreviated option names are not permitted in options configuration files"; + test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file); + + test_name = "too_many_positional_options"; + expected_message = "too many positional options have been specified on the command line"; + test_exception<too_many_positional_options_error>( + test_name, expected_message, too_many_positional_options); + + test_invalid_command_line_style_exception_msg(); + + + return 0; +} + diff --git a/src/boost/libs/program_options/test/minitest.hpp b/src/boost/libs/program_options/test/minitest.hpp new file mode 100644 index 00000000..7d38efd2 --- /dev/null +++ b/src/boost/libs/program_options/test/minitest.hpp @@ -0,0 +1,25 @@ +#ifndef BOOST_PROGRAM_OPTIONS_MINITEST +#define BOOST_PROGRAM_OPTIONS_MINITEST + +#include <assert.h> +#include <iostream> +#include <stdlib.h> + +#define BOOST_REQUIRE(b) assert(b) +#define BOOST_CHECK(b) assert(b) +#define BOOST_CHECK_EQUAL(a, b) assert(a == b) +#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n"; abort(); +#define BOOST_CHECK_THROW(expression, exception) \ + try \ + { \ + expression; \ + BOOST_ERROR("expected exception not thrown");\ + throw 10; \ + } \ + catch(exception &) \ + { \ + } + + + +#endif diff --git a/src/boost/libs/program_options/test/optional_test.cpp b/src/boost/libs/program_options/test/optional_test.cpp new file mode 100644 index 00000000..b38308e5 --- /dev/null +++ b/src/boost/libs/program_options/test/optional_test.cpp @@ -0,0 +1,53 @@ +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/program_options.hpp> +namespace po = boost::program_options; + +#include <boost/optional.hpp> + +#include <string> + +#include "minitest.hpp" + +std::vector<std::string> sv(const char* array[], unsigned size) +{ + std::vector<std::string> r; + for (unsigned i = 0; i < size; ++i) + r.push_back(array[i]); + return r; +} + +void test_optional() +{ + boost::optional<int> foo, bar, baz; + + po::options_description desc; + desc.add_options() + ("foo,f", po::value(&foo), "") + ("bar,b", po::value(&bar), "") + ("baz,z", po::value(&baz), "") + ; + + const char* cmdline1_[] = { "--foo=12", "--bar", "1"}; + std::vector<std::string> cmdline1 = sv(cmdline1_, + sizeof(cmdline1_)/sizeof(const char*)); + po::variables_map vm; + po::store(po::command_line_parser(cmdline1).options(desc).run(), vm); + po::notify(vm); + + BOOST_REQUIRE(!!foo); + BOOST_CHECK(*foo == 12); + + BOOST_REQUIRE(!!bar); + BOOST_CHECK(*bar == 1); + + BOOST_CHECK(!baz); +} + +int main(int, char*[]) +{ + test_optional(); + return 0; +} diff --git a/src/boost/libs/program_options/test/options_description_test.cpp b/src/boost/libs/program_options/test/options_description_test.cpp new file mode 100644 index 00000000..d0df7ef3 --- /dev/null +++ b/src/boost/libs/program_options/test/options_description_test.cpp @@ -0,0 +1,347 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options/options_description.hpp> +using namespace boost::program_options; + +#include <boost/function.hpp> +using namespace boost; + +#include <utility> +#include <string> +#include <sstream> +using namespace std; + +#include "minitest.hpp" + +void test_type() +{ + options_description desc; + desc.add_options() + ("foo", value<int>(), "") + ("bar", value<string>(), "") + ; + +#ifndef BOOST_NO_RTTI + const typed_value_base* b = dynamic_cast<const typed_value_base*> + (desc.find("foo", false).semantic().get()); + BOOST_CHECK(b); + BOOST_CHECK(b->value_type() == typeid(int)); + + const typed_value_base* b2 = dynamic_cast<const typed_value_base*> + (desc.find("bar", false).semantic().get()); + BOOST_CHECK(b2); + BOOST_CHECK(b2->value_type() == typeid(string)); +#endif +} + +void test_approximation() +{ + options_description desc; + desc.add_options() + ("foo", new untyped_value()) + ("fee", new untyped_value()) + ("baz", new untyped_value()) + ("all-chroots", new untyped_value()) + ("all-sessions", new untyped_value()) + ("all", new untyped_value()) + ; + + BOOST_CHECK_EQUAL(desc.find("fo", true).long_name(), "foo"); + + BOOST_CHECK_EQUAL(desc.find("all", true).long_name(), "all"); + BOOST_CHECK_EQUAL(desc.find("all-ch", true).long_name(), "all-chroots"); + + options_description desc2; + desc2.add_options() + ("help", "display this message") + ("config", value<string>(), "config file name") + ("config-value", value<string>(), "single config value") + ; + + BOOST_CHECK_EQUAL(desc2.find("config", true).long_name(), "config"); + BOOST_CHECK_EQUAL(desc2.find("config-value", true).long_name(), + "config-value"); + + +// BOOST_CHECK(desc.count_approx("foo") == 1); +// set<string> a = desc.approximations("f"); +// BOOST_CHECK(a.size() == 2); +// BOOST_CHECK(*a.begin() == "fee"); +// BOOST_CHECK(*(++a.begin()) == "foo"); +} + +void test_approximation_with_multiname_options() +{ + options_description desc; + desc.add_options() + ("foo", new untyped_value()) + ("fee", new untyped_value()) + ("fe,baz", new untyped_value()) + ("chroots,all-chroots", new untyped_value()) + ("sessions,all-sessions", new untyped_value()) + ("everything,all", new untyped_value()) + ("qux,fo", new untyped_value()) + ; + + BOOST_CHECK_EQUAL(desc.find("fo", true).long_name(), "qux"); + + BOOST_CHECK_EQUAL(desc.find("all", true).long_name(), "everything"); + BOOST_CHECK_EQUAL(desc.find("all-ch", true).long_name(), "chroots"); + + BOOST_CHECK_EQUAL(desc.find("foo", false, false, false).long_names().second, 1u); + BOOST_CHECK_EQUAL(desc.find("foo", false, false, false).long_names().first[0], "foo"); + + BOOST_CHECK_EQUAL(desc.find("fe", false, false, false).long_names().second, 2u); + BOOST_CHECK_EQUAL(desc.find("fe", false, false, false).long_names().first[0], "fe"); + BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().first[1], "baz"); + + BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().second, 2u); + BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().first[0], "fizbaz"); + BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().first[1], "baz"); +} + +void test_long_names_for_option_description() +{ + options_description desc; + desc.add_options() + ("foo", new untyped_value()) + ("fe,baz", new untyped_value()) + ("chroots,all-chroots", new untyped_value()) + ("sessions,all-sessions", new untyped_value()) + ("everything,all", new untyped_value()) + ("qux,fo,q", new untyped_value()) + ; + + BOOST_CHECK_EQUAL(desc.find("foo", false, false, false).long_names().second, 1u); + BOOST_CHECK_EQUAL(desc.find("foo", false, false, false).long_names().first[0], "foo"); + + BOOST_CHECK_EQUAL(desc.find("fe", false, false, false).long_names().second, 2u); + BOOST_CHECK_EQUAL(desc.find("fe", false, false, false).long_names().first[0], "fe"); + BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().first[1], "baz"); + + BOOST_CHECK_EQUAL(desc.find("qux", false, false, false).long_names().second, 2u); + BOOST_CHECK_EQUAL(desc.find("qux", false, false, false).long_names().first[0], "qux"); + BOOST_CHECK_EQUAL(desc.find("qux", false, false, false).long_names().first[1], "fo"); +} + + +void test_formatting() +{ + // Long option descriptions used to crash on MSVC-8.0. + options_description desc; + desc.add_options()( + "test", new untyped_value(), + "foo foo foo foo foo foo foo foo foo foo foo foo foo foo" + "foo foo foo foo foo foo foo foo foo foo foo foo foo foo" + "foo foo foo foo foo foo foo foo foo foo foo foo foo foo" + "foo foo foo foo foo foo foo foo foo foo foo foo foo foo") + ("list", new untyped_value(), + "a list:\n \t" + "item1, item2, item3, item4, item5, item6, item7, item8, item9, " + "item10, item11, item12, item13, item14, item15, item16, item17, item18") + ("well_formated", new untyped_value(), + "As you can see this is a very well formatted option description.\n" + "You can do this for example:\n\n" + "Values:\n" + " Value1: \tdoes this and that, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n" + " Value2: \tdoes something else, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n\n" + " This paragraph has a first line indent only, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla") + ; + + stringstream ss; + ss << desc; + BOOST_CHECK_EQUAL(ss.str(), +" --test arg foo foo foo foo foo foo foo foo foo foo foo foo foo \n" +" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n" +" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n" +" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n" +" foo\n" +" --list arg a list:\n" +" item1, item2, item3, item4, item5, item6, item7, \n" +" item8, item9, item10, item11, item12, item13, \n" +" item14, item15, item16, item17, item18\n" +" --well_formated arg As you can see this is a very well formatted option \n" +" description.\n" +" You can do this for example:\n" +" \n" +" Values:\n" +" Value1: does this and that, bla bla bla bla bla bla \n" +" bla bla bla bla bla bla bla bla bla\n" +" Value2: does something else, bla bla bla bla bla bla \n" +" bla bla bla bla bla bla bla bla bla\n" +" \n" +" This paragraph has a first line indent only, bla \n" +" bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n" + ); +} + +void test_multiname_option_formatting() +{ + options_description desc; + desc.add_options() + ("foo,bar", new untyped_value(), "a multiple-name option") + ; + + stringstream ss; + ss << desc; + BOOST_CHECK_EQUAL(ss.str(), +" --foo arg a multiple-name option\n" + ); +} + + +void test_formatting_description_length() +{ + { + options_description desc("", + options_description::m_default_line_length, + options_description::m_default_line_length / 2U); + desc.add_options() + ("an-option-that-sets-the-max", new untyped_value(), // > 40 available for desc + "this description sits on the same line, but wrapping should still work correctly") + ("a-long-option-that-would-leave-very-little-space-for-description", new untyped_value(), + "the description of the long opt, but placed on the next line\n" + " \talso ensure that the tabulation works correctly when a" + " description size has been set"); + + stringstream ss; + ss << desc; + BOOST_CHECK_EQUAL(ss.str(), + " --an-option-that-sets-the-max arg this description sits on the same line,\n" + " but wrapping should still work \n" + " correctly\n" + " --a-long-option-that-would-leave-very-little-space-for-description arg\n" + " the description of the long opt, but \n" + " placed on the next line\n" + " also ensure that the tabulation \n" + " works correctly when a description \n" + " size has been set\n"); + } + { + // the default behaviour reserves 23 (+1 space) characters for the + // option column; this shows that the min_description_length does not + // breach that. + options_description desc("", + options_description::m_default_line_length, + options_description::m_default_line_length - 10U); // leaves < 23 (default option space) + desc.add_options() + ("an-option-that-encroaches-description", new untyped_value(), + "this description should always be placed on the next line, and wrapping should continue as normal"); + + stringstream ss; + ss << desc; + BOOST_CHECK_EQUAL(ss.str(), + " --an-option-that-encroaches-description arg\n" + //123456789_123456789_ + " this description should always be placed on the next line, and \n" + " wrapping should continue as normal\n"); + } +} + +void test_long_default_value() +{ + options_description desc; + desc.add_options() + ("cfgfile,c", + value<string>()->default_value("/usr/local/etc/myprogramXXXXXXXXX/configuration.conf"), + "the configfile") + ; + + stringstream ss; + ss << desc; + BOOST_CHECK_EQUAL(ss.str(), +" -c [ --cfgfile ] arg (=/usr/local/etc/myprogramXXXXXXXXX/configuration.conf)\n" +" the configfile\n" + ); +} + +void test_word_wrapping() +{ + options_description desc("Supported options"); + desc.add_options() + ("help", "this is a sufficiently long text to require word-wrapping") + ("prefix", value<string>()->default_value("/h/proj/tmp/dispatch"), "root path of the dispatch installation") + ("opt1", "this_is_a_sufficiently_long_text_to_require_word-wrapping_but_cannot_be_wrapped") + ("opt2", "this_is_a_sufficiently long_text_to_require_word-wrapping") + ("opt3", "this_is_a sufficiently_long_text_to_require_word-wrapping_but_will_not_be_wrapped") + ; + stringstream ss; + ss << desc; + BOOST_CHECK_EQUAL(ss.str(), +"Supported options:\n" +" --help this is a sufficiently long text to \n" +" require word-wrapping\n" +" --prefix arg (=/h/proj/tmp/dispatch) root path of the dispatch installation\n" +" --opt1 this_is_a_sufficiently_long_text_to_requ\n" +" ire_word-wrapping_but_cannot_be_wrapped\n" +" --opt2 this_is_a_sufficiently \n" +" long_text_to_require_word-wrapping\n" +" --opt3 this_is_a sufficiently_long_text_to_requ\n" +" ire_word-wrapping_but_will_not_be_wrappe\n" +" d\n" + ); +} + +void test_default_values() +{ + options_description desc("Supported options"); + desc.add_options() + ("maxlength", value<double>()->default_value(.1, "0.1"), "Maximum edge length to keep.") + ; + stringstream ss; + ss << desc; + BOOST_CHECK_EQUAL(ss.str(), +"Supported options:\n" +" --maxlength arg (=0.1) Maximum edge length to keep.\n" + ); +} + +void test_value_name() +{ + options_description desc("Supported options"); + desc.add_options() + ("include", value<string>()->value_name("directory"), "Search for headers in 'directory'.") + ; + + stringstream ss; + ss << desc; + BOOST_CHECK_EQUAL(ss.str(), +"Supported options:\n" +" --include directory Search for headers in 'directory'.\n" + ); +} + +void test_multiname_key_and_switch_selection() +{ + // cases: + // foo,f -> f + // foo, c -> c + // foo,f,g -> g + // f,g,h -> h + // f,foo throws + // foo,bar -> no switch + // foo,f,bar -> no switch + + // what about empty strings - consecutive ,'s ? +} + +int main(int, char* []) +{ + test_type(); + test_approximation(); + test_long_names_for_option_description(); + test_formatting(); + test_multiname_key_and_switch_selection(); + test_multiname_option_formatting(); + test_formatting_description_length(); + test_long_default_value(); + test_word_wrapping(); + test_default_values(); + test_value_name(); + return 0; +} + diff --git a/src/boost/libs/program_options/test/parsers_test.cpp b/src/boost/libs/program_options/test/parsers_test.cpp new file mode 100644 index 00000000..4cf832ee --- /dev/null +++ b/src/boost/libs/program_options/test/parsers_test.cpp @@ -0,0 +1,388 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +using namespace boost::program_options; +// We'll use po::value everywhere to workaround vc6 bug. +namespace po = boost::program_options; + +#include <boost/function.hpp> +using namespace boost; + +#include <sstream> +#include <iostream> +#include <iomanip> +using namespace std; + +#if defined(__sun) +#include <stdlib.h> // for putenv on solaris +#else +#include <cstdlib> // for putenv +#endif + +#include "minitest.hpp" + +#define TEST_CHECK_THROW(expression, exception, description) \ + try \ + { \ + expression; \ + BOOST_ERROR(description);\ + throw 10; \ + } \ + catch(exception &) \ + { \ + } + +pair<string, vector< vector<string> > > msp(const string& s1) +{ + return std::make_pair(s1, vector< vector<string> >()); +} + + +pair<string, vector< vector<string> > > msp(const string& s1, const string& s2) +{ + vector< vector<string> > v(1); + v[0].push_back(s2); + return std::make_pair(s1, v); +} + +void check_value(const option& option, const char* name, const char* value) +{ + BOOST_CHECK(option.string_key == name); + BOOST_REQUIRE(option.value.size() == 1); + BOOST_CHECK(option.value.front() == value); +} + +vector<string> sv(const char* array[], unsigned size) +{ + vector<string> r; + for (unsigned i = 0; i < size; ++i) + r.push_back(array[i]); + return r; +} + +pair<string, string> additional_parser(const std::string&) +{ + return pair<string, string>(); +} + +namespace command_line { + +#if 0 +// The following commented out blocks used to test parsing +// command line without syntax specification behaviour. +// It is disabled now and probably will never be enabled again: +// it is not possible to figure out what command line means without +// user's help. +void test_parsing_without_specifying_options() { + char* cmdline1[] = { "--a", "--b=12", "-f", "-g4", "-", "file" }; + options_and_arguments a1 = parse_command_line(cmdline1, + cmdline1 + sizeof(cmdline1) / sizeof(cmdline1[0])); + BOOST_REQUIRE(a1.options().size() == 4); + BOOST_CHECK(a1.options()[0] == msp("a", "")); + BOOST_CHECK(a1.options()[1] == msp("b", "12")); + BOOST_CHECK(a1.options()[2] == msp("-f", "")); + BOOST_CHECK(a1.options()[3] == msp("-g", "4")); + BOOST_REQUIRE(a1.arguments().size() == 2); + BOOST_CHECK(a1.arguments()[0] == "-"); + BOOST_CHECK(a1.arguments()[1] == "file"); + char* cmdline2[] = { "--a", "--", "file" }; + options_and_arguments a2 = parse_command_line(cmdline2, + cmdline2 + sizeof(cmdline2) / sizeof(cmdline2[0])); + BOOST_REQUIRE(a2.options().size() == 1); + BOOST_CHECK(a2.options()[0] == msp("a", "")); + BOOST_CHECK(a2.arguments().size() == 1); + BOOST_CHECK(a2.arguments()[0] == "file"); +} +#endif + +void test_many_different_options() { + options_description desc; + desc.add_options() + ("foo,f", new untyped_value(), "") + ( // Explicit qualification is a workaround for vc6 + "bar,b", po::value<std::string>(), "") + ("car,voiture", new untyped_value()) + ("dog,dawg", new untyped_value()) + ("baz", new untyped_value()) + ("plug*", new untyped_value()); + const char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4", + "--voiture=15", "--dawg=16", "--dog=17", "--plug3=10" }; + vector<string> cmdline3 = sv(cmdline3_, + sizeof(cmdline3_) / sizeof(const char*)); + vector<option> a3 = + command_line_parser(cmdline3).options(desc).run().options; + BOOST_CHECK_EQUAL(a3.size(), 8u); + check_value(a3[0], "foo", "12"); + check_value(a3[1], "foo", "4"); + check_value(a3[2], "bar", "11"); + check_value(a3[3], "bar", "4"); + check_value(a3[4], "car", "15"); + check_value(a3[5], "dog", "16"); + check_value(a3[6], "dog", "17"); + check_value(a3[7], "plug3", "10"); + + // Regression test: check that '0' as style is interpreted as + // 'default_style' + vector<option> a4 = parse_command_line( + sizeof(cmdline3_) / sizeof(const char*), cmdline3_, desc, 0, + additional_parser).options; + // The default style is unix-style, where the first argument on the command-line + // is the name of a binary, not an option value, so that should be ignored + BOOST_CHECK_EQUAL(a4.size(), 7u); + check_value(a4[0], "foo", "4"); + check_value(a4[1], "bar", "11"); + check_value(a4[2], "bar", "4"); + check_value(a4[3], "car", "15"); + check_value(a4[4], "dog", "16"); + check_value(a4[5], "dog", "17"); + check_value(a4[6], "plug3", "10"); +} + +void test_not_crashing_with_empty_string_values() { + // Check that we don't crash on empty values of type 'string' + const char* cmdline4[] = { "", "--open", "" }; + options_description desc2; + desc2.add_options()("open", po::value<string>()); + variables_map vm; + po::store( + po::parse_command_line(sizeof(cmdline4) / sizeof(const char*), + const_cast<char**>(cmdline4), desc2), vm); +} + +void test_multitoken() { + const char* cmdline5[] = { "", "-p7", "-o", "1", "2", "3", "-x8" }; + options_description desc3; + desc3.add_options() + (",p", po::value<string>()) + (",o", po::value<string>()->multitoken()) + (",x", po::value<string>()); + vector<option> a5 = parse_command_line( + sizeof(cmdline5) / sizeof(const char*), + const_cast<char**>(cmdline5), desc3, 0, additional_parser).options; + BOOST_CHECK_EQUAL(a5.size(), 3u); + check_value(a5[0], "-p", "7"); + BOOST_REQUIRE(a5[1].value.size() == 3); + BOOST_CHECK_EQUAL(a5[1].string_key, "-o"); + BOOST_CHECK_EQUAL(a5[1].value[0], "1"); + BOOST_CHECK_EQUAL(a5[1].value[1], "2"); + BOOST_CHECK_EQUAL(a5[1].value[2], "3"); + check_value(a5[2], "-x", "8"); +} + +void test_multitoken_and_multiname() { + const char* cmdline[] = { "program", "-fone", "-b", "two", "--foo", "three", "four", "-zfive", "--fee", "six" }; + options_description desc; + desc.add_options() + ("bar,b", po::value<string>()) + ("foo,fee,f", po::value<string>()->multitoken()) + ("fizbaz,baz,z", po::value<string>()); + + vector<option> parsed_options = parse_command_line( + sizeof(cmdline) / sizeof(const char*), + const_cast<char**>(cmdline), desc, 0, additional_parser).options; + + BOOST_CHECK_EQUAL(parsed_options.size(), 5u); + check_value(parsed_options[0], "foo", "one"); + check_value(parsed_options[1], "bar", "two"); + BOOST_CHECK_EQUAL(parsed_options[2].string_key, "foo"); + BOOST_REQUIRE(parsed_options[2].value.size() == 2); + BOOST_CHECK_EQUAL(parsed_options[2].value[0], "three"); + BOOST_CHECK_EQUAL(parsed_options[2].value[1], "four"); + check_value(parsed_options[3], "fizbaz", "five"); + check_value(parsed_options[4], "foo", "six"); + + const char* cmdline_2[] = { "program", "-fone", "-b", "two", "--fee", "three", "four", "-zfive", "--foo", "six" }; + + parsed_options = parse_command_line( + sizeof(cmdline_2) / sizeof(const char*), + const_cast<char**>(cmdline_2), desc, 0, additional_parser).options; + + BOOST_CHECK_EQUAL(parsed_options.size(), 5u); + check_value(parsed_options[0], "foo", "one"); + check_value(parsed_options[1], "bar", "two"); + BOOST_CHECK_EQUAL(parsed_options[2].string_key, "foo"); + BOOST_REQUIRE(parsed_options[2].value.size() == 2); + BOOST_CHECK_EQUAL(parsed_options[2].value[0], "three"); + BOOST_CHECK_EQUAL(parsed_options[2].value[1], "four"); + check_value(parsed_options[3], "fizbaz", "five"); + check_value(parsed_options[4], "foo", "six"); +} + + +void test_multitoken_vector_option() { + po::options_description desc4(""); + desc4.add_options() + ("multitoken,multi-token,m", po::value<std::vector<std::string> >()->multitoken(), "values") + ("file", po::value<std::string>(), "the file to process"); + po::positional_options_description p; + p.add("file", 1); + const char* cmdline6[] = { "", "-m", "token1", "token2", "--", "some_file" }; + vector<option> a6 = + command_line_parser(sizeof(cmdline6) / sizeof(const char*), + const_cast<char**>(cmdline6)).options(desc4).positional(p).run().options; + BOOST_CHECK_EQUAL(a6.size(), 2u); + BOOST_REQUIRE(a6[0].value.size() == 2); + BOOST_CHECK_EQUAL(a6[0].string_key, "multitoken"); + BOOST_CHECK_EQUAL(a6[0].value[0], "token1"); + BOOST_CHECK_EQUAL(a6[0].value[1], "token2"); + BOOST_CHECK_EQUAL(a6[1].string_key, "file"); + BOOST_REQUIRE(a6[1].value.size() == 1); + BOOST_CHECK_EQUAL(a6[1].value[0], "some_file"); +} + +} // namespace command_line + +void test_command_line() +{ + #if 0 + command_line::test_parsing_without_specifying_options(); + #endif + command_line::test_many_different_options(); + // Check that we don't crash on empty values of type 'string' + command_line::test_not_crashing_with_empty_string_values(); + command_line::test_multitoken(); + command_line::test_multitoken_vector_option(); + command_line::test_multitoken_and_multiname(); +} + +void test_config_file(const char* config_file) +{ + options_description desc; + desc.add_options() + ("gv1", new untyped_value) + ("gv2", new untyped_value) + ("empty_value", new untyped_value) + ("plug*", new untyped_value) + ("m1.v1", new untyped_value) + ("m1.v2", new untyped_value) + ("m1.v3,alias3", new untyped_value) + ("b", bool_switch()) + ; + + const char content1[] = + " gv1 = 0#asd\n" + "empty_value = \n" + "plug3 = 7\n" + "b = true\n" + "[m1]\n" + "v1 = 1\n" + "\n" + "v2 = 2\n" + "v3 = 3\n" + ; + + stringstream ss(content1); + vector<option> a1 = parse_config_file(ss, desc).options; + BOOST_REQUIRE(a1.size() == 7); + check_value(a1[0], "gv1", "0"); + check_value(a1[1], "empty_value", ""); + check_value(a1[2], "plug3", "7"); + check_value(a1[3], "b", "true"); + check_value(a1[4], "m1.v1", "1"); + check_value(a1[5], "m1.v2", "2"); + check_value(a1[6], "m1.v3", "3"); + + // same test, but now options come from file + vector<option> a2 = parse_config_file<char>(config_file, desc).options; + BOOST_REQUIRE(a2.size() == 7); + check_value(a2[0], "gv1", "0"); + check_value(a2[1], "empty_value", ""); + check_value(a2[2], "plug3", "7"); + check_value(a2[3], "b", "true"); + check_value(a2[4], "m1.v1", "1"); + check_value(a2[5], "m1.v2", "2"); + check_value(a2[6], "m1.v3", "3"); +} + +void test_environment() +{ + options_description desc; + desc.add_options() + ("foo", new untyped_value, "") + ("bar", new untyped_value, "") + ; + +#if (defined(_WIN32) && ! defined(__BORLANDC__)) || (defined(__CYGWIN__)) + _putenv("PO_TEST_FOO=1"); +#else + putenv(const_cast<char*>("PO_TEST_FOO=1")); +#endif + parsed_options p = parse_environment(desc, "PO_TEST_"); + + BOOST_REQUIRE(p.options.size() == 1); + BOOST_CHECK (p.options[0].string_key == "foo"); + BOOST_REQUIRE(p.options[0].value.size() == 1); + BOOST_CHECK (p.options[0].value[0] == "1"); + + //TODO: since 'bar' does not allow a value, it cannot appear in environemt, + // which already has a value. +} + +void test_unregistered() +{ + options_description desc; + + const char* cmdline1_[] = { "--foo=12", "--bar", "1"}; + vector<string> cmdline1 = sv(cmdline1_, + sizeof(cmdline1_)/sizeof(const char*)); + vector<option> a1 = + command_line_parser(cmdline1).options(desc).allow_unregistered().run() + .options; + + BOOST_REQUIRE(a1.size() == 3); + BOOST_CHECK(a1[0].string_key == "foo"); + BOOST_CHECK(a1[0].unregistered == true); + BOOST_REQUIRE(a1[0].value.size() == 1); + BOOST_CHECK(a1[0].value[0] == "12"); + BOOST_CHECK(a1[1].string_key == "bar"); + BOOST_CHECK(a1[1].unregistered == true); + BOOST_CHECK(a1[2].string_key == ""); + BOOST_CHECK(a1[2].unregistered == false); + + + vector<string> a2 = collect_unrecognized(a1, include_positional); + BOOST_CHECK(a2[0] == "--foo=12"); + BOOST_CHECK(a2[1] == "--bar"); + BOOST_CHECK(a2[2] == "1"); + + // Test that storing unregisted options has no effect + variables_map vm; + + store(command_line_parser(cmdline1).options(desc). + allow_unregistered().run(), + vm); + + BOOST_CHECK_EQUAL(vm.size(), 0u); + + + const char content1[] = + "gv1 = 0\n" + "[m1]\n" + "v1 = 1\n" + ; + + stringstream ss(content1); + vector<option> a3 = parse_config_file(ss, desc, true).options; + BOOST_REQUIRE(a3.size() == 2); + cout << "XXX" << a3[0].value.front() << "\n"; + check_value(a3[0], "gv1", "0"); + check_value(a3[1], "m1.v1", "1"); +} + + + +int main(int, char* av[]) +{ + test_command_line(); + test_config_file(av[1]); + test_environment(); + test_unregistered(); + return 0; +} + diff --git a/src/boost/libs/program_options/test/positional_options_test.cpp b/src/boost/libs/program_options/test/positional_options_test.cpp new file mode 100644 index 00000000..2c1dfd66 --- /dev/null +++ b/src/boost/libs/program_options/test/positional_options_test.cpp @@ -0,0 +1,91 @@ +// Copyright Vladimir Prus 2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/program_options/positional_options.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/parsers.hpp> +using namespace boost::program_options; +// We'll use po::value everywhere to workaround vc6 bug. +namespace po = boost::program_options; + + +#include <boost/limits.hpp> + +#include "minitest.hpp" + +#include <vector> +using namespace std; + +void test_positional_options() +{ + positional_options_description p; + p.add("first", 1); + + BOOST_CHECK_EQUAL(p.max_total_count(), 1u); + BOOST_CHECK_EQUAL(p.name_for_position(0), "first"); + + p.add("second", 2); + + BOOST_CHECK_EQUAL(p.max_total_count(), 3u); + BOOST_CHECK_EQUAL(p.name_for_position(0), "first"); + BOOST_CHECK_EQUAL(p.name_for_position(1), "second"); + BOOST_CHECK_EQUAL(p.name_for_position(2), "second"); + + p.add("third", -1); + + BOOST_CHECK_EQUAL(p.max_total_count(), (std::numeric_limits<unsigned>::max)()); + BOOST_CHECK_EQUAL(p.name_for_position(0), "first"); + BOOST_CHECK_EQUAL(p.name_for_position(1), "second"); + BOOST_CHECK_EQUAL(p.name_for_position(2), "second"); + BOOST_CHECK_EQUAL(p.name_for_position(3), "third"); + BOOST_CHECK_EQUAL(p.name_for_position(10000), "third"); +} + +void test_parsing() +{ + options_description desc; + desc.add_options() + ("first", po::value<int>()) + ("second", po::value<int>()) + ("input-file", po::value< vector<string> >()) + ("some-other", po::value<string>()) + ; + + positional_options_description p; + p.add("input-file", 2).add("some-other", 1); + + vector<string> args; + args.push_back("--first=10"); + args.push_back("file1"); + args.push_back("--second=10"); + args.push_back("file2"); + args.push_back("file3"); + + // Check that positional options are handled. + parsed_options parsed = + command_line_parser(args).options(desc).positional(p).run(); + + BOOST_REQUIRE(parsed.options.size() == 5); + BOOST_CHECK_EQUAL(parsed.options[1].string_key, "input-file"); + BOOST_CHECK_EQUAL(parsed.options[1].value[0], "file1"); + BOOST_CHECK_EQUAL(parsed.options[3].string_key, "input-file"); + BOOST_CHECK_EQUAL(parsed.options[3].value[0], "file2"); + BOOST_CHECK_EQUAL(parsed.options[4].value[0], "file3"); + + args.push_back("file4"); + + // Check that excessive number of positional options is detected. + BOOST_CHECK_THROW(command_line_parser(args).options(desc).positional(p) + .run(), + too_many_positional_options_error); +} + +int main(int, char* []) +{ + test_positional_options(); + test_parsing(); + return 0; +} + diff --git a/src/boost/libs/program_options/test/program_options_size_test.py b/src/boost/libs/program_options/test/program_options_size_test.py new file mode 100644 index 00000000..25ca2f74 --- /dev/null +++ b/src/boost/libs/program_options/test/program_options_size_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/python + +import os +import string + + +call = " hook(10);\n"; +call = " hook(10); hook2(10);hook3(0);hook4(0);\n"; + +def run_test(num_calls, compiler_command): + f = open("program_options_test.cpp", "w") + f.write("""#include <boost/program_options.hpp> +using namespace boost::program_options; + +void do_it() +{ + boost::program_options::options_description desc; + desc.add_options() +""") + for i in range(0, num_calls): + f.write("(\"opt%d\", value<int>())\n") + f.write(";\n}\n") + f.close() + os.system(compiler_command + " -c -save-temps -I /home/ghost/Work/Boost/boost-svn program_options_test.cpp") + + nm = os.popen("nm -S program_options_test.o") + for l in nm: + if string.find(l, "Z5do_itv") != -1: + break + size = int(string.split(l)[1], 16) + return size + +def run_tests(range, compiler_command): + + last_size = None + first_size = None + for num in range: + size = run_test(num, compiler_command) + if last_size: + print "%2d calls: %5d bytes (+ %d)" % (num, size, size-last_size) + else: + print "%2d calls: %5d bytes" % (num, size) + first_size = size + last_size = size + print "Avarage: ", (last_size-first_size)/(range[-1]-range[0]) + +if __name__ == '__main__': + for compiler in [ "g++ -Os", "g++ -O3"]: + print "****", compiler, "****" + run_tests(range(1, 20), compiler) + + + diff --git a/src/boost/libs/program_options/test/quick.cpp b/src/boost/libs/program_options/test/quick.cpp new file mode 100644 index 00000000..edf45c4a --- /dev/null +++ b/src/boost/libs/program_options/test/quick.cpp @@ -0,0 +1,49 @@ + +// Copyright 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + +// See library home page at http://www.boost.org/libs/program_options + +#include <boost/program_options.hpp> +#include <boost/core/lightweight_test.hpp> + +namespace po = boost::program_options; + +int main( int argc, char const* argv[] ) +{ + po::options_description desc( "Allowed options" ); + + desc.add_options() + ( "path,p", po::value<std::string>(), "set initial path" ) + ; + + po::variables_map vm; + + try + { + po::store( po::parse_command_line( argc, argv, desc ), vm ); + po::notify( vm ); + } + catch( std::exception const & x ) + { + std::cerr << "Error: " << x.what() << std::endl; + return 1; + } + + std::string p; + + if( vm.count( "path" ) ) + { + p = vm[ "path" ].as<std::string>(); + } + + std::string expected( "initial" ); + + BOOST_TEST_EQ( p, expected ); + + return boost::report_errors(); +} diff --git a/src/boost/libs/program_options/test/required_test.cfg b/src/boost/libs/program_options/test/required_test.cfg new file mode 100644 index 00000000..4b25dd68 --- /dev/null +++ b/src/boost/libs/program_options/test/required_test.cfg @@ -0,0 +1 @@ +cfgfile = file.cfg diff --git a/src/boost/libs/program_options/test/required_test.cpp b/src/boost/libs/program_options/test/required_test.cpp new file mode 100644 index 00000000..1c623cd5 --- /dev/null +++ b/src/boost/libs/program_options/test/required_test.cpp @@ -0,0 +1,125 @@ +// Copyright Sascha Ochsenknecht 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/program_options.hpp> +using namespace boost::program_options; + +#include <string> +#include <iostream> +#include <fstream> +using namespace std; + +#include "minitest.hpp" + + +void required_throw_test() +{ + options_description opts; + opts.add_options() + ("cfgfile,c", value<string>()->required(), "the configfile") + ("fritz,f", value<string>()->required(), "the output file") + ; + + variables_map vm; + bool thrown = false; + { + // This test must throw exception + string cmdline = "prg -f file.txt"; + vector< string > tokens = split_unix(cmdline); + thrown = false; + try { + store(command_line_parser(tokens).options(opts).run(), vm); + notify(vm); + } + catch (required_option& e) { + BOOST_CHECK_EQUAL(e.what(), string("the option '--cfgfile' is required but missing")); + thrown = true; + } + BOOST_CHECK(thrown); + } + + { + // This test mustn't throw exception + string cmdline = "prg -c config.txt"; + vector< string > tokens = split_unix(cmdline); + thrown = false; + try { + store(command_line_parser(tokens).options(opts).run(), vm); + notify(vm); + } + catch (required_option& e) { + thrown = true; + } + BOOST_CHECK(!thrown); + } +} + + + +void simple_required_test(const char* config_file) +{ + options_description opts; + opts.add_options() + ("cfgfile,c", value<string>()->required(), "the configfile") + ("fritz,f", value<string>()->required(), "the output file") + ; + + variables_map vm; + bool thrown = false; + { + // This test must throw exception + string cmdline = "prg -f file.txt"; + vector< string > tokens = split_unix(cmdline); + thrown = false; + try { + // options coming from different sources + store(command_line_parser(tokens).options(opts).run(), vm); + store(parse_config_file<char>(config_file, opts), vm); + notify(vm); + } + catch (required_option& e) { + thrown = true; + } + BOOST_CHECK(!thrown); + } +} + +void multiname_required_test() +{ + options_description opts; + opts.add_options() + ("foo,bar", value<string>()->required(), "the foo") + ; + + variables_map vm; + bool thrown = false; + { + // This test must throw exception + string cmdline = "prg --bar file.txt"; + vector< string > tokens = split_unix(cmdline); + thrown = false; + try { + // options coming from different sources + store(command_line_parser(tokens).options(opts).run(), vm); + notify(vm); + } + catch (required_option& e) { + thrown = true; + } + BOOST_CHECK(!thrown); + } +} + + + +int main(int /*argc*/, char* av[]) +{ + required_throw_test(); + simple_required_test(av[1]); + multiname_required_test(); + + return 0; +} + diff --git a/src/boost/libs/program_options/test/split_test.cpp b/src/boost/libs/program_options/test/split_test.cpp new file mode 100644 index 00000000..eecd0934 --- /dev/null +++ b/src/boost/libs/program_options/test/split_test.cpp @@ -0,0 +1,189 @@ +// Copyright Sascha Ochsenknecht 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/program_options/cmdline.hpp> +using namespace boost::program_options; + +#include <iostream> +#include <sstream> +#include <vector> +#include <cassert> +using namespace std; + +#include "minitest.hpp" + +void check_value(const string& option, const string& value) +{ + BOOST_CHECK(option == value); +} + +void split_whitespace(const options_description& description) +{ + + const char* cmdline = "prg --input input.txt \r --optimization 4 \t --opt \n option"; + + vector< string > tokens = split_unix(cmdline, " \t\n\r"); + + BOOST_REQUIRE(tokens.size() == 7); + + check_value(tokens[0], "prg"); + check_value(tokens[1], "--input"); + check_value(tokens[2], "input.txt"); + check_value(tokens[3], "--optimization"); + check_value(tokens[4], "4"); + check_value(tokens[5], "--opt"); + check_value(tokens[6], "option"); + + variables_map vm; + store(command_line_parser(tokens).options(description).run(), vm); + notify(vm); +} + +void split_equalsign(const options_description& description) +{ + + const char* cmdline = "prg --input=input.txt --optimization=4 --opt=option"; + + vector< string > tokens = split_unix(cmdline, "= "); + + BOOST_REQUIRE(tokens.size() == 7); + check_value(tokens[0], "prg"); + check_value(tokens[1], "--input"); + check_value(tokens[2], "input.txt"); + check_value(tokens[3], "--optimization"); + check_value(tokens[4], "4"); + check_value(tokens[5], "--opt"); + check_value(tokens[6], "option"); + + variables_map vm; + store(command_line_parser(tokens).options(description).run(), vm); + notify(vm); +} + +void split_semi(const options_description& description) +{ + + const char* cmdline = "prg;--input input.txt;--optimization 4;--opt option"; + + vector< string > tokens = split_unix(cmdline, "; "); + + BOOST_REQUIRE(tokens.size() == 7); + check_value(tokens[0], "prg"); + check_value(tokens[1], "--input"); + check_value(tokens[2], "input.txt"); + check_value(tokens[3], "--optimization"); + check_value(tokens[4], "4"); + check_value(tokens[5], "--opt"); + check_value(tokens[6], "option"); + + variables_map vm; + store(command_line_parser(tokens).options(description).run(), vm); + notify(vm); +} + +void split_quotes(const options_description& description) +{ + const char* cmdline = "prg --input \"input.txt input.txt\" --optimization 4 --opt \"option1 option2\""; + + vector< string > tokens = split_unix(cmdline, " "); + + BOOST_REQUIRE(tokens.size() == 7); + check_value(tokens[0], "prg"); + check_value(tokens[1], "--input"); + check_value(tokens[2], "input.txt input.txt"); + check_value(tokens[3], "--optimization"); + check_value(tokens[4], "4"); + check_value(tokens[5], "--opt"); + check_value(tokens[6], "option1 option2"); + + variables_map vm; + store(command_line_parser(tokens).options(description).run(), vm); + notify(vm); +} + +void split_escape(const options_description& description) +{ + const char* cmdline = "prg --input \\\"input.txt\\\" --optimization 4 --opt \\\"option1\\ option2\\\""; + + vector< string > tokens = split_unix(cmdline, " "); + + BOOST_REQUIRE(tokens.size() == 7); + check_value(tokens[0], "prg"); + check_value(tokens[1], "--input"); + check_value(tokens[2], "\"input.txt\""); + check_value(tokens[3], "--optimization"); + check_value(tokens[4], "4"); + check_value(tokens[5], "--opt"); + check_value(tokens[6], "\"option1 option2\""); + + variables_map vm; + store(command_line_parser(tokens).options(description).run(), vm); + notify(vm); +} + + +void split_single_quote(const options_description& description) +{ + const char* cmdline = "prg --input 'input.txt input.txt' --optimization 4 --opt 'option1 option2'"; + + vector< string > tokens = split_unix(cmdline, " ", "'"); + + BOOST_REQUIRE(tokens.size() == 7); + check_value(tokens[0], "prg"); + check_value(tokens[1], "--input"); + check_value(tokens[2], "input.txt input.txt"); + check_value(tokens[3], "--optimization"); + check_value(tokens[4], "4"); + check_value(tokens[5], "--opt"); + check_value(tokens[6], "option1 option2"); + + variables_map vm; + store(command_line_parser(tokens).options(description).run(), vm); + notify(vm); +} + +void split_defaults(const options_description& description) +{ + const char* cmdline = "prg --input \t \'input file.txt\' \t --optimization 4 --opt \\\"option1\\ option2\\\""; + + vector< string > tokens = split_unix(cmdline); + + BOOST_REQUIRE(tokens.size() == 7); + check_value(tokens[0], "prg"); + check_value(tokens[1], "--input"); + check_value(tokens[2], "input file.txt"); + check_value(tokens[3], "--optimization"); + check_value(tokens[4], "4"); + check_value(tokens[5], "--opt"); + check_value(tokens[6], "\"option1 option2\""); + + variables_map vm; + store(command_line_parser(tokens).options(description).run(), vm); + notify(vm); +} + +int main(int /*ac*/, char** /*av*/) +{ + options_description desc; + desc.add_options() + ("input,i", value<string>(), "the input file") + ("optimization,O", value<unsigned>(), "optimization level") + ("opt,o", value<string>(), "misc option") + ; + + split_whitespace(desc); + split_equalsign(desc); + split_semi(desc); + split_quotes(desc); + split_escape(desc); + split_single_quote(desc); + split_defaults(desc); + + return 0; +} diff --git a/src/boost/libs/program_options/test/test_convert.cpp b/src/boost/libs/program_options/test/test_convert.cpp new file mode 100644 index 00000000..f03a19eb --- /dev/null +++ b/src/boost/libs/program_options/test/test_convert.cpp @@ -0,0 +1,146 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include <cstring> +#include <cassert> +#include <string> +#include <fstream> +#include <sstream> +#include <iostream> +#include <boost/progress.hpp> +#include <boost/bind.hpp> +#include <boost/ref.hpp> + +#include <boost/program_options/detail/convert.hpp> +#include <boost/program_options/detail/utf8_codecvt_facet.hpp> + +#include "minitest.hpp" + +using namespace std; + +string file_content(const string& filename) +{ + ifstream ifs(filename.c_str()); + assert(ifs); + + stringstream ss; + ss << ifs.rdbuf(); + + return ss.str(); +} + +// A version of from_8_bit which does not use functional object, for +// performance comparison. +std::wstring from_8_bit_2(const std::string& s, + const codecvt<wchar_t, char, mbstate_t>& cvt) +{ + std::wstring result; + + + std::mbstate_t state = std::mbstate_t(); + + const char* from = s.data(); + const char* from_end = s.data() + s.size(); + // The interace of cvt is not really iterator-like, and it's + // not possible the tell the required output size without the conversion. + // All we can is convert data by pieces. + while(from != from_end) { + + // std::basic_string does not provide non-const pointers to the data, + // so converting directly into string is not possible. + wchar_t buffer[32]; + + wchar_t* to_next = buffer; + // Try to convert remaining input. + std::codecvt_base::result r = + cvt.in(state, from, from_end, from, buffer, buffer + 32, to_next); + + if (r == std::codecvt_base::error) + throw logic_error("character conversion failed"); + // 'partial' is not an error, it just means not all source characters + // we converted. However, we need to check that at least one new target + // character was produced. If not, it means the source data is + // incomplete, and since we don't have extra data to add to source, it's + // error. + if (to_next == buffer) + throw logic_error("character conversion failed"); + + // Add converted characters + result.append(buffer, to_next); + } + + return result; +} + + +void test_convert(const std::string& input, + const std::string& expected_output) +{ + boost::program_options::detail::utf8_codecvt_facet facet; + + std::wstring output; + { + boost::progress_timer t; + for (int i = 0; i < 10000; ++i) + output = boost::from_8_bit( + input, + facet); + } + + { + boost::progress_timer t; + for (int i = 0; i < 10000; ++i) + output = from_8_bit_2( + input, + facet); + } + + BOOST_CHECK(output.size()*2 == expected_output.size()); + + for(unsigned i = 0; i < output.size(); ++i) { + + { + unsigned low = output[i]; + low &= 0xFF; + unsigned low2 = expected_output[2*i]; + low2 &= 0xFF; + BOOST_CHECK(low == low2); + } + { + unsigned high = output[i]; + high >>= 8; + high &= 0xFF; + unsigned high2 = expected_output[2*i+1]; + BOOST_CHECK(high == high2); + } + } + + string ref = boost::to_8_bit(output, facet); + + BOOST_CHECK(ref == input); +} + +int main(int ac, char* av[]) +{ + std::string input = file_content("utf8.txt"); + std::string expected = file_content("ucs2.txt"); + + test_convert(input, expected); + + if (ac > 1) { + cout << "Trying to convert the command line argument\n"; + + locale::global(locale("")); + std::wstring w = boost::from_local_8_bit(av[1]); + + cout << "Got something, printing decimal code point values\n"; + for (unsigned i = 0; i < w.size(); ++i) { + cout << (unsigned)w[i] << "\n"; + } + + } + + return 0; +} diff --git a/src/boost/libs/program_options/test/ucs2.txt b/src/boost/libs/program_options/test/ucs2.txt Binary files differnew file mode 100644 index 00000000..c4c0fe72 --- /dev/null +++ b/src/boost/libs/program_options/test/ucs2.txt diff --git a/src/boost/libs/program_options/test/unicode_test.cpp b/src/boost/libs/program_options/test/unicode_test.cpp new file mode 100644 index 00000000..97d19f0b --- /dev/null +++ b/src/boost/libs/program_options/test/unicode_test.cpp @@ -0,0 +1,164 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options/variables_map.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/detail/utf8_codecvt_facet.hpp> +using namespace boost::program_options; +// We'll use po::value everywhere to workaround vc6 bug. +namespace po = boost::program_options; + +#include <boost/function.hpp> +using namespace boost; + +#include <sstream> +using namespace std; + +#include "minitest.hpp" + +// Test that unicode input is forwarded to unicode option without +// problems. +void test_unicode_to_unicode() +{ + options_description desc; + + desc.add_options() + ("foo", po::wvalue<wstring>(), "unicode option") + ; + + vector<wstring> args; + args.push_back(L"--foo=\x044F"); + + variables_map vm; + basic_parsed_options<wchar_t> parsed = + wcommand_line_parser(args).options(desc).run(); + store(parsed, vm); + + BOOST_CHECK(vm["foo"].as<wstring>() == L"\x044F"); + BOOST_CHECK(parsed.options[0].original_tokens.size() == 1); + BOOST_CHECK(parsed.options[0].original_tokens[0] == L"--foo=\x044F"); +} + +// Test that unicode input is property converted into +// local 8 bit string. To test this, make local 8 bit encoding +// be utf8. +void test_unicode_to_native() +{ + std::codecvt<wchar_t, char, mbstate_t>* facet = + new boost::program_options::detail::utf8_codecvt_facet; + locale::global(locale(locale(), facet)); + + options_description desc; + + desc.add_options() + ("foo", po::value<string>(), "unicode option") + ; + + vector<wstring> args; + args.push_back(L"--foo=\x044F"); + + variables_map vm; + store(wcommand_line_parser(args).options(desc).run(), vm); + + BOOST_CHECK(vm["foo"].as<string>() == "\xD1\x8F"); +} + +void test_native_to_unicode() +{ + std::codecvt<wchar_t, char, mbstate_t>* facet = + new boost::program_options::detail::utf8_codecvt_facet; + locale::global(locale(locale(), facet)); + + options_description desc; + + desc.add_options() + ("foo", po::wvalue<wstring>(), "unicode option") + ; + + vector<string> args; + args.push_back("--foo=\xD1\x8F"); + + variables_map vm; + store(command_line_parser(args).options(desc).run(), vm); + + BOOST_CHECK(vm["foo"].as<wstring>() == L"\x044F"); +} + +vector<wstring> sv(const wchar_t* array[], unsigned size) +{ + vector<wstring> r; + for (unsigned i = 0; i < size; ++i) + r.push_back(array[i]); + return r; +} + +void check_value(const woption& option, const char* name, const wchar_t* value) +{ + BOOST_CHECK(option.string_key == name); + BOOST_REQUIRE(option.value.size() == 1); + BOOST_CHECK(option.value.front() == value); +} + +void test_command_line() +{ + options_description desc; + desc.add_options() + ("foo,f", new untyped_value(), "") + // Explicit qualification is a workaround for vc6 + ("bar,b", po::value<std::string>(), "") + ("baz", new untyped_value()) + ("qux,plug*", new untyped_value()) + ; + + const wchar_t* cmdline4_[] = { L"--foo=1\u0FF52", L"-f4", L"--bar=11", + L"-b4", L"--plug3=10"}; + vector<wstring> cmdline4 = sv(cmdline4_, + sizeof(cmdline4_)/sizeof(cmdline4_[0])); + vector<woption> a4 = + wcommand_line_parser(cmdline4).options(desc).run().options; + + BOOST_REQUIRE(a4.size() == 5); + + check_value(a4[0], "foo", L"1\u0FF52"); + check_value(a4[1], "foo", L"4"); + check_value(a4[2], "bar", L"11"); + check_value(a4[4], "qux", L"10"); +} + +// Since we've already tested conversion between parser encoding and +// option encoding, all we need to check for config file is that +// when reading wistream, it generates proper UTF8 data. +void test_config_file() +{ + std::codecvt<wchar_t, char, mbstate_t>* facet = + new boost::program_options::detail::utf8_codecvt_facet; + locale::global(locale(locale(), facet)); + + options_description desc; + + desc.add_options() + ("foo", po::value<string>(), "unicode option") + ; + + std::wstringstream stream(L"foo = \x044F"); + + variables_map vm; + store(parse_config_file(stream, desc), vm); + + BOOST_CHECK(vm["foo"].as<string>() == "\xD1\x8F"); +} + +int main(int, char* []) +{ + test_unicode_to_unicode(); + test_unicode_to_native(); + test_native_to_unicode(); + test_command_line(); + test_config_file(); + return 0; +} + diff --git a/src/boost/libs/program_options/test/unrecognized_test.cpp b/src/boost/libs/program_options/test/unrecognized_test.cpp new file mode 100644 index 00000000..7dfb3805 --- /dev/null +++ b/src/boost/libs/program_options/test/unrecognized_test.cpp @@ -0,0 +1,88 @@ +// Copyright Sascha Ochsenknecht 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options/cmdline.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/detail/cmdline.hpp> +using namespace boost::program_options; +using boost::program_options::detail::cmdline; + +#include <iostream> +#include <sstream> +#include <vector> +#include <cassert> +using namespace std; + +#include "minitest.hpp" + + +// Test free function collect_unrecognized() +// +// it collects the tokens of all not registered options. It can be used +// to pass them to an own parser implementation + + + +void test_unrecognize_cmdline() +{ + options_description desc; + + string content = "prg --input input.txt --optimization 4 --opt option"; + vector< string > tokens = split_unix(content); + + cmdline cmd(tokens); + cmd.set_options_description(desc); + cmd.allow_unregistered(); + + vector< option > opts = cmd.run(); + vector< string > result = collect_unrecognized(opts, include_positional); + + BOOST_CHECK_EQUAL(result.size(), 7); + BOOST_CHECK_EQUAL(result[0], "prg"); + BOOST_CHECK_EQUAL(result[1], "--input"); + BOOST_CHECK_EQUAL(result[2], "input.txt"); + BOOST_CHECK_EQUAL(result[3], "--optimization"); + BOOST_CHECK_EQUAL(result[4], "4"); + BOOST_CHECK_EQUAL(result[5], "--opt"); + BOOST_CHECK_EQUAL(result[6], "option"); +} + + + +void test_unrecognize_config() +{ + + options_description desc; + + string content = + " input = input.txt\n" + " optimization = 4\n" + " opt = option\n" + ; + + stringstream ss(content); + vector< option > opts = parse_config_file(ss, desc, true).options; + vector< string > result = collect_unrecognized(opts, include_positional); + + BOOST_CHECK_EQUAL(result.size(), 6); + BOOST_CHECK_EQUAL(result[0], "input"); + BOOST_CHECK_EQUAL(result[1], "input.txt"); + BOOST_CHECK_EQUAL(result[2], "optimization"); + BOOST_CHECK_EQUAL(result[3], "4"); + BOOST_CHECK_EQUAL(result[4], "opt"); + BOOST_CHECK_EQUAL(result[5], "option"); +} + + + +int main(int /*ac*/, char** /*av*/) +{ + test_unrecognize_cmdline(); + test_unrecognize_config(); + + return 0; +} diff --git a/src/boost/libs/program_options/test/utf8.txt b/src/boost/libs/program_options/test/utf8.txt Binary files differnew file mode 100644 index 00000000..d97d3601 --- /dev/null +++ b/src/boost/libs/program_options/test/utf8.txt diff --git a/src/boost/libs/program_options/test/variable_map_test.cpp b/src/boost/libs/program_options/test/variable_map_test.cpp new file mode 100644 index 00000000..04b43473 --- /dev/null +++ b/src/boost/libs/program_options/test/variable_map_test.cpp @@ -0,0 +1,290 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include <boost/program_options/variables_map.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/parsers.hpp> +#include <boost/program_options/detail/utf8_codecvt_facet.hpp> +using namespace boost::program_options; +// We'll use po::value everywhere to workaround vc6 bug. +namespace po = boost::program_options; + +#include <boost/function.hpp> +using namespace boost; + +#include <sstream> +using namespace std; + +#include "minitest.hpp" + +vector<string> sv(const char* array[], unsigned size) +{ + vector<string> r; + for (unsigned i = 0; i < size; ++i) + r.push_back(array[i]); + return r; +} + +void test_variable_map() +{ + options_description desc; + desc.add_options() + ("foo,f", new untyped_value) + ("bar,b", po::value<string>()) + ("biz,z", po::value<string>()) + ("baz", new untyped_value()) + ("output,o", new untyped_value(), "") + ; + const char* cmdline3_[] = { "--foo='12'", "--bar=11", "-z3", "-ofoo" }; + vector<string> cmdline3 = sv(cmdline3_, + sizeof(cmdline3_)/sizeof(const char*)); + parsed_options a3 = command_line_parser(cmdline3).options(desc).run(); + variables_map vm; + store(a3, vm); + notify(vm); + BOOST_REQUIRE(vm.size() == 4); + BOOST_CHECK(vm["foo"].as<string>() == "'12'"); + BOOST_CHECK(vm["bar"].as<string>() == "11"); + BOOST_CHECK(vm.count("biz") == 1); + BOOST_CHECK(vm["biz"].as<string>() == "3"); + BOOST_CHECK(vm["output"].as<string>() == "foo"); + + int i; + desc.add_options() + ("zee", bool_switch(), "") + ("zak", po::value<int>(&i), "") + ("opt", bool_switch(), ""); + + const char* cmdline4_[] = { "--zee", "--zak=13" }; + vector<string> cmdline4 = sv(cmdline4_, + sizeof(cmdline4_)/sizeof(const char*)); + parsed_options a4 = command_line_parser(cmdline4).options(desc).run(); + + variables_map vm2; + store(a4, vm2); + notify(vm2); + BOOST_REQUIRE(vm2.size() == 3); + BOOST_CHECK(vm2["zee"].as<bool>() == true); + BOOST_CHECK(vm2["zak"].as<int>() == 13); + BOOST_CHECK(vm2["opt"].as<bool>() == false); + BOOST_CHECK(i == 13); + + options_description desc2; + desc2.add_options() + ("vee", po::value<string>()->default_value("42")) + ("voo", po::value<string>()) + ("iii", po::value<int>()->default_value(123)) + ; + const char* cmdline5_[] = { "--voo=1" }; + vector<string> cmdline5 = sv(cmdline5_, + sizeof(cmdline5_)/sizeof(const char*)); + parsed_options a5 = command_line_parser(cmdline5).options(desc2).run(); + + variables_map vm3; + store(a5, vm3); + notify(vm3); + BOOST_REQUIRE(vm3.size() == 3); + BOOST_CHECK(vm3["vee"].as<string>() == "42"); + BOOST_CHECK(vm3["voo"].as<string>() == "1"); + BOOST_CHECK(vm3["iii"].as<int>() == 123); + + options_description desc3; + desc3.add_options() + ("imp", po::value<int>()->implicit_value(100)) + ("iim", po::value<int>()->implicit_value(200)->default_value(201)) + ("mmp,m", po::value<int>()->implicit_value(123)->default_value(124)) + ("foo", po::value<int>()) + ; + /* The -m option is implicit. It does not have value in inside the token, + and we should not grab the next token. */ + const char* cmdline6_[] = { "--imp=1", "-m", "--foo=1" }; + vector<string> cmdline6 = sv(cmdline6_, + sizeof(cmdline6_)/sizeof(const char*)); + parsed_options a6 = command_line_parser(cmdline6).options(desc3).run(); + + variables_map vm4; + store(a6, vm4); + notify(vm4); + BOOST_REQUIRE(vm4.size() == 4); + BOOST_CHECK(vm4["imp"].as<int>() == 1); + BOOST_CHECK(vm4["iim"].as<int>() == 201); + BOOST_CHECK(vm4["mmp"].as<int>() == 123); +} + +int stored_value; +void notifier(const vector<int>& v) +{ + stored_value = v.front(); +} + +void test_semantic_values() +{ + options_description desc; + desc.add_options() + ("foo", new untyped_value()) + ("bar", po::value<int>()) + ("biz", po::value< vector<string> >()) + ("baz", po::value< vector<string> >()->multitoken()) + ("int", po::value< vector<int> >()->notifier(¬ifier)) + ; + + + parsed_options parsed(&desc); + vector<option>& options = parsed.options; + vector<string> v; + v.push_back("q"); + options.push_back(option("foo", vector<string>(1, "1"))); + options.push_back(option("biz", vector<string>(1, "a"))); + options.push_back(option("baz", v)); + options.push_back(option("bar", vector<string>(1, "1"))); + options.push_back(option("biz", vector<string>(1, "b x"))); + v.push_back("w"); + options.push_back(option("baz", v)); + + variables_map vm; + store(parsed, vm); + notify(vm); + BOOST_REQUIRE(vm.count("biz") == 1); + BOOST_REQUIRE(vm.count("baz") == 1); + const vector<string> av = vm["biz"].as< vector<string> >(); + const vector<string> av2 = vm["baz"].as< vector<string> >(); + string exp1[] = { "a", "b x" }; + BOOST_CHECK(av == vector<string>(exp1, exp1 + 2)); + string exp2[] = { "q", "q", "w" }; + BOOST_CHECK(av2 == vector<string>(exp2, exp2 + 3)); + + options.push_back(option("int", vector<string>(1, "13"))); + + variables_map vm2; + store(parsed, vm2); + notify(vm2); + BOOST_REQUIRE(vm2.count("int") == 1); + BOOST_CHECK(vm2["int"].as< vector<int> >() == vector<int>(1, 13)); + BOOST_CHECK_EQUAL(stored_value, 13); + + vector<option> saved_options = options; + + options.push_back(option("bar", vector<string>(1, "2"))); + variables_map vm3; + BOOST_CHECK_THROW(store(parsed, vm3), multiple_occurrences); + + options = saved_options; + // Now try passing two int in one 'argv' element. + // This should not work. + options.push_back(option("int", vector<string>(1, "2 3"))); + variables_map vm4; + BOOST_CHECK_THROW(store(parsed, vm4), validation_error); +} + +void test_priority() +{ + options_description desc; + desc.add_options() + // Value of this option will be specified in two sources, + // and only first one should be used. + ("first", po::value< vector<int > >()) + // Value of this option will have default value in the first source, + // and explicit assignment in the second, so the second should be used. + ("second", po::value< vector<int > >()->default_value(vector<int>(1, 1), "")) + ("aux", po::value< vector<int > >()) + // This will have values in both sources, and values should be combined + ("include", po::value< vector<int> >()->composing()) + ; + + const char* cmdline1_[] = { "--first=1", "--aux=10", "--first=3", "--include=1" }; + vector<string> cmdline1 = sv(cmdline1_, + sizeof(cmdline1_)/sizeof(const char*)); + + parsed_options p1 = command_line_parser(cmdline1).options(desc).run(); + + const char* cmdline2_[] = { "--first=12", "--second=7", "--include=7" }; + vector<string> cmdline2 = sv(cmdline2_, + sizeof(cmdline2_)/sizeof(const char*)); + + parsed_options p2 = command_line_parser(cmdline2).options(desc).run(); + + variables_map vm; + store(p1, vm); + + BOOST_REQUIRE(vm.count("first") == 1); + BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2); + BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1); + BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3); + + BOOST_REQUIRE(vm.count("second") == 1); + BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1); + BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 1); + + store(p2, vm); + + // Value should not change. + BOOST_REQUIRE(vm.count("first") == 1); + BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2); + BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1); + BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3); + + // Value should change to 7 + BOOST_REQUIRE(vm.count("second") == 1); + BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1); + BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 7); + + BOOST_REQUIRE(vm.count("include") == 1); + BOOST_REQUIRE(vm["include"].as< vector<int> >().size() == 2); + BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[0], 1); + BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[1], 7); +} + +void test_multiple_assignments_with_different_option_description() +{ + // Test that if we store option twice into the same variable_map, + // and some of the options stored the first time are not present + // in the options descrription provided the second time, we don't crash. + + options_description desc1(""); + desc1.add_options() + ("help,h", "") + ("includes", po::value< vector<string> >()->composing(), ""); + ; + + options_description desc2(""); + desc2.add_options() + ("output,o", ""); + + vector<string> input1; + input1.push_back("--help"); + input1.push_back("--includes=a"); + parsed_options p1 = command_line_parser(input1).options(desc1).run(); + + vector<string> input2; + input1.push_back("--output"); + parsed_options p2 = command_line_parser(input2).options(desc2).run(); + + vector<string> input3; + input3.push_back("--includes=b"); + parsed_options p3 = command_line_parser(input3).options(desc1).run(); + + + variables_map vm; + store(p1, vm); + store(p2, vm); + store(p3, vm); + + BOOST_REQUIRE(vm.count("help") == 1); + BOOST_REQUIRE(vm.count("includes") == 1); + BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[0], "a"); + BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[1], "b"); + +} + +int main(int, char* []) +{ + test_variable_map(); + test_semantic_values(); + test_priority(); + test_multiple_assignments_with_different_option_description(); + return 0; +} + diff --git a/src/boost/libs/program_options/test/winmain.cpp b/src/boost/libs/program_options/test/winmain.cpp new file mode 100644 index 00000000..0c3512f9 --- /dev/null +++ b/src/boost/libs/program_options/test/winmain.cpp @@ -0,0 +1,74 @@ +// Copyright Vladimir Prus 2002-2004. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if defined(_WIN32) +#include <string> +#include <vector> +#include <cctype> +#include <iostream> +#include <stdlib.h> + +using namespace std; + +#include <boost/program_options/parsers.hpp> +using namespace boost::program_options; + +void check_equal(const std::vector<string>& actual, char **expected, int n) +{ + if (actual.size() != n) + { + std::cerr << "Size mismatch between expected and actual data\n"; + abort(); + } + for (int i = 0; i < n; ++i) + { + if (actual[i] != expected[i]) + { + std::cerr << "Unexpected content\n"; + abort(); + } + } +} + +#include <boost/preprocessor/cat.hpp> + +void test_winmain() +{ + +#define C , +#define TEST(input, expected) \ + char* BOOST_PP_CAT(e, __LINE__)[] = expected;\ + vector<string> BOOST_PP_CAT(v, __LINE__) = split_winmain(input);\ + check_equal(BOOST_PP_CAT(v, __LINE__), BOOST_PP_CAT(e, __LINE__),\ + sizeof(BOOST_PP_CAT(e, __LINE__))/sizeof(char*)); + +// The following expectations were obtained in Win2000 shell: + TEST("1 ", {"1"}); + TEST("1\"2\" ", {"12"}); + TEST("1\"2 ", {"12 "}); + TEST("1\"\\\"2\" ", {"1\"2"}); + TEST("\"1\" \"2\" ", {"1" C "2"}); + TEST("1\\\" ", {"1\""}); + TEST("1\\\\\" ", {"1\\ "}); + TEST("1\\\\\\\" ", {"1\\\""}); + TEST("1\\\\\\\\\" ", {"1\\\\ "}); + + TEST("1\" 1 ", {"1 1 "}); + TEST("1\\\" 1 ", {"1\"" C "1"}); + TEST("1\\1 ", {"1\\1"}); + TEST("1\\\\1 ", {"1\\\\1"}); +} + +int main(int, char*[]) +{ + test_winmain(); + return 0; +} +#else +int main(int, char*[]) +{ + return 0; +} +#endif diff --git a/src/boost/libs/program_options/test/winmain.py b/src/boost/libs/program_options/test/winmain.py new file mode 100644 index 00000000..5c43242e --- /dev/null +++ b/src/boost/libs/program_options/test/winmain.py @@ -0,0 +1,39 @@ + +from StringIO import StringIO +import string + +testcases = r"""1 -> 1 +1"2" -> 12 +1"2 -> 12 +1"\"2" -> 1"2 +"1" "2" -> 1, 2 +1\" -> 1" +1\\" -> 1\ +1\\\" -> 1\" +1\\\\" -> 1\\ +1" 1 -> 1 1 +1\" 1 -> 1", 1 +1\1 -> 1\1 +1\\1 -> 1\\1 +""" + +#testcases = r"""1\\\\" -> 1\\ +#""" + +t = StringIO(testcases) + +def quote(s): + result = s.replace("\\", r"\\") + result = result.replace("\"", "\\\"") + return '"' + result + '"' + + +for s in t: + s = string.strip(s) + (value, result) = string.split(s, "->") +# print value, result + tokens = string.split(result, ",") + value = quote(value) + tokens = map(string.strip, tokens) + tokens = map(quote, tokens) + print "TEST(%s, {%s});" % (value, string.join(tokens, ",")) |