本文整理汇总了C++中env_var_t类的典型用法代码示例。如果您正苦于以下问题:C++ env_var_t类的具体用法?C++ env_var_t怎么用?C++ env_var_t使用的例子?那么恭喜您, 这里精选的类代码示例或许可以为您提供帮助。
在下文中一共展示了env_var_t类的20个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的C++代码示例。
示例1: path_create_data
static wcstring path_create_data() {
bool done = false;
wcstring res;
const env_var_t xdg_dir = env_get_string(L"XDG_DATA_HOME");
if (!xdg_dir.missing()) {
res = xdg_dir + L"/fish";
if (!create_directory(res)) {
done = true;
}
} else {
const env_var_t home = env_get_string(L"HOME");
if (!home.missing()) {
res = home + L"/.local/share/fish";
if (!create_directory(res)) {
done = true;
}
}
}
if (!done) {
res.clear();
debug(0, _(L"Unable to create a data directory for fish. Your history will not be saved. "
L"Please set the $XDG_DATA_HOME variable to a directory where the current user "
L"has write access."));
}
return res;
}
开发者ID:AlexShiLucky,项目名称:fish-shell,代码行数:29,代码来源:path.cpp
示例2: setup_path
/// Make sure the PATH variable contains something.
static void setup_path() {
const env_var_t path = env_get_string(L"PATH");
if (path.missing_or_empty()) {
const wchar_t *value = L"/usr/bin" ARRAY_SEP_STR L"/bin";
env_set(L"PATH", value, ENV_GLOBAL | ENV_EXPORT);
}
}
开发者ID:,项目名称:,代码行数:8,代码来源:
示例3: update_export_array_if_necessary
static void update_export_array_if_necessary(bool recalc) {
ASSERT_IS_MAIN_THREAD();
if (recalc && !get_proc_had_barrier()) {
set_proc_had_barrier(true);
env_universal_barrier();
}
if (has_changed_exported) {
std::map<wcstring, wcstring> vals;
debug(4, L"env_export_arr() recalc");
get_exported(top, &vals);
if (uvars()) {
const wcstring_list_t uni = uvars()->get_names(true, false);
for (size_t i = 0; i < uni.size(); i++) {
const wcstring &key = uni.at(i);
const env_var_t val = uvars()->get(key);
if (!val.missing() && val != ENV_NULL) {
// Note that std::map::insert does NOT overwrite a value already in the map,
// which we depend on here.
vals.insert(std::pair<wcstring, wcstring>(key, val));
}
}
}
std::vector<std::string> local_export_buffer;
export_func(vals, local_export_buffer);
export_array.set(local_export_buffer);
has_changed_exported = false;
}
}
开发者ID:,项目名称:,代码行数:34,代码来源:
示例4: path_create_config
static wcstring path_create_config() {
bool done = false;
wcstring res;
const env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME");
if (!xdg_dir.missing()) {
res = xdg_dir + L"/fish";
if (!create_directory(res)) {
done = true;
}
} else {
const env_var_t home = env_get_string(L"HOME");
if (!home.missing()) {
res = home + L"/.config/fish";
if (!create_directory(res)) {
done = true;
}
}
}
if (!done) {
res.clear();
debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings "
L"will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory "
L"where the current user has write access."));
}
return res;
}
开发者ID:AlexShiLucky,项目名称:fish-shell,代码行数:29,代码来源:path.cpp
示例5: handle_locale
/// Properly sets all locale information.
static void handle_locale(const wchar_t *env_var_name) {
debug(2, L"handle_locale() called in response to '%ls' changing", env_var_name);
const char *old_msg_locale = setlocale(LC_MESSAGES, NULL);
const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
const std::string &value = wcs2string(val);
const std::string &name = wcs2string(env_var_name);
debug(2, L"locale var %s='%s'", name.c_str(), value.c_str());
if (val.empty()) {
unsetenv(name.c_str());
} else {
setenv(name.c_str(), value.c_str(), 1);
}
char *locale = setlocale(LC_ALL, "");
fish_setlocale();
debug(2, L"handle_locale() setlocale(): '%s'", locale);
const char *new_msg_locale = setlocale(LC_MESSAGES, NULL);
debug(3, L"old LC_MESSAGES locale: '%s'", old_msg_locale);
debug(3, L"new LC_MESSAGES locale: '%s'", new_msg_locale);
#ifdef HAVE__NL_MSG_CAT_CNTR
if (strcmp(old_msg_locale, new_msg_locale)) {
// Make change known to GNU gettext.
extern int _nl_msg_cat_cntr;
_nl_msg_cat_cntr++;
}
#endif
}
开发者ID:,项目名称:,代码行数:29,代码来源:
示例6: start_fishd
/**
When fishd isn't started, this function is provided to
env_universal as a callback, it tries to start up fishd. It's
implementation is a bit of a hack, since it evaluates a bit of
shellscript, and it might be used at times when that might not be
the best idea.
*/
static void start_fishd()
{
struct passwd *pw = getpwuid(getuid());
debug(3, L"Spawning new copy of fishd");
if (!pw)
{
debug(0, _(L"Could not get user information"));
return;
}
wcstring cmd = format_string(FISHD_CMD, pw->pw_name);
/* Prefer the fishd in __fish_bin_dir, if exists */
const env_var_t bin_dir = env_get_string(L"__fish_bin_dir");
if (! bin_dir.missing_or_empty())
{
wcstring path = bin_dir + L"/fishd";
if (waccess(path, X_OK) == 0)
{
/* The path command just looks like 'fishd', so insert the bin path to make it absolute */
cmd.insert(0, bin_dir + L"/");
}
}
parser_t &parser = parser_t::principal_parser();
parser.eval(cmd, io_chain_t(), TOP);
}
开发者ID:GarethLewin,项目名称:fish-shell,代码行数:35,代码来源:env.cpp
示例7: env_set_defaults
/**
Set up default values for various variables if not defined.
*/
static void env_set_defaults()
{
if( env_get_string(L"USER").missing() )
{
struct passwd *pw = getpwuid( getuid());
wchar_t *unam = str2wcs( pw->pw_name );
env_set( L"USER", unam, ENV_GLOBAL );
free( unam );
}
if( env_get_string(L"HOME").missing() )
{
const env_var_t unam = env_get_string( L"USER" );
char *unam_narrow = wcs2str( unam.c_str() );
struct passwd *pw = getpwnam( unam_narrow );
wchar_t *dir = str2wcs( pw->pw_dir );
env_set( L"HOME", dir, ENV_GLOBAL );
free( dir );
free( unam_narrow );
}
env_set_pwd();
}
开发者ID:octaexon,项目名称:fish-shell,代码行数:28,代码来源:env.cpp
示例8: env_set_defaults
/**
Set up default values for various variables if not defined.
*/
static void env_set_defaults()
{
if (env_get_string(L"USER").missing())
{
struct passwd *pw = getpwuid(getuid());
if (pw->pw_name != NULL)
{
const wcstring wide_name = str2wcstring(pw->pw_name);
env_set(L"USER", wide_name.c_str(), ENV_GLOBAL);
}
}
if (env_get_string(L"HOME").missing())
{
const env_var_t unam = env_get_string(L"USER");
char *unam_narrow = wcs2str(unam.c_str());
struct passwd *pw = getpwnam(unam_narrow);
if (pw->pw_dir != NULL)
{
const wcstring dir = str2wcstring(pw->pw_dir);
env_set(L"HOME", dir.c_str(), ENV_GLOBAL);
}
free(unam_narrow);
}
env_set_pwd();
}
开发者ID:GarethLewin,项目名称:fish-shell,代码行数:32,代码来源:env.cpp
示例9: autoload_names
/// Insert a list of all dynamically loaded functions into the specified list.
static void autoload_names(std::set<wcstring> &names, int get_hidden) {
size_t i;
const env_var_t path_var_wstr = env_get_string(L"fish_function_path");
if (path_var_wstr.missing()) return;
const wchar_t *path_var = path_var_wstr.c_str();
wcstring_list_t path_list;
tokenize_variable_array(path_var, path_list);
for (i = 0; i < path_list.size(); i++) {
const wcstring &ndir_str = path_list.at(i);
const wchar_t *ndir = (wchar_t *)ndir_str.c_str();
DIR *dir = wopendir(ndir);
if (!dir) continue;
wcstring name;
while (wreaddir(dir, name)) {
const wchar_t *fn = name.c_str();
const wchar_t *suffix;
if (!get_hidden && fn[0] == L'_') continue;
suffix = wcsrchr(fn, L'.');
if (suffix && (wcscmp(suffix, L".fish") == 0)) {
wcstring name(fn, suffix - fn);
names.insert(name);
}
}
closedir(dir);
}
}
开发者ID:Mabinogiysk,项目名称:fish-shell,代码行数:32,代码来源:function.cpp
示例10: can_load
bool autoload_t::can_load(const wcstring &cmd, const env_vars_snapshot_t &vars) {
const env_var_t path_var = vars.get(env_var_name);
if (path_var.missing_or_empty()) return false;
std::vector<wcstring> path_list;
tokenize_variable_array(path_var, path_list);
return this->locate_file_and_maybe_load_it(cmd, false, false, path_list);
}
开发者ID:haarts,项目名称:fish-shell,代码行数:8,代码来源:autoload.cpp
示例11: kill_add
void kill_add(const wcstring &str)
{
ASSERT_IS_MAIN_THREAD();
if (str.empty())
return;
wcstring cmd;
wcstring escaped_str;
kill_list.push_front(str);
/*
Check to see if user has set the FISH_CLIPBOARD_CMD variable,
and, if so, use it instead of checking the display, etc.
I couldn't think of a safe way to allow overide of the echo
command too, so, the command used must accept the input via stdin.
*/
const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD");
if (!clipboard_wstr.missing())
{
escaped_str = escape(str.c_str(), ESCAPE_ALL);
cmd.assign(L"echo -n ");
cmd.append(escaped_str);
cmd.append(clipboard_wstr);
}
else
{
/* This is for sending the kill to the X copy-and-paste buffer */
if (!has_xsel())
{
return;
}
const env_var_t disp_wstr = env_get_string(L"DISPLAY");
if (!disp_wstr.missing())
{
escaped_str = escape(str.c_str(), ESCAPE_ALL);
cmd.assign(L"echo -n ");
cmd.append(escaped_str);
cmd.append(L" | xsel -i -b");
}
}
if (! cmd.empty())
{
if (exec_subshell(cmd, false /* do not apply exit status */) == -1)
{
/*
Do nothing on failiure
*/
}
cut_buffer = escaped_str;
}
}
开发者ID:ALSchwalm,项目名称:fish-shell,代码行数:56,代码来源:kill.cpp
示例12: ASSERT_IS_MAIN_THREAD
env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t *const *keys) {
ASSERT_IS_MAIN_THREAD();
wcstring key;
for (size_t i = 0; keys[i]; i++) {
key.assign(keys[i]);
const env_var_t val = env_get_string(key);
if (!val.missing()) {
vars[key] = val;
}
}
}
开发者ID:,项目名称:,代码行数:11,代码来源:
示例13: handle_timezone
/// Properly sets all timezone information.
static void handle_timezone(const wchar_t *env_var_name) {
debug(2, L"handle_timezone() called in response to '%ls' changing", env_var_name);
const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
const std::string &value = wcs2string(val);
const std::string &name = wcs2string(env_var_name);
debug(2, L"timezone var %s='%s'", name.c_str(), value.c_str());
if (val.empty()) {
unsetenv(name.c_str());
} else {
setenv(name.c_str(), value.c_str(), 1);
}
tzset();
}
开发者ID:,项目名称:,代码行数:14,代码来源:
示例14: handle_curses
/// Push all curses/terminfo env vars into the global environment where they can be found by those
/// libraries.
static void handle_curses(const wchar_t *env_var_name) {
debug(2, L"handle_curses() called in response to '%ls' changing", env_var_name);
const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
const std::string &name = wcs2string(env_var_name);
const std::string &value = wcs2string(val);
debug(2, L"curses var %s='%s'", name.c_str(), value.c_str());
if (val.empty()) {
unsetenv(name.c_str());
} else {
setenv(name.c_str(), value.c_str(), 1);
}
// TODO: Modify input_init() to allow calling it when the terminfo env vars are dynamically
// changed. At the present time it can be called just once. Also, we should really only do this
// if the TERM var is set.
// input_init();
}
开发者ID:,项目名称:,代码行数:18,代码来源:
示例15: expand_escape_variable
wcstring expand_escape_variable(const env_var_t &var) {
wcstring buff;
wcstring_list_t lst;
var.to_list(lst);
if (lst.size() == 0) {
; // empty list expands to nothing
} else if (lst.size() == 1) {
const wcstring &el = lst.at(0);
if (el.find(L' ') != wcstring::npos && is_quotable(el)) {
buff.append(L"'");
buff.append(el);
buff.append(L"'");
} else {
buff.append(escape_string(el, 1));
}
} else {
for (size_t j = 0; j < lst.size(); j++) {
const wcstring &el = lst.at(j);
if (j) buff.append(L" ");
if (is_quotable(el)) {
buff.append(L"'");
buff.append(el);
buff.append(L"'");
} else {
buff.append(escape_string(el, 1));
}
}
}
return buff;
}
开发者ID:raichoo,项目名称:fish-shell,代码行数:34,代码来源:expand.cpp
示例16: is_screen_name_escape_seq
/// Does this look like the escape sequence for setting a screen name.
static bool is_screen_name_escape_seq(const wchar_t *code, size_t *resulting_length) {
bool found = false;
if (code[1] == L'k') {
const env_var_t term_name = env_get_string(L"TERM");
if (!term_name.missing() && string_prefixes_string(L"screen", term_name)) {
const wchar_t *const screen_name_end_sentinel = L"\x1b\\";
const wchar_t *screen_name_end = wcsstr(&code[2], screen_name_end_sentinel);
if (screen_name_end == NULL) {
// Consider just <esc>k to be the code.
*resulting_length = 2;
} else {
const wchar_t *escape_sequence_end =
screen_name_end + wcslen(screen_name_end_sentinel);
*resulting_length = escape_sequence_end - code;
}
found = true;
}
}
return found;
}
开发者ID:AlexShiLucky,项目名称:fish-shell,代码行数:21,代码来源:screen.cpp
示例17: kill_check_x_buffer
/**
Check the X clipboard. If it has been changed, add the new
clipboard contents to the fish killring.
*/
static void kill_check_x_buffer()
{
if (!has_xsel())
return;
const env_var_t disp = env_get_string(L"DISPLAY");
if (! disp.missing())
{
size_t i;
wcstring cmd = L"xsel -t 500 -b";
wcstring new_cut_buffer=L"";
wcstring_list_t list;
if (exec_subshell(cmd, list, false /* do not apply exit status */) != -1)
{
for (i=0; i<list.size(); i++)
{
wcstring next_line = escape_string(list.at(i), 0);
if (i > 0) new_cut_buffer += L"\\n";
new_cut_buffer += next_line;
}
if (new_cut_buffer.size() > 0)
{
/*
The buffer is inserted with backslash escapes,
since we don't really like tabs, newlines,
etc. anyway.
*/
if (cut_buffer != new_cut_buffer)
{
cut_buffer = new_cut_buffer;
kill_list.push_front(new_cut_buffer);
}
}
}
}
}
开发者ID:ALSchwalm,项目名称:fish-shell,代码行数:43,代码来源:kill.cpp
示例18: path_create
static void path_create(wcstring &path, const wcstring &xdg_var, const wcstring &which_dir,
const wcstring &custom_error_msg) {
bool path_done = false;
bool using_xdg = false;
int saved_errno = 0;
// The vars we fetch must be exported. Allowing them to be universal doesn't make sense and
// allowing that creates a lock inversion that deadlocks the shell since we're called before
// uvars are available.
const env_var_t xdg_dir = env_get_string(xdg_var, ENV_GLOBAL | ENV_EXPORT);
if (!xdg_dir.missing_or_empty()) {
using_xdg = true;
path = xdg_dir + L"/fish";
if (create_directory(path) != -1) {
path_done = true;
} else {
saved_errno = errno;
}
} else {
const env_var_t home = env_get_string(L"HOME", ENV_GLOBAL | ENV_EXPORT);
if (!home.missing_or_empty()) {
path = home + (which_dir == L"config" ? L"/.config/fish" : L"/.local/share/fish");
if (create_directory(path) != -1) {
path_done = true;
} else {
saved_errno = errno;
}
}
}
if (!path_done) {
maybe_issue_path_warning(which_dir, custom_error_msg, using_xdg, xdg_var, path,
saved_errno);
path.clear();
}
return;
}
开发者ID:Hunsu,项目名称:fish-shell,代码行数:38,代码来源:path.cpp
示例19: env_init
//.........这里部分代码省略.........
std::replace(val.begin(), val.end(), L':', ARRAY_SEP);
}
env_set(key, val.c_str(), ENV_EXPORT | ENV_GLOBAL);
}
}
/* Set the given paths in the environment, if we have any */
if (paths != NULL)
{
env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL);
env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL);
env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL);
env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL);
}
/*
Set up the PATH variable
*/
setup_path();
/*
Set up the USER variable
*/
if (env_get_string(L"USER").missing_or_empty())
{
const struct passwd *pw = getpwuid(getuid());
if (pw && pw->pw_name)
{
const wcstring uname = str2wcstring(pw->pw_name);
env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT);
}
}
/*
Set up the version variables
*/
wcstring version = str2wcstring(get_fish_version());
env_set(L"version", version.c_str(), ENV_GLOBAL);
env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL);
/*
Set up SHLVL variable
*/
const env_var_t shlvl_str = env_get_string(L"SHLVL");
wcstring nshlvl_str = L"1";
if (! shlvl_str.missing())
{
wchar_t *end;
long shlvl_i = wcstol(shlvl_str.c_str(), &end, 10);
while (iswspace(*end)) ++end; /* skip trailing whitespace */
if (shlvl_i >= 0 && *end == '\0')
{
nshlvl_str = to_string<long>(shlvl_i + 1);
}
}
env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT);
env_read_only.insert(L"SHLVL");
/* Set up the HOME variable */
if (env_get_string(L"HOME").missing_or_empty())
{
const env_var_t unam = env_get_string(L"USER");
char *unam_narrow = wcs2str(unam.c_str());
struct passwd *pw = getpwnam(unam_narrow);
if (pw->pw_dir != NULL)
{
const wcstring dir = str2wcstring(pw->pw_dir);
env_set(L"HOME", dir.c_str(), ENV_GLOBAL | ENV_EXPORT);
}
free(unam_narrow);
}
/* Set PWD */
env_set_pwd();
/* Set up universal variables. The empty string means to use the deafult path. */
assert(s_universal_variables == NULL);
s_universal_variables = new env_universal_t(L"");
s_universal_variables->load();
/* Set g_log_forks */
env_var_t log_forks = env_get_string(L"fish_log_forks");
g_log_forks = ! log_forks.missing_or_empty() && from_string<bool>(log_forks);
/* Set g_use_posix_spawn. Default to true. */
env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn");
g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn));
/* Set fish_bind_mode to "default" */
env_set(FISH_BIND_MODE_VAR, DEFAULT_BIND_MODE, ENV_GLOBAL);
/*
Now that the global scope is fully initialized, add a toplevel local
scope. This same local scope will persist throughout the lifetime of the
fish process, and it will ensure that `set -l` commands run at the
command-line don't affect the global scope.
*/
env_push(false);
}
开发者ID:lnsoso,项目名称:fish-shell,代码行数:101,代码来源:env.cpp
示例20: env_init
//.........这里部分代码省略.........
global_env = top;
global = &top->env;
/*
Now the environemnt variable handling is set up, the next step
is to insert valid data
*/
/*
Import environment variables
*/
for (char **p = (environ ? environ : __environ); p && *p; p++)
{
const wcstring key_and_val = str2wcstring(*p); //like foo=bar
size_t eql = key_and_val.find(L'=');
if (eql == wcstring::npos)
{
// no equals found
env_set(key_and_val, L"", ENV_EXPORT);
}
else
{
wcstring key = key_and_val.substr(0, eql);
wcstring val = key_and_val.substr(eql + 1);
if (variable_can_be_array(val))
{
std::replace(val.begin(), val.end(), L':', ARRAY_SEP);
}
env_set(key, val.c_str(), ENV_EXPORT | ENV_GLOBAL);
}
}
/* Set the given paths in the environment, if we have any */
if (paths != NULL)
{
env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL | ENV_EXPORT);
env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL | ENV_EXPORT);
env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL | ENV_EXPORT);
env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL | ENV_EXPORT);
}
/*
Set up the PATH variable
*/
setup_path();
/*
Set up the USER variable
*/
const struct passwd *pw = getpwuid(getuid());
if (pw && pw->pw_name)
{
const wcstring uname = str2wcstring(pw->pw_name);
env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT);
}
/*
Set up the version variables
*/
wcstring version = str2wcstring(FISH_BUILD_VERSION);
env_set(L"version", version.c_str(), ENV_GLOBAL);
env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL);
const env_var_t fishd_dir_wstr = env_get_string(L"FISHD_SOCKET_DIR");
const env_var_t user_dir_wstr = env_get_string(L"USER");
wchar_t * fishd_dir = fishd_dir_wstr.missing()?NULL:const_cast<wchar_t*>(fishd_dir_wstr.c_str());
wchar_t * user_dir = user_dir_wstr.missing()?NULL:const_cast<wchar_t*>(user_dir_wstr.c_str());
env_universal_init(fishd_dir , user_dir ,
&start_fishd,
&universal_callback);
/*
Set up SHLVL variable
*/
const env_var_t shlvl_str = env_get_string(L"SHLVL");
wcstring nshlvl_str = L"1";
if (! shlvl_str.missing())
{
long shlvl_i = wcstol(shlvl_str.c_str(), NULL, 10);
if (shlvl_i >= 0)
{
nshlvl_str = to_string<long>(shlvl_i + 1);
}
}
env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT);
/* Set correct defaults for e.g. USER and HOME variables */
env_set_defaults();
/* Set g_log_forks */
env_var_t log_forks = env_get_string(L"fish_log_forks");
g_log_forks = ! log_forks.missing_or_empty() && from_string<bool>(log_forks);
/* Set g_use_posix_spawn. Default to true. */
env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn");
g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn));
}
开发者ID:GarethLewin,项目名称:fish-shell,代码行数:101,代码来源:env.cpp
注:本文中的env_var_t类示例由纯净天空整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。 |
请发表评论