yipo
7/6/2015 - 4:20 PM

To split command-line arguments with double quotes.

To split command-line arguments with double quotes.

#include "split_cmdline_args.hpp"
#include <assert.h>
using namespace std;

void case_empty()
{
    assert(split_cmdline_args(string()) == deque<string>());
    assert(split_cmdline_args("") == deque<string>());

    assert(split_cmdline_args(wstring()) == deque<wstring>());
    assert(split_cmdline_args(L"") == deque<wstring>());
}

void case_basic()
{
    assert(split_cmdline_args("   foo    bar \n\t baz  ") == deque<string>({ "foo", "bar", "baz" }));
    assert(split_cmdline_args("fo\"oo\"o \"ba\"r ba\"z\" \"qu x\" \"q u\"\"u x\"") == deque<string>({ "fo\"oo\"o", "\"ba\"r", "ba\"z\"", "\"qu x\"", "\"q u\"\"u x\"" }));

    assert(split_cmdline_args(L"とある 科学の \"超\"電磁砲") == deque<wstring>({ L"とある", L"科学の", L"\"超\"電磁砲" }));
    // Have to save in 'UTF-8 with BOM' to compile by MSVC. :( -- ref. https://msdn.microsoft.com/en-us/library/xwy0e8f2
    // However, the Unicode Standard neither requires nor recommends the use of the BOM for UTF-8.
}

void case_misc()
{
    for (auto i : split_cmdline_args("foo bar"));
}

int main()
{
    case_empty();
    case_basic();
    case_misc();

    return 0;
}

#pragma once

#include <string>
#include <deque>
#include <locale> // Use isspace template instead of isspace/iswspace in <cctype>.

template<typename T>
std::deque<std::basic_string<T>> split_cmdline_args(const std::basic_string<T> &cmd)
{
    using namespace std;
    typedef basic_string<T> string_type;

    auto in = cmd.begin();
    deque<string_type> out;

    auto has_next = [&]() -> bool
    {
        return in != cmd.end();
    };
    locale loc;

    while (has_next())
    {
        while (has_next() && isspace(*in, loc)) in++;

        string_type token;
        while (has_next() && !isspace(*in, loc))
        {
            if (*in == '"')
            {
                token.push_back(*in++);
                while (has_next())
                {
                    token.push_back(*in++);
                    if (token.back() == '"') break;
                }
            }
            else
            {
                token.push_back(*in++);
            }
        }

        if (token.size() > 0) out.push_back(token);
    }

    return out;
}

template<typename T>
inline std::deque<std::basic_string<T>> split_cmdline_args(const T *cmd)
{
    return split_cmdline_args(std::basic_string<T>(cmd));
}

cmake_minimum_required(VERSION 3.1)

project(split_cmdline_args)

add_executable(${PROJECT_NAME} split_cmdline_args.hpp split_cmdline_args_test.cpp)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)

@ECHO OFF
CD /D %~dp0

PATH ^
%ProgramFiles%\CMake\bin;^
%ProgramFiles(x86)%\CMake\bin;^
C:\MinGW\bin

IF NOT EXIST build MKDIR build
CD build

cmake %* ..
PAUSE