mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-25 09:39:52 +08:00
Lots of miscellaneous cleanup. Unified the path_get_cd_path, path_allocate_cd_path, etc. functions
This commit is contained in:
parent
b08fb86637
commit
261bf12c91
|
@ -116,11 +116,10 @@ int autoload_t::load( const wcstring &cmd, bool reload )
|
|||
|
||||
bool autoload_t::can_load( const wcstring &cmd, const env_vars_snapshot_t &vars )
|
||||
{
|
||||
const wchar_t *path_var_ptr = vars.get(env_var_name.c_str());
|
||||
if (! path_var_ptr || ! path_var_ptr[0])
|
||||
const env_var_t path_var = vars.get(env_var_name);
|
||||
if (path_var.missing_or_empty())
|
||||
return false;
|
||||
|
||||
const wcstring path_var(path_var_ptr);
|
||||
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 );
|
||||
|
|
16
builtin.cpp
16
builtin.cpp
|
@ -2591,7 +2591,7 @@ static int builtin_exit( parser_t &parser, wchar_t **argv )
|
|||
static int builtin_cd( parser_t &parser, wchar_t **argv )
|
||||
{
|
||||
env_var_t dir_in;
|
||||
wchar_t *dir = NULL;
|
||||
wcstring dir;
|
||||
int res=STATUS_BUILTIN_OK;
|
||||
|
||||
|
||||
|
@ -2609,11 +2609,13 @@ static int builtin_cd( parser_t &parser, wchar_t **argv )
|
|||
dir_in = argv[1];
|
||||
}
|
||||
|
||||
if (! dir_in.missing()) {
|
||||
dir = path_allocate_cdpath(dir_in);
|
||||
bool got_cd_path = false;
|
||||
if (! dir_in.missing())
|
||||
{
|
||||
got_cd_path = path_get_cdpath(dir_in, &dir);
|
||||
}
|
||||
|
||||
if( !dir )
|
||||
if( !got_cd_path )
|
||||
{
|
||||
if( errno == ENOTDIR )
|
||||
{
|
||||
|
@ -2665,7 +2667,7 @@ static int builtin_cd( parser_t &parser, wchar_t **argv )
|
|||
append_format(stderr_buffer,
|
||||
_( L"%ls: Permission denied: '%ls'\n" ),
|
||||
argv[0],
|
||||
dir );
|
||||
dir.c_str() );
|
||||
|
||||
}
|
||||
else
|
||||
|
@ -2674,7 +2676,7 @@ static int builtin_cd( parser_t &parser, wchar_t **argv )
|
|||
append_format(stderr_buffer,
|
||||
_( L"%ls: '%ls' is not a directory\n" ),
|
||||
argv[0],
|
||||
dir );
|
||||
dir.c_str() );
|
||||
}
|
||||
|
||||
if( !get_is_interactive() )
|
||||
|
@ -2690,8 +2692,6 @@ static int builtin_cd( parser_t &parser, wchar_t **argv )
|
|||
append_format(stderr_buffer, _( L"%ls: Could not set PWD variable\n" ), argv[0] );
|
||||
}
|
||||
|
||||
free(dir);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
11
common.cpp
11
common.cpp
|
@ -219,17 +219,6 @@ int fgetws2( wchar_t **b, int *len, FILE *f )
|
|||
|
||||
}
|
||||
|
||||
|
||||
static bool string_sort_predicate(const wcstring& d1, const wcstring& d2)
|
||||
{
|
||||
return wcsfilecmp(d1.c_str(), d2.c_str()) < 0;
|
||||
}
|
||||
|
||||
void sort_strings( std::vector<wcstring> &strings)
|
||||
{
|
||||
std::sort(strings.begin(), strings.end(), string_sort_predicate);
|
||||
}
|
||||
|
||||
wchar_t *str2wcs( const char *in )
|
||||
{
|
||||
wchar_t *out;
|
||||
|
|
2
common.h
2
common.h
|
@ -207,8 +207,6 @@ int fgetws2( wchar_t **buff, int *len, FILE *f );
|
|||
/** Like fgetws2, but reads into a string */
|
||||
int fgetws2(wcstring *s, FILE *f);
|
||||
|
||||
void sort_strings( std::vector<wcstring> &strings);
|
||||
|
||||
/**
|
||||
Returns a newly allocated wide character string equivalent of the
|
||||
specified multibyte character string
|
||||
|
|
34
env.cpp
34
env.cpp
|
@ -1538,27 +1538,41 @@ void env_export_arr(bool recalc, null_terminated_array_t<char> &output)
|
|||
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++) {
|
||||
const env_var_t val = env_get_string(keys[i]);
|
||||
if (!val.missing()) {
|
||||
vars[keys[i]] = val;
|
||||
key.assign(keys[i]);
|
||||
const env_var_t val = env_get_string(key);
|
||||
if (! val.missing()) {
|
||||
vars[key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env_vars_snapshot_t::env_vars_snapshot_t() { }
|
||||
|
||||
const wchar_t *env_vars_snapshot_t::get(const wchar_t *key) const
|
||||
/* The "current" variables are not a snapshot at all, but instead trampoline to env_get_string, etc. We identify the current snapshot based on pointer values. */
|
||||
static const env_vars_snapshot_t sCurrentSnapshot;
|
||||
const env_vars_snapshot_t &env_vars_snapshot_t::current()
|
||||
{
|
||||
std::map<wcstring, wcstring>::const_iterator iter = vars.find(key);
|
||||
return (iter == vars.end() ? NULL : iter->second.c_str());
|
||||
return sCurrentSnapshot;
|
||||
}
|
||||
|
||||
bool env_vars_snapshot_t::is_current() const
|
||||
{
|
||||
return this == &sCurrentSnapshot;
|
||||
}
|
||||
|
||||
env_var_t env_vars_snapshot_t::get(const wcstring &key) const
|
||||
{
|
||||
std::map<wcstring, wcstring>::const_iterator iter = vars.find(key);
|
||||
return (iter == vars.end() ? env_var_t::missing_var() : env_var_t(iter->second));
|
||||
/* If we represent the current state, bounce to env_get_string */
|
||||
if (this->is_current())
|
||||
{
|
||||
return env_get_string(key);
|
||||
}
|
||||
else {
|
||||
std::map<wcstring, wcstring>::const_iterator iter = vars.find(key);
|
||||
return (iter == vars.end() ? env_var_t::missing_var() : env_var_t(iter->second));
|
||||
}
|
||||
}
|
||||
|
||||
const wchar_t * const env_vars_snapshot_t::highlighting_keys[] = {L"PATH", L"CDPATH", L"HIGHLIGHT_DELAY", L"fish_function_path", NULL};
|
||||
|
||||
const wchar_t * const env_vars_snapshot_t::highlighting_keys[] = {L"PATH", L"CDPATH", L"fish_function_path", NULL};
|
||||
|
|
5
env.h
5
env.h
|
@ -183,14 +183,17 @@ int env_set_pwd();
|
|||
|
||||
class env_vars_snapshot_t {
|
||||
std::map<wcstring, wcstring> vars;
|
||||
bool is_current() const;
|
||||
|
||||
public:
|
||||
env_vars_snapshot_t(const wchar_t * const * keys);
|
||||
env_vars_snapshot_t(void);
|
||||
|
||||
const wchar_t *get(const wchar_t *key) const;
|
||||
env_var_t get(const wcstring &key) const;
|
||||
|
||||
// Returns the fake snapshot representing the live variables array
|
||||
static const env_vars_snapshot_t ¤t();
|
||||
|
||||
// vars necessary for highlighting
|
||||
static const wchar_t * const highlighting_keys[];
|
||||
};
|
||||
|
|
|
@ -850,8 +850,9 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio
|
|||
if (is_help) {
|
||||
suggestionOK = false;
|
||||
} else {
|
||||
wchar_t *path = path_allocate_cdpath(dir, working_directory.c_str());
|
||||
if (path == NULL) {
|
||||
wcstring path;
|
||||
bool can_cd = path_get_cdpath(dir, &path, working_directory.c_str(), vars);
|
||||
if (! can_cd) {
|
||||
suggestionOK = false;
|
||||
} else if (paths_are_same_file(working_directory, path)) {
|
||||
/* Don't suggest the working directory as the path! */
|
||||
|
@ -859,7 +860,6 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio
|
|||
} else {
|
||||
suggestionOK = true;
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1084,7 +1084,7 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
|||
if (! is_cmd)
|
||||
{
|
||||
if (use_builtin || (use_function && function_exists_no_autoload( L"cd", vars)))
|
||||
is_cmd = path_can_be_implicit_cd(cmd, NULL, working_directory.c_str());
|
||||
is_cmd = path_can_be_implicit_cd(cmd, NULL, working_directory.c_str(), vars);
|
||||
}
|
||||
|
||||
if( is_cmd )
|
||||
|
|
83
path.cpp
83
path.cpp
|
@ -133,7 +133,7 @@ static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, const en
|
|||
|
||||
bool path_get_path(const wcstring &cmd, wcstring *out_path, const env_vars_snapshot_t &vars)
|
||||
{
|
||||
return path_get_path_core(cmd, out_path, vars.get(wcstring(L"PATH")));
|
||||
return path_get_path_core(cmd, out_path, vars.get(L"PATH"));
|
||||
}
|
||||
|
||||
bool path_get_path(const wcstring &cmd, wcstring *out_path)
|
||||
|
@ -141,7 +141,7 @@ bool path_get_path(const wcstring &cmd, wcstring *out_path)
|
|||
return path_get_path_core(cmd, out_path, env_get_string(L"PATH"));
|
||||
}
|
||||
|
||||
bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_vars_snapshot_t &vars)
|
||||
bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_var_t &cdpath)
|
||||
{
|
||||
wchar_t *res = 0;
|
||||
int err = ENOENT;
|
||||
|
@ -168,13 +168,12 @@ bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env
|
|||
else
|
||||
{
|
||||
|
||||
const wchar_t *path = L".";
|
||||
wcstring path = L".";
|
||||
|
||||
// Respect CDPATH
|
||||
env_var_t cdpath = env_get_string(L"CDPATH");
|
||||
if (! cdpath.missing_or_empty()) {
|
||||
path = cdpath.c_str();
|
||||
printf("CDPATH: %ls\n", path);
|
||||
}
|
||||
|
||||
wcstokenizer tokenizer(path, ARRAY_SEP_STR);
|
||||
|
@ -220,21 +219,19 @@ bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
||||
bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, const env_vars_snapshot_t &env_vars)
|
||||
{
|
||||
wchar_t *res = NULL;
|
||||
int err = ENOENT;
|
||||
if (dir.empty())
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
if (wd) {
|
||||
if (wd)
|
||||
{
|
||||
size_t len = wcslen(wd);
|
||||
assert(wd[len - 1] == L'/');
|
||||
}
|
||||
|
||||
wcstring_list_t paths;
|
||||
|
||||
if (dir.at(0) == L'/') {
|
||||
/* Absolute path */
|
||||
paths.push_back(dir);
|
||||
|
@ -248,41 +245,35 @@ wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
|||
path.append(dir);
|
||||
paths.push_back(path);
|
||||
} else {
|
||||
wchar_t *path_cpy;
|
||||
wchar_t *state;
|
||||
|
||||
// Respect CDPATH
|
||||
env_var_t path = env_get_string(L"CDPATH");
|
||||
if (path.missing_or_empty()) path = L"."; //We'll change this to the wd if we have one
|
||||
env_var_t path = env_vars.get(L"CDPATH");
|
||||
if (path.missing_or_empty())
|
||||
path = L"."; //We'll change this to the wd if we have one
|
||||
|
||||
path_cpy = wcsdup( path.c_str() );
|
||||
|
||||
for( const wchar_t *nxt_path = wcstok( path_cpy, ARRAY_SEP_STR, &state );
|
||||
nxt_path != NULL;
|
||||
nxt_path = wcstok( 0, ARRAY_SEP_STR, &state) )
|
||||
wcstring nxt_path;
|
||||
wcstokenizer tokenizer(path, ARRAY_SEP_STR);
|
||||
while (tokenizer.next(nxt_path))
|
||||
{
|
||||
|
||||
if (! wcscmp(nxt_path, L".") && wd != NULL) {
|
||||
if (nxt_path == L"." && wd != NULL) {
|
||||
// nxt_path is just '.', and we have a working directory, so use the wd instead
|
||||
// TODO: if nxt_path starts with ./ we need to replace the . with the wd
|
||||
nxt_path = wd;
|
||||
}
|
||||
|
||||
wcstring expanded_path = nxt_path;
|
||||
expand_tilde(expanded_path);
|
||||
expand_tilde(nxt_path);
|
||||
|
||||
// debug( 2, L"woot %ls\n", expanded_path.c_str() );
|
||||
|
||||
if (expanded_path.empty())
|
||||
if (nxt_path.empty())
|
||||
continue;
|
||||
|
||||
wcstring whole_path = expanded_path;
|
||||
wcstring whole_path = nxt_path;
|
||||
append_path_component(whole_path, dir);
|
||||
paths.push_back(whole_path);
|
||||
}
|
||||
free( path_cpy );
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); ++iter) {
|
||||
struct stat buf;
|
||||
const wcstring &dir = *iter;
|
||||
|
@ -290,7 +281,9 @@ wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
|||
{
|
||||
if( S_ISDIR(buf.st_mode) )
|
||||
{
|
||||
res = wcsdup(dir.c_str());
|
||||
success = true;
|
||||
if (out)
|
||||
out->assign(dir);
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
@ -300,24 +293,12 @@ wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
|||
}
|
||||
}
|
||||
|
||||
if( !res )
|
||||
{
|
||||
if (! success)
|
||||
errno = err;
|
||||
}
|
||||
|
||||
return res;
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd)
|
||||
{
|
||||
wchar_t *tmp = path_allocate_cdpath(in, wd);
|
||||
bool result = (tmp != NULL);
|
||||
free(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wchar_t *wd)
|
||||
bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wchar_t *wd, const env_vars_snapshot_t &vars)
|
||||
{
|
||||
wcstring exp_path = path;
|
||||
expand_tilde(exp_path);
|
||||
|
@ -328,16 +309,8 @@ bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wch
|
|||
string_prefixes_string(L"../", exp_path) ||
|
||||
exp_path == L"..")
|
||||
{
|
||||
/* These paths can be implicit cd. Note that a single period cannot (that's used for sourcing files anyways) */
|
||||
wchar_t *cd_path = path_allocate_cdpath(exp_path, wd);
|
||||
if (cd_path)
|
||||
{
|
||||
/* It worked. Return the path if desired */
|
||||
if (out_path)
|
||||
out_path->assign(cd_path);
|
||||
free(cd_path);
|
||||
result = true;
|
||||
}
|
||||
/* These paths can be implicit cd, so see if you cd to the path. Note that a single period cannot (that's used for sourcing files anyways) */
|
||||
result = path_get_cdpath(exp_path, out_path, wd, vars);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -426,10 +399,10 @@ bool path_is_valid(const wcstring &path, const wcstring &working_directory)
|
|||
/* Prepend the working directory. Note that we know path is not empty here. */
|
||||
wcstring tmp = working_directory;
|
||||
tmp.append(path);
|
||||
path_is_valid = (0 == waccess(tmp.c_str(), F_OK));
|
||||
path_is_valid = (0 == waccess(tmp, F_OK));
|
||||
} else {
|
||||
/* Simple check */
|
||||
path_is_valid = (0 == waccess(path.c_str(), F_OK));
|
||||
path_is_valid = (0 == waccess(path, F_OK));
|
||||
}
|
||||
return path_is_valid;
|
||||
}
|
||||
|
|
36
path.h
36
path.h
|
@ -9,6 +9,8 @@
|
|||
#ifndef FISH_PATH_H
|
||||
#define FISH_PATH_H
|
||||
|
||||
#include "env.h"
|
||||
|
||||
/**
|
||||
Return value for path_cdpath_get when locatied a rotten symlink
|
||||
*/
|
||||
|
@ -24,22 +26,16 @@
|
|||
bool path_get_config(wcstring &path);
|
||||
|
||||
/**
|
||||
Finds the full path of an executable in a newly allocated string.
|
||||
Finds the full path of an executable. Returns YES if successful.
|
||||
|
||||
\param cmd The name of the executable.
|
||||
\param output_or_NULL If non-NULL, store the full path.
|
||||
\param vars The environment variables snapshot to use
|
||||
\return 0 if the command can not be found, the path of the command otherwise. The result should be freed with free().
|
||||
*/
|
||||
bool path_get_path( const wcstring &cmd, wcstring *output_or_NULL );
|
||||
|
||||
/**
|
||||
A version of path_get_path() that takes the user's PATH variable from the given environment variable snapshot
|
||||
*/
|
||||
class env_vars_snapshot_t;
|
||||
bool path_get_path(const wcstring &cmd, wcstring *output_or_NULL, const env_vars_snapshot_t &vars);
|
||||
|
||||
|
||||
/** Returns whether the path can be used for an implicit cd command; if so, also returns the path by reference (if desired). This requires it to start with one of the allowed prefixes (., .., ~) and resolve to a directory. */
|
||||
bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path = NULL, const wchar_t *wd = NULL);
|
||||
bool path_get_path(const wcstring &cmd,
|
||||
wcstring *output_or_NULL,
|
||||
const env_vars_snapshot_t &vars = env_vars_snapshot_t::current());
|
||||
|
||||
/**
|
||||
Returns the full path of the specified directory, using the CDPATH
|
||||
|
@ -54,14 +50,22 @@ bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path = NULL, co
|
|||
symlink and a file are found, it is undefined which error status
|
||||
will be returned.
|
||||
|
||||
\param in The name of the directory.
|
||||
\param dir The name of the directory.
|
||||
\param out_or_NULL If non-NULL, return the path to the resolved directory
|
||||
\param wd The working directory, or NULL to use the default. The working directory should have a slash appended at the end.
|
||||
\param vars The environment variable snapshot to use (for the CDPATH variable)
|
||||
\return 0 if the command can not be found, the path of the command otherwise. The path should be free'd with free().
|
||||
*/
|
||||
bool path_get_cdpath(const wcstring &dir,
|
||||
wcstring *out_or_NULL,
|
||||
const wchar_t *wd = NULL,
|
||||
const env_vars_snapshot_t &vars = env_vars_snapshot_t::current());
|
||||
|
||||
wchar_t *path_allocate_cdpath( const wcstring &in, const wchar_t *wd = NULL);
|
||||
bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd = NULL);
|
||||
bool path_get_cdpath_string(const wcstring &in, wcstring &out, const env_vars_snapshot_t &vars);
|
||||
/** Returns whether the path can be used for an implicit cd command; if so, also returns the path by reference (if desired). This requires it to start with one of the allowed prefixes (., .., ~) and resolve to a directory. */
|
||||
bool path_can_be_implicit_cd(const wcstring &path,
|
||||
wcstring *out_path = NULL,
|
||||
const wchar_t *wd = NULL,
|
||||
const env_vars_snapshot_t &vars = env_vars_snapshot_t::current());
|
||||
|
||||
/**
|
||||
Remove double slashes and trailing slashes from a path,
|
||||
|
|
|
@ -2247,18 +2247,9 @@ public:
|
|||
// The gen count has changed, so don't do anything
|
||||
return 0;
|
||||
}
|
||||
const wchar_t *delayer = vars.get(L"HIGHLIGHT_DELAY");
|
||||
double secDelay = 0;
|
||||
if (delayer) {
|
||||
wcstring tmp = delayer;
|
||||
secDelay = from_string<double>(tmp);
|
||||
}
|
||||
if (secDelay > 0) usleep((useconds_t)(secDelay * 1E6));
|
||||
//write(0, "Start", 5);
|
||||
if (! string_to_highlight.empty()) {
|
||||
highlight_function( string_to_highlight.c_str(), colors, match_highlight_pos, NULL /* error */, vars);
|
||||
}
|
||||
//write(0, "End", 3);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
|
35
util.cpp
35
util.cpp
|
@ -47,41 +47,6 @@
|
|||
*/
|
||||
#define SB_MAX_SIZE (128*1024*1024)
|
||||
|
||||
/**
|
||||
Handle oom condition. Default action is to print a stack trace and
|
||||
exit, but an alternative action can be specified.
|
||||
*/
|
||||
#define oom_handler( p ) \
|
||||
{ \
|
||||
if( oom_handler_internal == util_die_on_oom ) \
|
||||
{ \
|
||||
DIE_MEM(); \
|
||||
} \
|
||||
oom_handler_internal( p ); \
|
||||
} \
|
||||
|
||||
|
||||
|
||||
void util_die_on_oom( void * p);
|
||||
|
||||
void (*oom_handler_internal)(void *) = &util_die_on_oom;
|
||||
|
||||
void (*util_set_oom_handler( void (*h)(void *) ))(void *)
|
||||
{
|
||||
void (*old)(void *) = oom_handler_internal;
|
||||
|
||||
if( h )
|
||||
oom_handler_internal = h;
|
||||
else
|
||||
oom_handler_internal = &util_die_on_oom;
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
void util_die_on_oom( void *p )
|
||||
{
|
||||
}
|
||||
|
||||
int mini( int a,
|
||||
int b )
|
||||
{
|
||||
|
|
15
util.h
15
util.h
|
@ -26,21 +26,6 @@ typedef struct buffer
|
|||
}
|
||||
buffer_t;
|
||||
|
||||
/**
|
||||
Set the out-of-memory handler callback function. If a memory
|
||||
allocation fails, this function will be called.
|
||||
*/
|
||||
void (*util_set_oom_handler( void (*h)(void *) ))(void *);
|
||||
|
||||
/**
|
||||
This is a possible out of memory handler that will kill the current
|
||||
process in response to any out of memory event, while also printing
|
||||
an error message describing what allocation failed.
|
||||
|
||||
This is the default out of memory handler.
|
||||
*/
|
||||
void util_die_on_oom( void *p );
|
||||
|
||||
/**
|
||||
Returns the larger of two ints
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue
Block a user