Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
apache
Incubator Pegasus
提交
034543f4
Incubator Pegasus
项目概览
apache
/
Incubator Pegasus
通知
9
Star
5
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Incubator Pegasus
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
034543f4
编写于
2月 07, 2020
作者:
W
Wu Tao
提交者:
neverchanje
3月 31, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat(shell): add dependency argh to parse options and arguments (#470)
上级
cbf7f349
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
446 addition
and
0 deletion
+446
-0
src/shell/argh.h
src/shell/argh.h
+446
-0
未找到文件。
src/shell/argh.h
0 → 100644
浏览文件 @
034543f4
// This file is copied from https://github.com/adishavit/argh v1.3.1 release.
//
// Copyright (c) 2016, Adi Shavit
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <cassert>
namespace
argh
{
// Terminology:
// A command line is composed of 2 types of args:
// 1. Positional args, i.e. free standing values
// 2. Options: args beginning with '-'. We identify two kinds:
// 2.1: Flags: boolean options => (exist ? true : false)
// 2.2: Parameters: a name followed by a non-option value
#if !defined(__GNUC__) || (__GNUC__ >= 5)
using
string_stream
=
std
::
istringstream
;
#else
// Until GCC 5, istringstream did not have a move constructor.
// stringstream_proxy is used instead, as a workaround.
class
stringstream_proxy
{
public:
stringstream_proxy
()
=
default
;
// Construct with a value.
stringstream_proxy
(
std
::
string
const
&
value
)
:
stream_
(
value
)
{}
// Copy constructor.
stringstream_proxy
(
const
stringstream_proxy
&
other
)
:
stream_
(
other
.
stream_
.
str
())
{
stream_
.
setstate
(
other
.
stream_
.
rdstate
());
}
void
setstate
(
std
::
ios_base
::
iostate
state
)
{
stream_
.
setstate
(
state
);
}
// Stream out the value of the parameter.
// If the conversion was not possible, the stream will enter the fail state,
// and operator bool will return false.
template
<
typename
T
>
stringstream_proxy
&
operator
>>
(
T
&
thing
)
{
stream_
>>
thing
;
return
*
this
;
}
// Get the string value.
std
::
string
str
()
const
{
return
stream_
.
str
();
}
std
::
stringbuf
*
rdbuf
()
const
{
return
stream_
.
rdbuf
();
}
// Check the state of the stream.
// False when the most recent stream operation failed
operator
bool
()
const
{
return
!!
stream_
;
}
~
stringstream_proxy
()
=
default
;
private:
std
::
istringstream
stream_
;
};
using
string_stream
=
stringstream_proxy
;
#endif
class
parser
{
public:
enum
Mode
{
PREFER_FLAG_FOR_UNREG_OPTION
=
1
<<
0
,
PREFER_PARAM_FOR_UNREG_OPTION
=
1
<<
1
,
NO_SPLIT_ON_EQUALSIGN
=
1
<<
2
,
SINGLE_DASH_IS_MULTIFLAG
=
1
<<
3
,
};
parser
()
=
default
;
parser
(
std
::
initializer_list
<
char
const
*
const
>
pre_reg_names
)
{
add_params
(
pre_reg_names
);
}
parser
(
const
char
*
const
argv
[],
int
mode
=
PREFER_FLAG_FOR_UNREG_OPTION
)
{
parse
(
argv
,
mode
);
}
parser
(
int
argc
,
const
char
*
const
argv
[],
int
mode
=
PREFER_FLAG_FOR_UNREG_OPTION
)
{
parse
(
argc
,
argv
,
mode
);
}
void
add_param
(
std
::
string
const
&
name
);
void
add_params
(
std
::
initializer_list
<
char
const
*
const
>
init_list
);
void
parse
(
const
char
*
const
argv
[],
int
mode
=
PREFER_FLAG_FOR_UNREG_OPTION
);
void
parse
(
int
argc
,
const
char
*
const
argv
[],
int
mode
=
PREFER_FLAG_FOR_UNREG_OPTION
);
std
::
multiset
<
std
::
string
>
const
&
flags
()
const
{
return
flags_
;
}
std
::
map
<
std
::
string
,
std
::
string
>
const
&
params
()
const
{
return
params_
;
}
std
::
vector
<
std
::
string
>
const
&
pos_args
()
const
{
return
pos_args_
;
}
// begin() and end() for using range-for over positional args.
std
::
vector
<
std
::
string
>::
const_iterator
begin
()
const
{
return
pos_args_
.
cbegin
();
}
std
::
vector
<
std
::
string
>::
const_iterator
end
()
const
{
return
pos_args_
.
cend
();
}
size_t
size
()
const
{
return
pos_args_
.
size
();
}
//////////////////////////////////////////////////////////////////////////
// Accessors
// flag (boolean) accessors: return true if the flag appeared, otherwise false.
bool
operator
[](
std
::
string
const
&
name
)
const
;
// multiple flag (boolean) accessors: return true if at least one of the flag appeared,
// otherwise false.
bool
operator
[](
std
::
initializer_list
<
char
const
*
const
>
init_list
)
const
;
// returns positional arg string by order. Like argv[] but without the options
std
::
string
const
&
operator
[](
size_t
ind
)
const
;
// returns a std::istream that can be used to convert a positional arg to a typed value.
string_stream
operator
()(
size_t
ind
)
const
;
// same as above, but with a default value in case the arg is missing (index out of range).
template
<
typename
T
>
string_stream
operator
()(
size_t
ind
,
T
&&
def_val
)
const
;
// parameter accessors, give a name get an std::istream that can be used to convert to a typed
// value. call .str() on result to get as string
string_stream
operator
()(
std
::
string
const
&
name
)
const
;
// accessor for a parameter with multiple names, give a list of names, get an std::istream that
// can be used to convert to a typed value. call .str() on result to get as string returns the
// first value in the list to be found.
string_stream
operator
()(
std
::
initializer_list
<
char
const
*
const
>
init_list
)
const
;
// same as above, but with a default value in case the param was missing.
// Non-string def_val types must have an operator<<() (output stream operator)
// If T only has an input stream operator, pass the string version of the type as in "3" instead
// of 3.
template
<
typename
T
>
string_stream
operator
()(
std
::
string
const
&
name
,
T
&&
def_val
)
const
;
// same as above but for a list of names. returns the first value to be found.
template
<
typename
T
>
string_stream
operator
()(
std
::
initializer_list
<
char
const
*
const
>
init_list
,
T
&&
def_val
)
const
;
private:
string_stream
bad_stream
()
const
;
std
::
string
trim_leading_dashes
(
std
::
string
const
&
name
)
const
;
bool
is_number
(
std
::
string
const
&
arg
)
const
;
bool
is_option
(
std
::
string
const
&
arg
)
const
;
bool
got_flag
(
std
::
string
const
&
name
)
const
;
bool
is_param
(
std
::
string
const
&
name
)
const
;
private:
std
::
vector
<
std
::
string
>
args_
;
std
::
map
<
std
::
string
,
std
::
string
>
params_
;
std
::
vector
<
std
::
string
>
pos_args_
;
std
::
multiset
<
std
::
string
>
flags_
;
std
::
set
<
std
::
string
>
registeredParams_
;
std
::
string
empty_
;
};
//////////////////////////////////////////////////////////////////////////
inline
void
parser
::
parse
(
const
char
*
const
argv
[],
int
mode
)
{
int
argc
=
0
;
for
(
auto
argvp
=
argv
;
*
argvp
;
++
argc
,
++
argvp
)
;
parse
(
argc
,
argv
,
mode
);
}
//////////////////////////////////////////////////////////////////////////
inline
void
parser
::
parse
(
int
argc
,
const
char
*
const
argv
[],
int
mode
/*= PREFER_FLAG_FOR_UNREG_OPTION*/
)
{
// convert to strings
args_
.
resize
(
argc
);
std
::
transform
(
argv
,
argv
+
argc
,
args_
.
begin
(),
[](
const
char
*
const
arg
)
{
return
arg
;
});
// parse line
for
(
auto
i
=
0u
;
i
<
args_
.
size
();
++
i
)
{
if
(
!
is_option
(
args_
[
i
]))
{
pos_args_
.
emplace_back
(
args_
[
i
]);
continue
;
}
auto
name
=
trim_leading_dashes
(
args_
[
i
]);
if
(
!
(
mode
&
NO_SPLIT_ON_EQUALSIGN
))
{
auto
equalPos
=
name
.
find
(
'='
);
if
(
equalPos
!=
std
::
string
::
npos
)
{
params_
.
insert
({
name
.
substr
(
0
,
equalPos
),
name
.
substr
(
equalPos
+
1
)});
continue
;
}
}
// if the option is unregistered and should be a multi-flag
if
(
1
==
(
args_
[
i
].
size
()
-
name
.
size
())
&&
// single dash
argh
::
parser
::
SINGLE_DASH_IS_MULTIFLAG
&
mode
&&
// multi-flag mode
!
is_param
(
name
))
// unregistered
{
std
::
string
keep_param
;
if
(
!
name
.
empty
()
&&
is_param
(
std
::
string
(
1ul
,
name
.
back
())))
// last char is param
{
keep_param
+=
name
.
back
();
name
.
resize
(
name
.
size
()
-
1
);
}
for
(
auto
const
&
c
:
name
)
{
flags_
.
emplace
(
std
::
string
{
c
});
}
if
(
!
keep_param
.
empty
())
{
name
=
keep_param
;
}
else
{
continue
;
// do not consider other options for this arg
}
}
// any potential option will get as its value the next arg, unless that arg is an option too
// in that case it will be determined a flag.
if
(
i
==
args_
.
size
()
-
1
||
is_option
(
args_
[
i
+
1
]))
{
flags_
.
emplace
(
name
);
continue
;
}
// if 'name' is a pre-registered option, then the next arg cannot be a free parameter to it
// is skipped otherwise we have 2 modes: PREFER_FLAG_FOR_UNREG_OPTION: a non-registered
// 'name' is determined a flag.
// The following value (the next arg) will be a free
// parameter.
//
// PREFER_PARAM_FOR_UNREG_OPTION: a non-registered 'name' is determined a parameter, the
// next arg
// will be the value of that option.
assert
(
!
(
mode
&
argh
::
parser
::
PREFER_FLAG_FOR_UNREG_OPTION
)
||
!
(
mode
&
argh
::
parser
::
PREFER_PARAM_FOR_UNREG_OPTION
));
bool
preferParam
=
mode
&
argh
::
parser
::
PREFER_PARAM_FOR_UNREG_OPTION
;
if
(
is_param
(
name
)
||
preferParam
)
{
params_
.
insert
({
name
,
args_
[
i
+
1
]});
++
i
;
// skip next value, it is not a free parameter
continue
;
}
else
{
flags_
.
emplace
(
name
);
}
};
}
//////////////////////////////////////////////////////////////////////////
inline
string_stream
parser
::
bad_stream
()
const
{
string_stream
bad
;
bad
.
setstate
(
std
::
ios_base
::
failbit
);
return
bad
;
}
//////////////////////////////////////////////////////////////////////////
inline
bool
parser
::
is_number
(
std
::
string
const
&
arg
)
const
{
// inefficient but simple way to determine if a string is a number (which can start with a '-')
std
::
istringstream
istr
(
arg
);
double
number
;
istr
>>
number
;
return
!
(
istr
.
fail
()
||
istr
.
bad
());
}
//////////////////////////////////////////////////////////////////////////
inline
bool
parser
::
is_option
(
std
::
string
const
&
arg
)
const
{
assert
(
0
!=
arg
.
size
());
if
(
is_number
(
arg
))
return
false
;
return
'-'
==
arg
[
0
];
}
//////////////////////////////////////////////////////////////////////////
inline
std
::
string
parser
::
trim_leading_dashes
(
std
::
string
const
&
name
)
const
{
auto
pos
=
name
.
find_first_not_of
(
'-'
);
return
std
::
string
::
npos
!=
pos
?
name
.
substr
(
pos
)
:
name
;
}
//////////////////////////////////////////////////////////////////////////
inline
bool
argh
::
parser
::
got_flag
(
std
::
string
const
&
name
)
const
{
return
flags_
.
end
()
!=
flags_
.
find
(
trim_leading_dashes
(
name
));
}
//////////////////////////////////////////////////////////////////////////
inline
bool
argh
::
parser
::
is_param
(
std
::
string
const
&
name
)
const
{
return
registeredParams_
.
count
(
name
);
}
//////////////////////////////////////////////////////////////////////////
inline
bool
parser
::
operator
[](
std
::
string
const
&
name
)
const
{
return
got_flag
(
name
);
}
//////////////////////////////////////////////////////////////////////////
inline
bool
parser
::
operator
[](
std
::
initializer_list
<
char
const
*
const
>
init_list
)
const
{
return
std
::
any_of
(
init_list
.
begin
(),
init_list
.
end
(),
[
&
](
char
const
*
const
name
)
{
return
got_flag
(
name
);
});
}
//////////////////////////////////////////////////////////////////////////
inline
std
::
string
const
&
parser
::
operator
[](
size_t
ind
)
const
{
if
(
ind
<
pos_args_
.
size
())
return
pos_args_
[
ind
];
return
empty_
;
}
//////////////////////////////////////////////////////////////////////////
inline
string_stream
parser
::
operator
()(
std
::
string
const
&
name
)
const
{
auto
optIt
=
params_
.
find
(
trim_leading_dashes
(
name
));
if
(
params_
.
end
()
!=
optIt
)
return
string_stream
(
optIt
->
second
);
return
bad_stream
();
}
//////////////////////////////////////////////////////////////////////////
inline
string_stream
parser
::
operator
()(
std
::
initializer_list
<
char
const
*
const
>
init_list
)
const
{
for
(
auto
&
name
:
init_list
)
{
auto
optIt
=
params_
.
find
(
trim_leading_dashes
(
name
));
if
(
params_
.
end
()
!=
optIt
)
return
string_stream
(
optIt
->
second
);
}
return
bad_stream
();
}
//////////////////////////////////////////////////////////////////////////
template
<
typename
T
>
string_stream
parser
::
operator
()(
std
::
string
const
&
name
,
T
&&
def_val
)
const
{
auto
optIt
=
params_
.
find
(
trim_leading_dashes
(
name
));
if
(
params_
.
end
()
!=
optIt
)
return
string_stream
(
optIt
->
second
);
std
::
ostringstream
ostr
;
ostr
<<
def_val
;
return
string_stream
(
ostr
.
str
());
// use default
}
//////////////////////////////////////////////////////////////////////////
// same as above but for a list of names. returns the first value to be found.
template
<
typename
T
>
string_stream
parser
::
operator
()(
std
::
initializer_list
<
char
const
*
const
>
init_list
,
T
&&
def_val
)
const
{
for
(
auto
&
name
:
init_list
)
{
auto
optIt
=
params_
.
find
(
trim_leading_dashes
(
name
));
if
(
params_
.
end
()
!=
optIt
)
return
string_stream
(
optIt
->
second
);
}
std
::
ostringstream
ostr
;
ostr
<<
def_val
;
return
string_stream
(
ostr
.
str
());
// use default
}
//////////////////////////////////////////////////////////////////////////
inline
string_stream
parser
::
operator
()(
size_t
ind
)
const
{
if
(
pos_args_
.
size
()
<=
ind
)
return
bad_stream
();
return
string_stream
(
pos_args_
[
ind
]);
}
//////////////////////////////////////////////////////////////////////////
template
<
typename
T
>
string_stream
parser
::
operator
()(
size_t
ind
,
T
&&
def_val
)
const
{
if
(
pos_args_
.
size
()
<=
ind
)
{
std
::
ostringstream
ostr
;
ostr
<<
def_val
;
return
string_stream
(
ostr
.
str
());
}
return
string_stream
(
pos_args_
[
ind
]);
}
//////////////////////////////////////////////////////////////////////////
inline
void
parser
::
add_param
(
std
::
string
const
&
name
)
{
registeredParams_
.
insert
(
trim_leading_dashes
(
name
));
}
//////////////////////////////////////////////////////////////////////////
inline
void
parser
::
add_params
(
std
::
initializer_list
<
char
const
*
const
>
init_list
)
{
for
(
auto
&
name
:
init_list
)
registeredParams_
.
insert
(
trim_leading_dashes
(
name
));
}
}
// namespace argh
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录