hush: reorder builtins (cd and pwd ought to be close, etc), no code changes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
f35ad3bd12
commit
a1184af5f8
604
shell/hush.c
604
shell/hush.c
@ -8832,6 +8832,30 @@ static int FAST_FUNC builtin_printf(char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_HELP
|
||||
static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
|
||||
{
|
||||
const struct built_in_command *x;
|
||||
|
||||
printf(
|
||||
"Built-in commands:\n"
|
||||
"------------------\n");
|
||||
for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
|
||||
if (x->b_descr)
|
||||
printf("%-10s%s\n", x->b_cmd, x->b_descr);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MAX_HISTORY && ENABLE_FEATURE_EDITING
|
||||
static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
|
||||
{
|
||||
show_history(G.line_input_state);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char **skip_dash_dash(char **argv)
|
||||
{
|
||||
argv++;
|
||||
@ -8840,24 +8864,6 @@ static char **skip_dash_dash(char **argv)
|
||||
return argv;
|
||||
}
|
||||
|
||||
static int FAST_FUNC builtin_eval(char **argv)
|
||||
{
|
||||
int rcode = EXIT_SUCCESS;
|
||||
|
||||
argv = skip_dash_dash(argv);
|
||||
if (*argv) {
|
||||
char *str = expand_strvec_to_string(argv);
|
||||
/* bash:
|
||||
* eval "echo Hi; done" ("done" is syntax error):
|
||||
* "echo Hi" will not execute too.
|
||||
*/
|
||||
parse_and_run_string(str);
|
||||
free(str);
|
||||
rcode = G.last_exitcode;
|
||||
}
|
||||
return rcode;
|
||||
}
|
||||
|
||||
static int FAST_FUNC builtin_cd(char **argv)
|
||||
{
|
||||
const char *newdir;
|
||||
@ -8885,6 +8891,30 @@ static int FAST_FUNC builtin_cd(char **argv)
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
|
||||
{
|
||||
puts(get_cwd(0));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int FAST_FUNC builtin_eval(char **argv)
|
||||
{
|
||||
int rcode = EXIT_SUCCESS;
|
||||
|
||||
argv = skip_dash_dash(argv);
|
||||
if (*argv) {
|
||||
char *str = expand_strvec_to_string(argv);
|
||||
/* bash:
|
||||
* eval "echo Hi; done" ("done" is syntax error):
|
||||
* "echo Hi" will not execute too.
|
||||
*/
|
||||
parse_and_run_string(str);
|
||||
free(str);
|
||||
rcode = G.last_exitcode;
|
||||
}
|
||||
return rcode;
|
||||
}
|
||||
|
||||
static int FAST_FUNC builtin_exec(char **argv)
|
||||
{
|
||||
argv = skip_dash_dash(argv);
|
||||
@ -8930,6 +8960,147 @@ static int FAST_FUNC builtin_exit(char **argv)
|
||||
hush_exit(xatoi(argv[0]) & 0xff);
|
||||
}
|
||||
|
||||
#if ENABLE_HUSH_TYPE
|
||||
/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
|
||||
static int FAST_FUNC builtin_type(char **argv)
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
while (*++argv) {
|
||||
const char *type;
|
||||
char *path = NULL;
|
||||
|
||||
if (0) {} /* make conditional compile easier below */
|
||||
/*else if (find_alias(*argv))
|
||||
type = "an alias";*/
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
else if (find_function(*argv))
|
||||
type = "a function";
|
||||
#endif
|
||||
else if (find_builtin(*argv))
|
||||
type = "a shell builtin";
|
||||
else if ((path = find_in_path(*argv)) != NULL)
|
||||
type = path;
|
||||
else {
|
||||
bb_error_msg("type: %s: not found", *argv);
|
||||
ret = EXIT_FAILURE;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%s is %s\n", *argv, type);
|
||||
free(path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_READ
|
||||
/* Interruptibility of read builtin in bash
|
||||
* (tested on bash-4.2.8 by sending signals (not by ^C)):
|
||||
*
|
||||
* Empty trap makes read ignore corresponding signal, for any signal.
|
||||
*
|
||||
* SIGINT:
|
||||
* - terminates non-interactive shell;
|
||||
* - interrupts read in interactive shell;
|
||||
* if it has non-empty trap:
|
||||
* - executes trap and returns to command prompt in interactive shell;
|
||||
* - executes trap and returns to read in non-interactive shell;
|
||||
* SIGTERM:
|
||||
* - is ignored (does not interrupt) read in interactive shell;
|
||||
* - terminates non-interactive shell;
|
||||
* if it has non-empty trap:
|
||||
* - executes trap and returns to read;
|
||||
* SIGHUP:
|
||||
* - terminates shell (regardless of interactivity);
|
||||
* if it has non-empty trap:
|
||||
* - executes trap and returns to read;
|
||||
*/
|
||||
static int FAST_FUNC builtin_read(char **argv)
|
||||
{
|
||||
const char *r;
|
||||
char *opt_n = NULL;
|
||||
char *opt_p = NULL;
|
||||
char *opt_t = NULL;
|
||||
char *opt_u = NULL;
|
||||
const char *ifs;
|
||||
int read_flags;
|
||||
|
||||
/* "!": do not abort on errors.
|
||||
* Option string must start with "sr" to match BUILTIN_READ_xxx
|
||||
*/
|
||||
read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
|
||||
if (read_flags == (uint32_t)-1)
|
||||
return EXIT_FAILURE;
|
||||
argv += optind;
|
||||
ifs = get_local_var_value("IFS"); /* can be NULL */
|
||||
|
||||
again:
|
||||
r = shell_builtin_read(set_local_var_from_halves,
|
||||
argv,
|
||||
ifs,
|
||||
read_flags,
|
||||
opt_n,
|
||||
opt_p,
|
||||
opt_t,
|
||||
opt_u
|
||||
);
|
||||
|
||||
if ((uintptr_t)r == 1 && errno == EINTR) {
|
||||
unsigned sig = check_and_run_traps();
|
||||
if (sig && sig != SIGINT)
|
||||
goto again;
|
||||
}
|
||||
|
||||
if ((uintptr_t)r > 1) {
|
||||
bb_error_msg("%s", r);
|
||||
r = (char*)(uintptr_t)1;
|
||||
}
|
||||
|
||||
return (uintptr_t)r;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_UMASK
|
||||
static int FAST_FUNC builtin_umask(char **argv)
|
||||
{
|
||||
int rc;
|
||||
mode_t mask;
|
||||
|
||||
rc = 1;
|
||||
mask = umask(0);
|
||||
argv = skip_dash_dash(argv);
|
||||
if (argv[0]) {
|
||||
mode_t old_mask = mask;
|
||||
|
||||
/* numeric umasks are taken as-is */
|
||||
/* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
|
||||
if (!isdigit(argv[0][0]))
|
||||
mask ^= 0777;
|
||||
mask = bb_parse_mode(argv[0], mask);
|
||||
if (!isdigit(argv[0][0]))
|
||||
mask ^= 0777;
|
||||
if ((unsigned)mask > 0777) {
|
||||
mask = old_mask;
|
||||
/* bash messages:
|
||||
* bash: umask: 'q': invalid symbolic mode operator
|
||||
* bash: umask: 999: octal number out of range
|
||||
*/
|
||||
bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
/* Mimic bash */
|
||||
printf("%04o\n", (unsigned) mask);
|
||||
/* fall through and restore mask which we set to 0 */
|
||||
}
|
||||
umask(mask);
|
||||
|
||||
return !rc; /* rc != 0 - success */
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP
|
||||
static void print_escaped(const char *s)
|
||||
{
|
||||
@ -9218,73 +9389,61 @@ static int FAST_FUNC builtin_shift(char **argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#if ENABLE_HUSH_READ
|
||||
/* Interruptibility of read builtin in bash
|
||||
* (tested on bash-4.2.8 by sending signals (not by ^C)):
|
||||
*
|
||||
* Empty trap makes read ignore corresponding signal, for any signal.
|
||||
*
|
||||
* SIGINT:
|
||||
* - terminates non-interactive shell;
|
||||
* - interrupts read in interactive shell;
|
||||
* if it has non-empty trap:
|
||||
* - executes trap and returns to command prompt in interactive shell;
|
||||
* - executes trap and returns to read in non-interactive shell;
|
||||
* SIGTERM:
|
||||
* - is ignored (does not interrupt) read in interactive shell;
|
||||
* - terminates non-interactive shell;
|
||||
* if it has non-empty trap:
|
||||
* - executes trap and returns to read;
|
||||
* SIGHUP:
|
||||
* - terminates shell (regardless of interactivity);
|
||||
* if it has non-empty trap:
|
||||
* - executes trap and returns to read;
|
||||
*/
|
||||
static int FAST_FUNC builtin_read(char **argv)
|
||||
static int FAST_FUNC builtin_source(char **argv)
|
||||
{
|
||||
const char *r;
|
||||
char *opt_n = NULL;
|
||||
char *opt_p = NULL;
|
||||
char *opt_t = NULL;
|
||||
char *opt_u = NULL;
|
||||
const char *ifs;
|
||||
int read_flags;
|
||||
|
||||
/* "!": do not abort on errors.
|
||||
* Option string must start with "sr" to match BUILTIN_READ_xxx
|
||||
*/
|
||||
read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
|
||||
if (read_flags == (uint32_t)-1)
|
||||
return EXIT_FAILURE;
|
||||
argv += optind;
|
||||
ifs = get_local_var_value("IFS"); /* can be NULL */
|
||||
|
||||
again:
|
||||
r = shell_builtin_read(set_local_var_from_halves,
|
||||
argv,
|
||||
ifs,
|
||||
read_flags,
|
||||
opt_n,
|
||||
opt_p,
|
||||
opt_t,
|
||||
opt_u
|
||||
);
|
||||
|
||||
if ((uintptr_t)r == 1 && errno == EINTR) {
|
||||
unsigned sig = check_and_run_traps();
|
||||
if (sig && sig != SIGINT)
|
||||
goto again;
|
||||
}
|
||||
|
||||
if ((uintptr_t)r > 1) {
|
||||
bb_error_msg("%s", r);
|
||||
r = (char*)(uintptr_t)1;
|
||||
}
|
||||
|
||||
return (uintptr_t)r;
|
||||
}
|
||||
char *arg_path, *filename;
|
||||
FILE *input;
|
||||
save_arg_t sv;
|
||||
char *args_need_save;
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
smallint sv_flg;
|
||||
#endif
|
||||
|
||||
argv = skip_dash_dash(argv);
|
||||
filename = argv[0];
|
||||
if (!filename) {
|
||||
/* bash says: "bash: .: filename argument required" */
|
||||
return 2; /* bash compat */
|
||||
}
|
||||
arg_path = NULL;
|
||||
if (!strchr(filename, '/')) {
|
||||
arg_path = find_in_path(filename);
|
||||
if (arg_path)
|
||||
filename = arg_path;
|
||||
}
|
||||
input = remember_FILE(fopen_or_warn(filename, "r"));
|
||||
free(arg_path);
|
||||
if (!input) {
|
||||
/* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
|
||||
/* POSIX: non-interactive shell should abort here,
|
||||
* not merely fail. So far no one complained :)
|
||||
*/
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
sv_flg = G_flag_return_in_progress;
|
||||
/* "we are inside sourced file, ok to use return" */
|
||||
G_flag_return_in_progress = -1;
|
||||
#endif
|
||||
args_need_save = argv[1]; /* used as a boolean variable */
|
||||
if (args_need_save)
|
||||
save_and_replace_G_args(&sv, argv);
|
||||
|
||||
/* "false; . ./empty_line; echo Zero:$?" should print 0 */
|
||||
G.last_exitcode = 0;
|
||||
parse_and_run_file(input);
|
||||
fclose_and_forget(input);
|
||||
|
||||
if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
|
||||
restore_G_args(&sv, argv);
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
G_flag_return_in_progress = sv_flg;
|
||||
#endif
|
||||
|
||||
return G.last_exitcode;
|
||||
}
|
||||
|
||||
#if ENABLE_HUSH_TRAP
|
||||
static int FAST_FUNC builtin_trap(char **argv)
|
||||
{
|
||||
@ -9377,41 +9536,6 @@ static int FAST_FUNC builtin_trap(char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_TYPE
|
||||
/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
|
||||
static int FAST_FUNC builtin_type(char **argv)
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
while (*++argv) {
|
||||
const char *type;
|
||||
char *path = NULL;
|
||||
|
||||
if (0) {} /* make conditional compile easier below */
|
||||
/*else if (find_alias(*argv))
|
||||
type = "an alias";*/
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
else if (find_function(*argv))
|
||||
type = "a function";
|
||||
#endif
|
||||
else if (find_builtin(*argv))
|
||||
type = "a shell builtin";
|
||||
else if ((path = find_in_path(*argv)) != NULL)
|
||||
type = path;
|
||||
else {
|
||||
bb_error_msg("type: %s: not found", *argv);
|
||||
ret = EXIT_FAILURE;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%s is %s\n", *argv, type);
|
||||
free(path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_JOB
|
||||
static struct pipe *parse_jobspec(const char *str)
|
||||
{
|
||||
@ -9441,6 +9565,23 @@ static struct pipe *parse_jobspec(const char *str)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
|
||||
{
|
||||
struct pipe *job;
|
||||
const char *status_string;
|
||||
|
||||
checkjobs(NULL, 0 /*(no pid to wait for)*/);
|
||||
for (job = G.job_list; job; job = job->next) {
|
||||
if (job->alive_cmds == job->stopped_cmds)
|
||||
status_string = "Stopped";
|
||||
else
|
||||
status_string = "Running";
|
||||
|
||||
printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* built-in 'fg' and 'bg' handler */
|
||||
static int FAST_FUNC builtin_fg_bg(char **argv)
|
||||
{
|
||||
@ -9496,192 +9637,6 @@ static int FAST_FUNC builtin_fg_bg(char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_HELP
|
||||
static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
|
||||
{
|
||||
const struct built_in_command *x;
|
||||
|
||||
printf(
|
||||
"Built-in commands:\n"
|
||||
"------------------\n");
|
||||
for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
|
||||
if (x->b_descr)
|
||||
printf("%-10s%s\n", x->b_cmd, x->b_descr);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MAX_HISTORY && ENABLE_FEATURE_EDITING
|
||||
static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
|
||||
{
|
||||
show_history(G.line_input_state);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_JOB
|
||||
static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
|
||||
{
|
||||
struct pipe *job;
|
||||
const char *status_string;
|
||||
|
||||
checkjobs(NULL, 0 /*(no pid to wait for)*/);
|
||||
for (job = G.job_list; job; job = job->next) {
|
||||
if (job->alive_cmds == job->stopped_cmds)
|
||||
status_string = "Stopped";
|
||||
else
|
||||
status_string = "Running";
|
||||
|
||||
printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_MEMLEAK
|
||||
static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
|
||||
{
|
||||
void *p;
|
||||
unsigned long l;
|
||||
|
||||
# ifdef M_TRIM_THRESHOLD
|
||||
/* Optional. Reduces probability of false positives */
|
||||
malloc_trim(0);
|
||||
# endif
|
||||
/* Crude attempt to find where "free memory" starts,
|
||||
* sans fragmentation. */
|
||||
p = malloc(240);
|
||||
l = (unsigned long)p;
|
||||
free(p);
|
||||
p = malloc(3400);
|
||||
if (l < (unsigned long)p) l = (unsigned long)p;
|
||||
free(p);
|
||||
|
||||
|
||||
# if 0 /* debug */
|
||||
{
|
||||
struct mallinfo mi = mallinfo();
|
||||
printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
|
||||
mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
|
||||
}
|
||||
# endif
|
||||
|
||||
if (!G.memleak_value)
|
||||
G.memleak_value = l;
|
||||
|
||||
l -= G.memleak_value;
|
||||
if ((long)l < 0)
|
||||
l = 0;
|
||||
l /= 1024;
|
||||
if (l > 127)
|
||||
l = 127;
|
||||
|
||||
/* Exitcode is "how many kilobytes we leaked since 1st call" */
|
||||
return l;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
|
||||
{
|
||||
puts(get_cwd(0));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int FAST_FUNC builtin_source(char **argv)
|
||||
{
|
||||
char *arg_path, *filename;
|
||||
FILE *input;
|
||||
save_arg_t sv;
|
||||
char *args_need_save;
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
smallint sv_flg;
|
||||
#endif
|
||||
|
||||
argv = skip_dash_dash(argv);
|
||||
filename = argv[0];
|
||||
if (!filename) {
|
||||
/* bash says: "bash: .: filename argument required" */
|
||||
return 2; /* bash compat */
|
||||
}
|
||||
arg_path = NULL;
|
||||
if (!strchr(filename, '/')) {
|
||||
arg_path = find_in_path(filename);
|
||||
if (arg_path)
|
||||
filename = arg_path;
|
||||
}
|
||||
input = remember_FILE(fopen_or_warn(filename, "r"));
|
||||
free(arg_path);
|
||||
if (!input) {
|
||||
/* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
|
||||
/* POSIX: non-interactive shell should abort here,
|
||||
* not merely fail. So far no one complained :)
|
||||
*/
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
sv_flg = G_flag_return_in_progress;
|
||||
/* "we are inside sourced file, ok to use return" */
|
||||
G_flag_return_in_progress = -1;
|
||||
#endif
|
||||
args_need_save = argv[1]; /* used as a boolean variable */
|
||||
if (args_need_save)
|
||||
save_and_replace_G_args(&sv, argv);
|
||||
|
||||
/* "false; . ./empty_line; echo Zero:$?" should print 0 */
|
||||
G.last_exitcode = 0;
|
||||
parse_and_run_file(input);
|
||||
fclose_and_forget(input);
|
||||
|
||||
if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
|
||||
restore_G_args(&sv, argv);
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
G_flag_return_in_progress = sv_flg;
|
||||
#endif
|
||||
|
||||
return G.last_exitcode;
|
||||
}
|
||||
|
||||
#if ENABLE_HUSH_UMASK
|
||||
static int FAST_FUNC builtin_umask(char **argv)
|
||||
{
|
||||
int rc;
|
||||
mode_t mask;
|
||||
|
||||
rc = 1;
|
||||
mask = umask(0);
|
||||
argv = skip_dash_dash(argv);
|
||||
if (argv[0]) {
|
||||
mode_t old_mask = mask;
|
||||
|
||||
/* numeric umasks are taken as-is */
|
||||
/* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
|
||||
if (!isdigit(argv[0][0]))
|
||||
mask ^= 0777;
|
||||
mask = bb_parse_mode(argv[0], mask);
|
||||
if (!isdigit(argv[0][0]))
|
||||
mask ^= 0777;
|
||||
if ((unsigned)mask > 0777) {
|
||||
mask = old_mask;
|
||||
/* bash messages:
|
||||
* bash: umask: 'q': invalid symbolic mode operator
|
||||
* bash: umask: 999: octal number out of range
|
||||
*/
|
||||
bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
/* Mimic bash */
|
||||
printf("%04o\n", (unsigned) mask);
|
||||
/* fall through and restore mask which we set to 0 */
|
||||
}
|
||||
umask(mask);
|
||||
|
||||
return !rc; /* rc != 0 - success */
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_KILL
|
||||
static int FAST_FUNC builtin_kill(char **argv)
|
||||
{
|
||||
@ -9984,3 +9939,46 @@ static int FAST_FUNC builtin_return(char **argv)
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_HUSH_MEMLEAK
|
||||
static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
|
||||
{
|
||||
void *p;
|
||||
unsigned long l;
|
||||
|
||||
# ifdef M_TRIM_THRESHOLD
|
||||
/* Optional. Reduces probability of false positives */
|
||||
malloc_trim(0);
|
||||
# endif
|
||||
/* Crude attempt to find where "free memory" starts,
|
||||
* sans fragmentation. */
|
||||
p = malloc(240);
|
||||
l = (unsigned long)p;
|
||||
free(p);
|
||||
p = malloc(3400);
|
||||
if (l < (unsigned long)p) l = (unsigned long)p;
|
||||
free(p);
|
||||
|
||||
|
||||
# if 0 /* debug */
|
||||
{
|
||||
struct mallinfo mi = mallinfo();
|
||||
printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
|
||||
mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
|
||||
}
|
||||
# endif
|
||||
|
||||
if (!G.memleak_value)
|
||||
G.memleak_value = l;
|
||||
|
||||
l -= G.memleak_value;
|
||||
if ((long)l < 0)
|
||||
l = 0;
|
||||
l /= 1024;
|
||||
if (l > 127)
|
||||
l = 127;
|
||||
|
||||
/* Exitcode is "how many kilobytes we leaked since 1st call" */
|
||||
return l;
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user